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;
}
});
}
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With