Как запустить Kotlin Coroutine в ViewModel с LiveData - PullRequest
1 голос
/ 01 августа 2020

Я пытался асинхронно получить все запускаемые установленные приложения из ViewModel.

Это мой ViewModel класс выглядит так:

class AppInstalledViewModel(application: Application) : AndroidViewModel(application) {
    private var appInstalledLiveData: LiveData<ArrayList<AppInstalled>>? = null

    fun getAppInstalledLiveData(): LiveData<ArrayList<AppInstalled>> {
        if (appInstalledLiveData == null) {
            appInstalledLiveData = liveData {
                val appInstalled = ArrayList<AppInstalled>()
                val pm: PackageManager =
                    getApplication<Application>().applicationContext.packageManager

                val main = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)

                // Get launchable installed apps
                val launchAble = pm.queryIntentActivities(main, 0)

                // Sort the installed app list
                Collections.sort(launchAble, ResolveInfo.DisplayNameComparator(pm))

                // Iterate over each launchable app and
                // add it to AppInstalled ArrayList
                for (l in launchAble) {
                    val activityInfo = l.activityInfo

                    activityInfo.run {
                        appInstalled.add(
                            AppInstalled(
                                loadLabel(pm).toString(),
                                applicationInfo.packageName,
                                loadIcon(pm)
                            )
                        )
                    }
                }

                emit(appInstalled)
            }
        }
        return appInstalledLiveData as LiveData<ArrayList<AppInstalled>>
    }
}

Нет проблем с загрузкой список приложений. Но похоже, что мой код все еще не выполняется асинхронно, потому что он все еще зависает на несколько секунд, прежде чем перейти к активности списка установленных приложений.

И это код, который я использую для наблюдения за LiveData:

appInstalledViewModel.getAppInstalledLiveData().observe(this, Observer { appInstalledItem ->
    if (appInstalledItem.size > 0) {
        appInstalledAdapter.appInstalled.addAll(appInstalledItem)
        app_list_progressbar.visibility = View.GONE
    }
})

Я так запутался, пытаясь использовать Kotlin в Android Development. Я впервые разрабатываю приложение с использованием Kotlin. Я даже не знаю, является ли код, который я написал выше, хорошим кодом в контексте стиля Kotlin.

Пожалуйста, помогите мне! Спасибо!

1 Ответ

1 голос
/ 01 августа 2020

Это из-за выполнения длительного задания в диспетчере Main. Я настоятельно рекомендую взглянуть на Диспетчеры сопрограмм .

Итак, если вы измените диспетчер на Dispatchers.Default, что приведет к извлечению установленных приложений, выполняющихся в фоновом потоке, следовательно, пользовательский интерфейс будет никогда не замораживаться.

appInstalledLiveData = liveData(Dispatchers.Default) {
    ...
}

Подробнее:

Если вы хотите получить лучшую структуру, было бы даже лучше не обращаться к LiveData путем вызова функции:

class AppInstalledViewModel(application: Application) : AndroidViewModel(application) {

    private val reloadLiveData = MutableLiveData<Unit>()

    val installedAppsLiveData: LiveData<ArrayList<AppInstalled>> =
        reloadLiveData.switchMap {
            liveData(Dispatchers.Default) {
                emit(fetchInstalledApps())
            }
        }

    fun reloadApps() {
        reloadLiveData.postValue(Unit)
    }

    private fun fetchInstalledApps(): ArrayList<AppInstalled> {
        val appInstalled = ArrayList<AppInstalled>()
        val pm: PackageManager = getApplication<Application>().applicationContext.packageManager

        val main = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)

        // Get launchable installed apps
        val launchAble = pm.queryIntentActivities(main, 0)

        // Sort the installed app list
        Collections.sort(launchAble, ResolveInfo.DisplayNameComparator(pm))

        // Iterate over each launchable app and
        // add it to AppInstalled ArrayList
        for (l in launchAble) {
            val activityInfo = l.activityInfo

            activityInfo.run {
                appInstalled.add(
                    AppInstalled(
                        loadLabel(pm).toString(),
                        applicationInfo.packageName,
                        loadIcon(pm)
                    )
                )
            }
        }
        return appInstalled
    }
}

Теперь во фрагменте:

appInstalledViewModel.installedAppsLiveData.observe(this, Observer { appInstalledItem ->
    if (appInstalledItem.size > 0) {
        appInstalledAdapter.appInstalled.addAll(appInstalledItem)
        app_list_progressbar.visibility = View.GONE
    }
})

appInstalledViewModel.reloadApps()

Используя эту структуру, если вы хотите обновить sh загруженные данные, достаточно просто позвонить appInstalledViewModel.reloadApps(), после чего список приложений будет обновлен.

...