Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing suspended function to work on one thread for mapping Realm objects in background

Let's say I periodically (each 3 seconds) need to read a list of some RealmObjects and I want to map those objects to simple data objects that will be used for filling the content of lists .

For those who wonder, there are three reasons for that:

  • big complexity of calculating the values and behaviors (visibility, i.e.) of views in ViewHolders in my case
  • I want to use ListAdapter which needs to have provided DiffUtilCallback which would be executed on some other thread than UI, so doing needed computations in its methods is not possible, because they would be used from the thread in which they are not created.
  • removing view layer's dependency from Realm

I can not simply use coroutines in basic way which jump from one available thread in its execution to another, since I will(and I did) eventually receive "Realm is already closed" exception.

The only solution that I come up with is using dispatcher that uses only one thread newSingleThreadContext("RealmSingleThreadContext") for example:

class RealmSingleThreadContext {
    val ctx = NewSingleThreadedContext("RealmSingleThreadContext")

    lateinit var realm : Realm

    init {
        // opening Realm connection for thread "RealmSingleThreadContext"
        GlobalScope.launch(ctx) { realm = Realm.getDefaultInstance() }
    }

    fun close() {
        GlobalScope.launch(ctx) { if (!realm.isClosed()) realm.close() }
    }


}

This class is provided as Singleton through Dagger and eventually at the end, I will have one thread to do the mapping for whole app in the background and my "main" viewModel, will call in its onClosed method close() method of this class.

This is how I use it:

class HomeViewModel @Inject constructor(/* other */var realmSingleThreadContext: RealmSingleThreadContext) {

    var topLiveEventsUI: MutableLiveData<List<EventPreviewUI>> = MutableLiveData(listOf())

    fun useRealmInBckg() {
       viewModeScope.launch(realmSingleThreadContext.ctx) {
           topLiveEventsUI.postValue(eventsReadUseCase.getTopLiveEvents()
               .map{ mapper.map(it) }
        }
    }

}

The only thing is that the function newSingleThreadContext(name: String) has @ObsoleteCoroutineApi annotation, so it means that it would be removed soon.

My question is: can anyone propose another solution than using this obsoleted Dispatcher?

like image 405
Draško Avatar asked May 27 '19 12:05

Draško


1 Answers

It's very easy to make your own single-threaded dispatcher from a Java executor:

val singleThreadContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
like image 128
Marko Topolnik Avatar answered Sep 23 '22 09:09

Marko Topolnik