提问者:小点点

如何用LiveData在ViewModel中启动Kotlin Coroutine


我试图从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>>
    }
}

加载应用程序列表没有问题。 但是,似乎我的代码仍然没有异步运行,因为它仍然冻结了几秒钟,然后移动到应用程序安装列表activity。

这是我用来观察LiveData的代码:

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

我对在Android开发中使用Kotlin感到很困惑。 这是我第一次使用Kotlin开发应用程序。 我甚至不知道我上面写的代码在Kotlin风格的上下文中是不是一个好代码。

请帮帮我! 谢谢!


共1个答案

匿名用户

这是因为在mainDispatcher中执行长时间运行的作业。 我强烈建议看看Coroutines Dispatchers。

因此,如果您将dispatcher更改为Dispatchers.default,导致获取在后台线程中执行的已安装应用程序,因此UI将永远不会被冻结。

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

使用这种结构,如果您想要刷新加载的数据,只需调用AppInstalledViewModel.reloAdapps()即可,然后应用程序列表将得到更新。