Принуждение приостановленной функции к работе в одном потоке для сопоставления объектов Realm в фоновом режиме

Скажем, мне периодически (каждые 3 секунды) нужно читать список некоторых RealmObject, и я хочу сопоставить эти объекты с простыми объектами данных, которые будут использоваться для заполнения содержимого списков.

Для тех, кто задается вопросом, на это есть три причины:

  • большая сложность вычисления значений и поведения (видимости, т. е.) представлений в ViewHolders в моем случае
  • Я хочу использовать ListAdapter, который должен иметь DiffUtilCallback, который будет выполняться в каком-то другом потоке, кроме пользовательского интерфейса, поэтому выполнение необходимых вычислений в его методах невозможно, потому что они будут использоваться из потока, в котором они не созданы.
  • удаление зависимости слоя просмотра от Realm

Я не могу просто использовать сопрограммы базовым способом, которые переходят от одного доступного потока при их выполнении к другому, поскольку я в конечном итоге получу (и я получил) исключение «Realm is already closed».

Единственное решение, которое я придумал, - использовать диспетчер, который использует только один поток newSingleThreadContext("RealmSingleThreadContext"), например:

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


}

Этот класс предоставляется как Singleton через Dagger, и в конечном итоге у меня будет один поток для отображения всего приложения в фоновом режиме, и моя «основная» модель просмотра будет вызывать свой onClosed метод close() этого класса.

Вот как я его использую:

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

}

Единственное, что функция newSingleThreadContext(name: String) имеет аннотацию @ObsoleteCoroutineApi, значит, она скоро будет удалена.

Мой вопрос: может ли кто-нибудь предложить другое решение, кроме использования этого устаревшего Диспетчера?


person Draško    schedule 27.05.2019    source источник


Ответы (1)


Сделать свой однопоточный диспетчер из Java-исполнителя очень просто:

val singleThreadContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
person Marko Topolnik    schedule 28.05.2019
comment
Спасибо, но это единственный или правильный способ сделать это? - person Draško; 28.05.2019
comment
На данный момент это лучший способ сделать это, который не является устаревшим. Пока вы пишете Kotlin для JVM, он всегда будет оставаться хорошим способом. Это причина существования функции расширения asCoroutineDispatcher(). - person Marko Topolnik; 28.05.2019