Kiran Malvi
Kiran Malvi

Reputation: 636

ArrayIndexOutOfBoundException in Realm

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

Answers (1)

EpicPandaForce
EpicPandaForce

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

Related Questions