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
            )
        }

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.

1 Like

Thank you, I got the Idea!