this is my code:
@Override
public <E extends RealmObject> void deleteAllAsync(Class<E> clazz, final RealmQuery query, final MHDDBCallBack callBack) {
replacePropertyValue(query, "realm", null);
RealmAsyncTask transaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
replacePropertyValue(query, "realm", realm);
RealmResults<E> objects = query.findAll();
for (E object : objects) {
object.deleteFromRealm();
}
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
if (callBack != null) {
callBack.success();
}
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
if (callBack != null) {
callBack.failed(error);
}
}
});
transactions.add(transaction);
}
private void replacePropertyValue(Object object, String propertyName, Object newValue) {
try {
Field field = object.getClass().getDeclaredField(propertyName);
field.setAccessible(true);
field.set(object, newValue);
} catch (Exception e) {
e.printStackTrace();
}
}
It'll crashed every time execute object.deleteFromRealm();.
Someone said i should add realm.beginTransaction() before object.deleteFromRealm(), but not work for me . It'll show another error The realm is already in a write transaction.
what should i do?
Someone said i should add realm.beginTransaction() before object.deleteFromRealm(), but not work for me . It'll show another error The realm is already in a write transaction.
No, you don't need that if you are using Realm.executeTransactionAsync().
It'll crashed every time execute object.deleteFromRealm();.
what is the full stack trace?
@beeender
java.lang.IllegalStateException: Cannot modify managed objects outside of a write transaction.
at io.realm.internal.Table.throwImmutable(Table.java:674)
at io.realm.internal.Table.checkImmutable(Table.java:549)
at io.realm.internal.Table.moveLastOver(Table.java:321)
at io.realm.RealmObject.deleteFromRealm(RealmObject.java:114)
at io.realm.RealmObject.deleteFromRealm(RealmObject.java:86)
at com.mhd.mhdpdarecycle.dataaccess.db.impl.MHDDBHelper$16.execute(MHDDBHelper.java:333)
at io.realm.Realm$1.run(Realm.java:1502)
at io.realm.internal.async.BgPriorityRunnable.run(BgPriorityRunnable.java:34)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
if i delete just one record, it'll be okay:
@Override
public void execute(Realm realm) {
if (callBack != null) {
replacePropertyValue(query, "realm", realm);
E object = (E) query.findFirst();
object.deleteFromRealm();
}
}
it's so weird.
The problem is your query passed to the method belongs to a different Realm instance.
You have to use the realm instance in the execute block to build a new query to use.
What version of Realm are you using?
@beeender I have changed the value of realm property in query. See method replacePropertyValue(Object object, String propertyName, Object newValue). I used the realm instance in execute(Realm realm) finally. And i don't think this is caused by the realm instance.
And just like what i said before, if i change the code of
RealmResults<E> objects = query.findAll();
for (E object : objects) {
object.deleteFromRealm();
}
to
E object = (E) query.findFirst();
object.deleteFromRealm();
everything will be okay, no Exception, no Error.
In the end, i cannot create query in the execute(Realm realm) callback, cause it's a param, it'll be created by other people, so i have to change the realm property in RealmQuery to null before executeTransactionAsync(), and then change it back to the new realm instance in execute(Realm realm). If not, it'll be crash because of the realm instance is not in the same thread.
@Zhuinden
classpath "io.realm:realm-gradle-plugin:4.3.1"
@beeender @Zhuinden I have put my demo here Realmdemo, the problem method is deleteAll() in MainActivity, and the method deleteFirst() is right. The method of updateAll() has the same problem, and the method updateFirst() is okay.
Does it work with deleteAllFromRealm on the RealmResults? Although you shouldn't be running into any trouble.
@Zhuinden yes, it's working, no Exception again. But there's another problem: the objects deleted still can be selected out with other Realm instance.
And is there something different between
for (E object : objects) {
object.deleteFromRealm();
}
and
objects.deleteAllFromRealm();
?
Anyone who can tell me that executeTransactionAsync whether or not can be used to delete or update RealmResults<E>? Our project is really urgent.
@archerLj as I said:
The problem is your query passed to the method belongs to a different Realm instance.
You have to use the realm instance in the execute block to build a new query to use.
Realm will throw IllegalStateException with "Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created." since you are trying to access the Query object which was created on the different thread.
Even you are using hacks to suppress the exception by
replacePropertyValue(query, "realm", realm);
private void replacePropertyValue(Object object, String propertyName, Object newValue) {
try {
Field field = object.getClass().getDeclaredField(propertyName);
field.setAccessible(true);
field.set(object, newValue);
} catch (Exception e) {
e.printStackTrace();
}
}
The hack will make the exception disappear, but it doesn't change the truth: RealmQuery object is thread-confined currently and it cannot be accessed from a different thread.
Please trust us, the restriction is there because of some reasons. And if it can be removed simply like that, we will do that.
Also please notice that we are working on removing the thread restrictions for RealmQuery. But it is still work in process. see https://github.com/realm/realm-core/issues/2943
Okay, although i still don't understand the basic reason about different performance between object.deleteFromRealm() and objects.deleteAllFromRealm(). I'll try other methods, and thanks for your reply.
@archerLj you can delete RealmResults in executeTransactionAsync only if you query the RealmResults inside that transaction block, using the Realm instance that the transaction block gives you.
@Zhuinden thanks, the problem has been solved. I add a new Interface param (DBCreateQuery) to throw a new query out instead of the RealmQuery param:
public <E extends RealmObject> void deleteAllAsync(final Class<E> clazz,
final DBCreateQuery createQuery,
final DBCallBack callBack) {
if (null != createQuery) {
RealmAsyncTask transaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
RealmQuery query = realm.where(clazz);
createQuery.createQuery(query);
RealmResults<E> objects = query.findAll();
if (null != objects && objects.size() > 0) {
objects.deleteAllFromRealm();
}
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
if (null != callBack) {
callBack.success();
}
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
if (null != callBack) {
callBack.failed(error);
}
}
});
transactions.add(transaction);
}
}
Good solution
@archerLj you can delete RealmResults in executeTransactionAsync only if you query the RealmResults inside that transaction block, using the Realm instance that the transaction block gives you.
Took me a while to release this fact, I think It should be added to documentation @Zhuinden
Most helpful comment
@Zhuinden thanks, the problem has been solved. I add a new Interface param (
DBCreateQuery) to throw a new query out instead of the RealmQuery param: