Android - Realm - Object delete - Object is no longer valid to operate on

为君一笑 提交于 2019-12-30 17:26:11

问题


I'm trying to delete an item from a RecyclerView populated from a Realm Database and I'm getting the following error:

java.lang.IllegalStateException: Illegal State: 
Object is no longer valid to operate on. Was it deleted by another thread?

Assumptions I guess that I'm trying the access when it's already deleted, but I don't understand where.

Context: I'm showing a list of cities and longClicking on an item shows a dialog asking to confirm the deletion.

The item is deleted in the database since when I relaunch the app, it's not there anymore.

Realm to ArrayList

public static ArrayList<City> getStoredCities(){
        RealmQuery<City> query = getRealmInstance().where(City.class);
        final RealmResults<City>results =
                realm.where(City.class)
                        .findAllSorted("timestamp", Sort.DESCENDING);

        results.size();

        ArrayList<City> cityArrayList = new ArrayList<>();

        for(int i = 0; i< results.size(); i++){
            cityArrayList.add(results.get(i));
        }

        return cityArrayList;
    }

Dialog code

builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int i) {
        RealmHelper.removeCity(cityArrayList.get(position));
        cityArrayList.remove(position);
        mRecyclerView.removeViewAt(position);
        mCityListAdapter.notifyItemRemoved(position);
        mCityListAdapter.notifyItemRangeChanged(position, cityArrayList.size());
        mCityListAdapter.notifyDataSetChanged();
    }
});

Realm method to delete the item

public static void removeCity(City city){
        RealmResults<City> result = realm.where(City.class).equalTo("cityName", city.getCityName()).findAll();
        realm.beginTransaction();
        result.deleteAllFromRealm();
        realm.commitTransaction();
}

Logs

07-28 11:02:08.461 9461-9461/com.ilepez.weatherapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ilepez.weatherapp, PID: 9461
java.lang.IllegalStateException: Illegal State: 
Object is no longer valid to operate on. Was it deleted by another thread?
at io.realm.internal.UncheckedRow.nativeGetString(Native Method)
at io.realm.internal.UncheckedRow.getString(UncheckedRow.java:153)
at io.realm.CityRealmProxy.realmGet$cityName(CityRealmProxy.java:75)
at com.ilepez.weatherapp.data.model.City.getCityName(City.java:41)
at com.ilepez.weatherapp.adapter.CityListAdapter.onBindViewHolder(CityListAdapter.java:56)
at com.ilepez.weatherapp.adapter.CityListAdapter.onBindViewHolder(CityListAdapter.java:20)
at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5768)
at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5801)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5037)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4913)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2029)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1414)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1377)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:588)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3260)
at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:2788)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.support.design.widget.NavigationView.onMeasure(NavigationView.java:218)
at android.view.View.measure(View.java:20151)
at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1108)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:747)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:629)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:747)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:629)
at android.view.View.measure(View.java:20151)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6328)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:3158)
at android.view.View.measure(View.java:20151)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2594)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1549)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1841)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1437)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7403)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCall

Adapter code

public class CityListAdapter extends RecyclerView.Adapter<CityListAdapter.CityListViewholder>{

public interface OnItemClickListener{
    void onItemClick(int position);
}

public interface OnItemLongClickListener{
    void onItemLongClick(int position);
}

private static final String LOG_TAG = CityListAdapter.class.getSimpleName();

private ArrayList<City> cityArrayList = new ArrayList<>();
private Context mContext;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;

public CityListAdapter(Context context, ArrayList<City> cityArrayList, OnItemClickListener onItemClickListener, OnItemLongClickListener onItemLongClickListener) {
    this.cityArrayList = cityArrayList;
    this.mContext = context;
    this.onItemClickListener = onItemClickListener;
    this.onItemLongClickListener = onItemLongClickListener;
}

@Override
public CityListViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.city_item_navigation_viewholder, null);
    CityListViewholder cityListViewholder = new CityListViewholder(view, parent.getContext());
    return cityListViewholder;
}

@Override
public void onBindViewHolder(CityListViewholder holder, int position) {
    holder.cityName.setText(cityArrayList.get(position).getCityName());
    holder.bindClick(position, onItemClickListener);
    holder.bindLongClick(position, onItemLongClickListener);
}

@Override
public int getItemCount() {
    return cityArrayList.size();
}


public class CityListViewholder extends RecyclerView.ViewHolder{

    TextView cityName;
    ImageView cityIcon;

    public CityListViewholder(View itemView, Context context) {
        super(itemView);
        cityName = (TextView)itemView.findViewById(R.id.city_name);
        cityIcon = (ImageView)itemView.findViewById(R.id.city_icon);
    }

    public void bindClick(final int position, final OnItemClickListener onItemClickListener){
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onItemClickListener.onItemClick(position);
            }
        });
    }

    public void bindLongClick(final int position, final OnItemLongClickListener onItemLongClickListener) {
        itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                onItemLongClickListener.onItemLongClick(position);
                return true;
            }
        });
    }
}
}

回答1:


Okay so you're calling adapter.notifyDataSetChanged(), so any other notify___ method is unnecessary (data set change disables animations anyways.)

In which case the easiest (and most efficient) way of doing things would be to use the RealmResults directly rather than retrieving every element into an ArrayList which is then used the exact same way.

So it should be like this

public static RealmResults<City> getStoredCities(){
        RealmQuery<City> query = getRealmInstance().where(City.class);
        return realm.where(City.class)
                        .findAllSorted("timestamp", Sort.DESCENDING);
}

And

public static void removeCity(City city){
        final String cityName = city.getCityName();
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                RealmResults<City> result = realm.where(City.class).equalTo("cityName", cityName).findAll();
                result.deleteAllFromRealm();
            }
        });
}

And

builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int i) {
        RealmHelper.removeCity(getItem(position));
    }
});

And

// dependency: compile 'io.realm:android-adapters:1.3.0' 
// <-- for Realm 3.x+, use 2.0.0
// for Realm 5.x+, use 3.0.0
public class CityListAdapter extends RealmRecyclerViewAdapter<City, CityListViewHolder> { 

    OnItemClickListener onItemClickListener;
    OnItemLongClickListener onItemLongClickListener;

    public CityListAdapter(@NonNull Context context, 
                           @Nullable OrderedRealmCollection<City> data, 
                           OnItemClickListener onItemClickListener, 
                           OnItemLongClickListener onItemLongClickListener) {
        super(context, data, true);
        this.onItemClickListener = onItemClickListener;
        this.onItemLongClickListener = onItemLongClickListener;
    }


    @Override
    public CityListViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.city_item_navigation_viewholder, parent, false);
        CityListViewholder cityListViewholder = new CityListViewholder(view, parent.getContext());
        return cityListViewholder;
    }

    @Override
    public void onBindViewHolder(CityListViewholder holder, int position) {
        holder.cityName.setText(getItem(position).getCityName());
        holder.bindClick(position, onItemClickListener);
        holder.bindLongClick(position, onItemLongClickListener);
    }

    public static class CityListViewholder extends RecyclerView.ViewHolder {
        TextView cityName;
        ImageView cityIcon;

        public CityListViewholder(View itemView, 
                                  Context context) {
            super(itemView);
            cityName = (TextView)itemView.findViewById(R.id.city_name);
            cityIcon = (ImageView)itemView.findViewById(R.id.city_icon);
        }

        public void bindClick(final int position, final OnItemClickListener onItemClickListener){
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    onItemClickListener.onItemClick(position);
                }
            });
        }

        public void bindLongClick(final int position, final OnItemLongClickListener onItemLongClickListener) {
            itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    onItemLongClickListener.onItemLongClick(position);
                    return true;
                }
            });
        }
    }
}

Using RealmRecyclerViewAdapter will call notifyDataSetChanged() whenever your results change.



来源:https://stackoverflow.com/questions/38631848/android-realm-object-delete-object-is-no-longer-valid-to-operate-on

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!