Closed andhie closed 2 years ago
Hi @andhie
Thanks for the detailed issue report. I'll reproduce your issue with the sample above.
One question. Why would you need to recreate the Person
schema in run time?
Is there any reason you don't inherit and create a Person
class from RealmObject
?
Why would you need to recreate the Person schema in run time?
As mentioned in the Goals, when user starts a Job, the Product varies between each Job. It would be better to clear off the older schema and rebuild with the new Product schema. Note: Person
in the code are put together from Realm samples.
the app doesnt really know the Columns are available as its determined by Server. The API will tell the app what columns/fields its requires.
@andhie
I have reproduce the issue with the sample you provided. This is being escalated for further investigation. Thanks again for the detailed report. 👍
There is actually another bug this sample. at least it produced a different stacktrace and failing point.
Step: Comment out the last transaction block (that deletes the schema) as below and run again
// Log.i("tag", "isClosed = " + realm.isClosed());
// realm.executeTransaction(new DynamicRealm.Transaction() {
// @Override
// public void execute(DynamicRealm realm) {
// realm.getSchema().remove("Person");
// Log.i("tag", "schema removed, isClosed = " + realm.isClosed());
// }
// });
Observation: Immediately crash also
com.example.realm D/dalvikvm: Trying to load lib /data/app-lib/com.example.realm-2/librealm-jni.so 0x41fcb348
com.example.realm D/dalvikvm: Added shared lib /data/app-lib/com.example.realm-2/librealm-jni.so 0x41fcb348
com.example.realm D/REALM: Table 0x4f1a7a58 is no longer attached!
com.example.realm D/REALM: jni: ThrowingException 8, Table is no longer valid to operate on., .
com.example.realm D/REALM: Exception has been throw: Illegal State: Table is no longer valid to operate on.
com.example.realm D/AndroidRuntime: Shutting down VM
com.example.realm W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x418f6300)
com.example.realm E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.realm/com.example.realm.MainActivity}: java.lang.IllegalStateException: Illegal State: Table is no longer valid to operate on.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2102)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2127)
at android.app.ActivityThread.access$600(ActivityThread.java:136)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1228)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4818)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Illegal State: Table is no longer valid to operate on.
at io.realm.internal.Table.nativeAddEmptyRow(Native Method)
at io.realm.internal.Table.addEmptyRow(Table.java:379)
at io.realm.DynamicRealm.createObject(DynamicRealm.java:80)
at com.example.realm.MainActivity$2.execute(MainActivity.java:57)
at io.realm.DynamicRealm.executeTransaction(DynamicRealm.java:165)
at com.example.realm.MainActivity.onCreate(MainActivity.java:53)
at android.app.Activity.performCreate(Activity.java:5062)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1082)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2066)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2127)
at android.app.ActivityThread.access$600(ActivityThread.java:136)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1228)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4818)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
The offending line stated in the stack trace is this code
// Creating new data during migrations
DynamicRealmObject person = realm.createObject("Person");
Thanks again for the detailed report. 👍
No problem. Filing bugs is my hobby :P
@andhie That's one stone for two birds. I'll look into it too. Appreciated.
Hi @andhie
We found the problem. You are creating a query here: RealmResults<DynamicRealmObject> persons = realm.where("Person").findAll();
But then deletes the Person schema right afterwards: realm.getSchema().remove("Person");
This means that the query no longer knows how to listen to updates and then throws an incorrect IllegalStateException. The problem isn't that the realm is closed, but that the table reference no longer exists. Even if you create the table again, the old reference doesn't know this.
I can see why you would expect this to work in the code given above, and I can think of 3 ways we can solve this:
1) Properly detect if the underlying table is no longer available and start treating the RealmResults as an empty list from that point on (we should probably log a warning in the log though).
2) Throw a proper illegalStateException with a better error message.
3) Try to dynamically detect that a Table have been removed and re-added.
The problem with 2) is that we depend on the GC to cleanup any lingering RealmResults, so even if you no longer use it, it might still be valid for being notified about changes.
I am not sure what I think about 3) as it wouldn't be easy to do for a chain of queries.
So I am leaning towards 1) ... It also makes sense IMO that you need to re-create an query after manipulating the schema, but what do you think @realm/java @stk1m1 ?
1) Properly detect if the underlying table is no longer available and start treating the RealmResults as an empty list from that point on (we should probably log a warning in the log though).
My vote goes to 1.
1) Properly detect if the underlying table is no longer available and start treating the RealmResults as an empty list from that point on (we should probably log a warning in the log though).
1 is the way how we treat the results built on a deleted LinkView, so we probably should do the same.
As i know there isnt a way to "close" a RealmResults like cursor.close()
right? If so i cant prevent any crashes.
1st solution should be good enough with error logs
Goal
my app needs to get configuration from server to create a Table/Schema after API call. since the schema is a job specific, another job will have a slight different schema. so i need to delete Schema once user is done with a job.
e.g. Product class schema. (not all product have expiry, may not always appear)
Expected Results
Able to dynamically create a table/schema, perform CRUD and delete Schema when user is done.
Actual Results
Steps & Code to Reproduce
Run the code, it crashes immediately. but it successfully log all action.
Code Sample
Version of Realm and tooling
Realm version(s): classpath "io.realm:realm-gradle-plugin:1.0.0"
Android Studio version: 2.1.2
Which Android version and device: Android Emulator version 25.1.7, Version 6.0 - API 23 (Rev 1)