Чистый способ сохранить CoroutineScope через изменения конфигурации без ViewModel - PullRequest
4 голосов
/ 23 января 2020

Я знаю, что рекомендуется использовать ViewModel с нашей Activity, чтобы мы могли использовать viewModelScope. Поскольку ViewModel переживает действие, нам не нужно отменять наши задания в activity.onDestroy().

Однако иногда у вас есть простое действие. Например, он может заполнить представление списка установленными отфильтрованными пакетами. Вы можете очень просто создать область действия, используя делегата, и отменить задания в onDestroy():

class MyActivity(): AppCompatActivity(), CoroutineScope by MainScope() {

    private val listAdapter = MyAdapter()

    override fun onCreate() {
        super.onCreate()
        setContentView(R.layout.my_activity)
        recycler_view.apply {
            layoutManager = LinearLayoutManager(this)
            adapter = listAdapter
        }
        launch {
            val packages = getOrgPackagesWithIcons()
            adapter.apply {
                data = packages
                notifyDataSetChanged()
            }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        cancel() // CoroutineContext
    }

    private suspend fun getOrgPackagesWithIcons() = withContext(Dispatchers.Default) {
        var toNextYield = 20
        packageManager.getInstalledPackages(0)
            .filter { it.packageName.startsWith("org")
            .take(100)
            .map { 
                     if (--toNextYield == 0) { // Make it cancellable
                         toNextYield = 20
                         yield()
                     }
                     MyPackageData(
                         it.applicationInfo.loadLabel(packageManager).toString(),
                         it.packageName,
                         it.applicationInfo.loadIcon(packageManager)
                     )
                 }
        }

}

. В таком случае ViewModel кажется излишним. Это был бы просто еще один слой для абстрагирования PackageManager, который сам по себе является моделью представления.

Приведенный выше код облегчает сбор данных в фоновом режиме. Проблема в том, что при повороте экрана или во время других изменений конфигурации сопрограмма отменяется и перезапускается. Есть ли чистый рецепт для поддержания CoroutineScope в действии путем изменения конфигурации для очень простого действия, подобного этому?

onRetainNonConfigurationInstance() не рекомендуется. Я полагаю, что мы могли бы поместить его во Fragment и использовать retainInstance = true, но введение слоя Fragment в такое простое действие также кажется излишним.

Может быть, есть способ создать пустую реализацию ViewModel просто так, чтобы мы могли одолжить свою сферу?

Ответы [ 2 ]

3 голосов
/ 23 января 2020

Для подобного случая ViewModel выглядит как излишнее.

Я бы сказал иначе и все же предположил бы, что это будет хорошим вариантом использования для AndroidViewModel .

Я считаю, что Activity не несет ответственности за выбор списка пакетов только потому, что у него есть доступ к PackageManager. Activity должен отвечать только за отображение списка.

Использование AndroidViewModel дает вам доступ к Context и viewModelScope в вашем экземпляре ViewModel.

0 голосов
/ 24 января 2020

Согласитесь с приведенным выше ответом, что ViewModel по-прежнему остается лучшим выбором, поскольку вы выполняете операции с данными, которые на самом деле не относятся к этой деятельности. Тем не менее, если вы посмотрите на , как ViewModel хранит данные - его простое состояние c. Не приводя здесь полную цепочку, а только ту часть, где все это собрано

if (mViewModelStore == null) {
    NonConfigurationInstances nc =
            (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        // Restore the ViewModelStore from NonConfigurationInstances
        mViewModelStore = nc.viewModelStore;
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
}

, где NonConfigurationInstances:

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

Поместите ее здесь, чтобы ответить на ваш вопрос о "пустой реализации ViewModel" - вот как это было сделано, и вы можете сделать что-то подобное - возможно, поместить в свой собственный класс приложения. Но, опять же, это говорит о том, что он есть, и он прост в использовании и предоставляет другие преимущества ... поэтому я всегда использую модель представления / androidviewmodel, и у меня в голове нет никаких накладных расходов, наоборот, код хорошо организован.

...