Hi, I have such exception even I using iterators methods:
Realm version 0.75.1, Android studio 1.0rc2
Caused by: java.util.ConcurrentModificationException: No outside changes to a Realm is allowed while iterating a RealmResults. Use iterators methods instead.
at io.realm.RealmResults.assertRealmIsStable(RealmResults.java:405)
at io.realm.RealmResults.access$200(RealmResults.java:44)
at io.realm.RealmResults$RealmResultsIterator.hasNext(RealmResults.java:422)
at com.coachesdirectory.coach.db.RealmHelper$5.doInBackground(RealmHelper.java:360)
at com.coachesdirectory.coach.db.RealmHelper$5.doInBackground(RealmHelper.java:347)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
聽聽聽聽聽聽聽聽聽聽聽聽at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
聽聽聽聽聽聽聽聽聽聽聽聽at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
聽聽聽聽聽聽聽聽聽聽聽聽at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
聽聽聽聽聽聽聽聽聽聽聽聽at java.lang.Thread.run(Thread.java:818)
Here is part of my code:
****AsyncTask*****
List<SchoolResult> favoriteSchools = params[0];
Realm realm = Realm.getInstance(context);
realm.beginTransaction();
// Set all schools to remove
RealmResults<RealmSchoolResult> markToRemove = realm.where(RealmSchoolResult.class)
.equalTo("userAccountId", userId)
.findAll();
Iterator<RealmSchoolResult> schoolResultIterator = markToRemove.iterator();
while (schoolResultIterator.hasNext()) { // Crash at this line
RealmSchoolResult realmSchoolResult = schoolResultIterator.next();
realmSchoolResult.setNeedRemove(true);
}
That exception is being thrown because another part of your code is modifying the RealmSchoolResult objects. A RealmResults is like a normal Array that way. You cannot remove or add elements that fit your query while you iterate the results of that query.
Do you perhaps have some other code in another thread that add those elements to the database?
cmelchior, in another thread I only getting those elements from db, not modifying anything. But it executing before this thread(thread with error).
@sergeyfitis There shouldn't be a problem querying the data on another thread, but how do you get objects into Realm then?
In any case, I will need to see some more code to properly debug this. Are you in a position to share your source code or can you create a small sample project that can reproduce the error?
@cmelchior I will try to create sample project.
@cmelchior, I created sample project. It crashing with the same error.
https://gist.github.com/sergeyfitis/5df1632dad5c3a255cf0
User class:
public class User extends RealmObject {
private String name;
private boolean needRemove;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isNeedRemove() {
return needRemove;
}
public void setNeedRemove(boolean needRemove) {
this.needRemove = needRemove;
}
}
Hi @sergeyfitis Thank you very much. I can reproduce it as well now. I will investigate.
Welcome :)
having the same issue.
Is there a 'dirty' fix to avoid the issue until a new version will be released?
@petersnoopy Yes, just use a normal for loop like this:
realm.beginTransaction();
for (int i = 0; i < results.size(); i++) {
results.get(i).setProperty("foo");
}
realm.commitTransaction();
@cmelchior Thanks!
ETA on this one?
Just a quick note that this has not been forgotten, but has been delayed by some needed changes to the core database. There is no timeframe, but we are actively working on it now.
Getting this as well.
Any updates on this?
no ((
(bump)
Sorry for the delay. The changes in the core database has taken a bit longer than anticipated. It is almost done at this point though, which means we can fix this bug soon.
Thx !
Btw
for (int i = 0; i < results.size(); i++) {
results.get(i).setProperty("foo");
}
does not work either, because result seems to be updated dynamically (which is cool but unexpected IMHO)
This issue would be great to see closed soon.
The dirty fix that I have used is to create an array from the RealmResults.
MyModel[] modelArray = results.toArray(new MyModel[results.size()]);
for(MyModel model : modelArray) {
model.setProperty("foo");
}
A workaround is to iterate the RealmResults backwards:
for (int i = results.size() -1; i >=0; i--) {
results.get(i).setProperty("foo");
}
Any updates on this issue?
Just got this as well. Use compile 'io.realm:realm-android:0.81.1'
I'm using
compile 'io.realm:realm-android:0.81.1'
and can confirm this is still an issue: fixed the issue by not using a for-each loop
ETA?
This require an update of the Core layer. That has been completed however, and will be released as part of https://github.com/realm/realm-java/issues/478. After that this bug can be fixed.
The necessary changes to core has been merged into master.
This task is now about the following:
1) Avoid updating the TableView (query result) while iterating.
2) This is done by avoiding calling 'TableView.sync_if_needed()`.
I know avoid calling sync_if_needed is not possible to achieve using standard iterator patterns in Java.. We sould find other method to cover this use case.
So bug should be removed and enhancement should be added?
The RealmResults are updated immediately in case of a transaction (and you can only write in a transaction, so you're pretty much definitely in a transaction), and transactions (beginTransaction()) are blocking so you are definitely updating the instances you want to update.
As such, if I know that the modified value will remove the realm objects from the realm results, then I just do the following:
RealmResults<Something> results = realm.where(Something.class).equalTo("property", "bar").findAll();
realm.beginTransaction();
for (int i = 0; i < results.size(); i++) {
results.get(i--).setProperty("foo");
}
realm.commitTransaction();
EDIT (2016-04-26): This is no longer the case since 0.89.0
for (int i = results.size()-1; i >= 0; i--) {
results.get(i).setProperty("foo");
}
This is the version-idempotent version.
For reference. We are actively working on fixing this now in https://github.com/realm/realm-java/pull/2124
It means there will be some changes to how the live-update of RealmResults work as they will now be "frozen" for the duration of a Looper event, but will still be automatically updated for the next loop. For most common programming patterns this will not make a difference, but instead for...each and simple iterators will work as expected.
@cmelchior will transactions still directly affect the RealmResults<T> that you're iterating, essentially removing elements from the query? (Does the code snippet I posted above still work?) Or will that RealmResults<T> only be updated on the next looper event?
Fixed in 0.89
+1
By 0.89 you mean 0.89.1? Can one use a for-each loop again?
@meierjan yes.
I am still seeing this in 0.89.1. Anything that can be dont wront to provoke this?
You might still hit this depending on what you do. We just aimed to have the same semantics as an normal ArrayList (which also throws ConcurrentModificationException if you remove/add while iterating).
What does your code look like?
The standard List.remove() method doesn't work for normal ArrayList iterators either .
You should use the Iterator.remove() method instead, it ensures that the iterator count isn't counting wrong https://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html#remove():
Iterator it = oldPlaceOfBike.getBikes().iterator();
while(it.hasNext()) {
BikeEntity bike = it.next();
if (bike.getUid().equals(bikeWithNewInformation.getUid())) {
it.remove();
}
}
@cmelchior Do you see anything wrong that I'm doing here? That would be great if you could just take a look. Thanks a lot. http://stackoverflow.com/questions/38892798/refresh-viewpager-from-within-the-drawerlayout