Android KotlinX Coroutines bug

Hello all,

I have tried to use coroutines in my code. I use it when fetching data from the backend server and accessing realm database. Unfortunately it somehow does not work (or I misuse it, please help .-)) as I get below exception when writing to the database:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
    Process: ua.allatra.org.audio.library, PID: 18933
    java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
        at io.realm.BaseRealm.checkIfValid(BaseRealm.java:442)
        at io.realm.Realm.where(Realm.java:1430)
        at ua.allatra.org.audio.library.db.RealmHandler.createNewAudioTrackDAO(RealmHandler.kt:127)
        at ua.allatra.org.audio.library.ui.adapter.AudioTrackDownloadAdapter$fetchAudioIfExistsElseConvert$2.invokeSuspend(AudioTrackDownloadAdapter.kt:294)
        at ua.allatra.org.audio.library.ui.adapter.AudioTrackDownloadAdapter$fetchAudioIfExistsElseConvert$2.invoke(Unknown Source:10)
        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:154)
        at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
        at ua.allatra.org.audio.library.ui.adapter.AudioTrackDownloadAdapter.fetchAudioIfExistsElseConvert(AudioTrackDownloadAdapter.kt:287)
        at ua.allatra.org.audio.library.ui.adapter.AudioTrackDownloadAdapter$ViewHolder$bind$1$1.invokeSuspend(AudioTrackDownloadAdapter.kt:261)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:561)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)

I init realm globally like this in the adapter:

launch(Dispatchers.IO) {
            withContext(Dispatchers.IO) {
                realmHandler = RealmHandler(parent.context)
            }
        }

Writing like this:

private suspend fun updateDownloadedTrack(it: AudioTrackObject) =
        withContext(Dispatchers.IO){
            Log.d(TAG, "ApiId = ${it.apiId} track downloaded.")
            realmHandler.updateExistingAudioTrackDAO(
                it.apiId,
                it.isConverting,
                it.isDownloading,
                it.filePath
            )
        }
1 Like

Dispatchers.IO uses a thread pool so you never know what specific thread the code will run on.

If you want a specific thread, use Executors.newSingleThreadExecutor() to create your own background thread and then create and access your realm only from that executor (don’t forget to shut it down when you close the realm).

To schedule coroutines use the asCoroutineDispatcher() extension method on Executor to get a CoroutineDispatcher to pass into withContext.

Keep in mind that any blocking code will block the entire dispatcher (unlike Dispatchers.IO which creates threads as needed). You’ll likely get a warning for not using Dispatchers.IO, but there’s not much to do in this situation except suppresses the warning, and be careful.

3 Likes

Thank you, I got the Idea!

For anyone landing here with the same question, nickallendev answered it appropriately, but it might be helpful to see an example.
Realm actually has a sample project built to showcase use of coroutines with Realm.
They use a similar function to nickallendev’s response, which is to call Executors.newFixedThreadPool(1) (see here)

private val monoThreadDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()

private val myListOfStuff = listOf(1, 2, 3)

withContext(monoThreadDispatcher) {
   Realm.getDefaultInstance().use { realmInstance ->
      realmInstance.executeTransactionAwait { transactionRealm ->
         transactionRealm.insertOrUpdate(myListOfStuff)
      }
   }
}

The Realm coroutines sample project is located here