Reputation: 636
I'm working on a project wherein I'm using Realm database, now I'm trying to fetch a value from a database, its working fine 3-4 times then code is crashing. The error message is as follows
E/AndroidRuntime: FATAL EXCEPTION: main Process: in.mumbaitravellers.mtleaders, PID: 22340 java.lang.ArrayIndexOutOfBoundsException: rowIndex > available rows: 3 > 3 at io.realm.internal.TableView.nativeGetSourceRowIndex(Native Method) at io.realm.internal.TableView.getSourceRowIndex(TableView.java:161) at io.realm.RealmResults.get(RealmResults.java:114) at in.mumbaitravellers.mtleaders.adapters.ExpenseAdapter.onBindViewHolder(ExpenseAdapter.java:55) at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5471) at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5504) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4741) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617) at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994) at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353) at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574) at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3028) at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2906) at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3283) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.support.design.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1037) at android.support.design.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:747) at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42) at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1156) at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:760) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) at android.widget.FrameLayout.onLayout(FrameLayout.java:273) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586) at android.widget.LinearLayout.onLayout(LinearLayout.java:1495) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) at android.widget.FrameLayout.onLayout(FrameLayout.java:273) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1743) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1586) at android.widget.LinearLayout.onLayout(LinearLayout.java:1495) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336) at android.widget.FrameLayout.onLayout(FrameLayout.java:273) at com.android.internal.policy.PhoneWindow$DecorView.onLayout(PhoneWindow.java:2678) at android.view.View.layout(View.java:16646) at android.view.ViewGroup.layout(ViewGroup.java:5440) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2183) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1943) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1119) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6060) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreog W/art: Suspending all threads took: 9.933ms
The code snippet is as follows:
public class ExpenseAdapter extends RealmRecyclerViewAdapater<Expense> {
final Context context;
private Realm realm;
private LayoutInflater inflater;
public ExpenseAdapter(Context context) {
this.context = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflate a new card view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.expense_recycler_view, parent, false);
// view.setOnCreateContextMenuListener(this);
return new CardViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
realm = RealmController.getInstance().getRealm();
final Expense expense = getItem(position);
final Tour tour = new Tour();
final CardViewHolder holder = (CardViewHolder) viewHolder;
String s = "";
RealmResults<Tour> r = realm.where(Tour.class).findAll();
realm.beginTransaction();
s = r.get(position).getEventId();
realm.commitTransaction();
holder.textExpense.setText(expense.getAmount() + " E: " + expense.getEventId());
holder.textDescription.setText(expense.getDescription() + " T: " + s);
holder.card.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View content = inflater.inflate(R.layout.activity_add_new_expense, null);
final EditText editAmount = (EditText) content.findViewById(R.id.edtxt_expense);
final EditText editDescription = (EditText) content.findViewById(R.id.edtxt_description);
editAmount.setText(expense.getAmount());
editDescription.setText(expense.getDescription());
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setView(content)
.setTitle("Edit Event")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
RealmResults<Expense> results = realm.where(Expense.class).findAll();
realm.beginTransaction();
results.get(position).setAmount(editAmount.getText().toString());
results.get(position).setDescription(editDescription.getText().toString());
realm.commitTransaction();
notifyDataSetChanged();
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}
@Override
public int getItemCount() {
if (getRealmAdapter() != null) {
return getRealmAdapter().getCount();
}
return 0;
}
public static class CardViewHolder extends RecyclerView.ViewHolder {
public CardView card;
public TextView textExpense;
public TextView textDescription;
public CardViewHolder(View itemView) {
// standard view holder pattern with Butterknife view injection
super(itemView);
card = (CardView) itemView.findViewById(R.id.card_expense);
textExpense = (TextView) itemView.findViewById(R.id.text_amount);
textDescription = (TextView) itemView.findViewById(R.id.text_description);
}
}
}
Thanks in Advance!!!
Upvotes: 0
Views: 284
Reputation: 81539
This is why:
RealmResults<Expense> results = realm.where(Expense.class).findAll();
realm.beginTransaction();
results.get(position).setAmount(editAmount.getText().toString());
results.get(position).setDescription(editDescription.getText().toString());
realm.commitTransaction();
notifyDataSetChanged(); // <--------- this causes the crash
Problems:
1.) if you manipulate a result set in a transaction, then you should do the query inside the transaction
realm.beginTransaction();
RealmResults<Expense> results = realm.where(Expense.class).findAll();
2.) You should avoid creating unnecessary proxies, you should obtain the element only once.
Expense expense = results.get(position);
expense.setAmount(editAmount.getText().toString());
expense.setDescription(editDescription.getText().toString());
3.) since 0.89.0, (but up to 3.0.0), RealmResults are updated only on the next event loop, after which RealmChangeListener
is called. This is managed automatically by RealmRecyclerViewAdapter
, which means if you do UI-thread local commits (you shouldn't, but you can), then you should NOT call adapter.notifyDataSetChanged()
manually, you should register a RealmChangeListener
and call it from there (this is managed internally by RealmRecyclerViewAdapter
).
So basically, remove the line notifyDataSetChanged()
.
Also I see you have a RealmController.getInstance()
call, I guess you used this guide from AndroidHive as basis for your Realm-driven application.
That guide is outdated garbage, I recommend using mine:
How to use Realm for Android like a champ, and how to tell if you're doing it wrong
Along with the corresponding source example.
Upvotes: 3