Zhuinden / realm-monarchy

[ACTIVE-ISH] A wrapper over Realm which exposes it as LiveData, managing Realm lifecycle internally.
Apache License 2.0
88 stars 11 forks source link

App crashes with this error `monarchy.realmThreadLocal.get()` returns null #26

Closed ksyamkrishnan closed 5 years ago

ksyamkrishnan commented 5 years ago

2019-08-03 13:10:40.732 19654-19677/com.example.android.codelabs.paging E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0 Process: com.example.android.codelabs.paging, PID: 19654 java.lang.NullPointerException: Attempt to invoke virtual method 'void io.realm.Realm.refresh()' on a null object reference at com.zhuinden.monarchy.Monarchy$RealmTiledDataSource.isInvalid(Monarchy.java:668) at androidx.paging.WrapperPositionalDataSource.isInvalid(WrapperPositionalDataSource.java:51) at androidx.paging.TiledPagedList.(TiledPagedList.java:89) at androidx.paging.PagedList.create(PagedList.java:203) at androidx.paging.PagedList.access$000(PagedList.java:114) at androidx.paging.PagedList$Builder.build(PagedList.java:362) at androidx.paging.LivePagedListBuilder$1.compute(LivePagedListBuilder.java:201) at androidx.paging.LivePagedListBuilder$1.compute(LivePagedListBuilder.java:166) at androidx.lifecycle.ComputableLiveData$2.run(ComputableLiveData.java:101) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764)

Zhuinden commented 5 years ago

Oh my, that really shouldn't happen 😭 One possible option though is to manually use monarchy.open somewhere so that stopping to observe the LiveData wouldn't kill/create a Realm in the background.

Do you have code that reproduces this so I can debug it a bit?

ksyamkrishnan commented 5 years ago

thanks, I was able to resolve it, the issue was I tired to create LiveData<PagedList> from RealmDataSourceFactory without Monarchy. But now I am getting this error Process: com.example.android.codelabs.paging, PID: 23228 java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable. at io.realm.BaseRealm.checkIfValid(BaseRealm.java:433) at io.realm.RealmQuery.findAll(RealmQuery.java:1765) at com.zhuinden.monarchy.PagedLiveResults.createQuery(PagedLiveResults.java:50) at com.zhuinden.monarchy.Monarchy.createAndObserveRealmQuery(Monarchy.java:195) at com.zhuinden.monarchy.Monarchy$5.run(Monarchy.java:243) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:193) at android.os.HandlerThread.run(HandlerThread.java:65)


            realm.executeTransaction { realm ->
                var query = realm.where(Repo::class.java).equalTo("name", name)
                datasource = monarchy.createDataSourceFactory({ realm -> query })
                dataSourceFactory = (datasource.map { input ->
                    var repoRealm = Repo()
                    repoRealm.id = input.id
                    repoRealm.name = input.name
                    repoRealm.fullName = input.fullName
                    repoRealm.url = input.url
                    repoRealm.description = input.description
                    repoRealm.stars = input.stars
                    repoRealm.forks = input.forks
                    repoRealm.language = input.language
                } as? DataSource.Factory<Int, Repo>)!!
                pagedList = monarchy.findAllPagedWithChanges(
                        datasource,
                        LivePagedListBuilder<Int, Repo>(dataSourceFactory, 50))
            }
        }```
ksyamkrishnan commented 5 years ago

On debug further, I got his reason java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.

Zhuinden commented 5 years ago

You cannot share RealmQuery across threads. Use the Realm instance in the Monarchy lambda to build the RealmQuery instance.

I'm actually not sure where the realm.executeTransaction came from, it's safer to use the monarchy.runTransactionSync method.

ksyamkrishnan commented 5 years ago

ok cool thanks @Zhuinden. Issue got fixed.


         realm -> realm.where(RepoRealm::class.java) }
         dataSourceFactory = dataSourceFactory = (realmDataSourceFactory.map { input ->
         Repo(input.id,input.name,input.fullName,input.description,
          input.url,input.stars,input.forks,input.language)
          } ) as DataSource.Factory<Int, Repo>
          monarchy.findAllPagedWithChanges(
          realmDataSourceFactory,
          LivePagedListBuilder<Int, Repo>(dataSourceFactory, 
          DATABASE_PAGE_SIZE).setBoundaryCallback(boundaryCallback))```

One more doubt can I use realm with and without Monarchywrapper in the same application 
ksyamkrishnan commented 5 years ago

This is not a library issue fixed from app side.

Zhuinden commented 5 years ago

You can use Monarchy with regular Realm, all you need to be aware of is that Monarchy runs stuff on its own thread (unless the method has __Sync in it).

Monarchy is generally safer though in regards that it makes it harder to keep Realm instances lingering around even when they should be closed.