简体   繁体   English

如何使用 LiveData 在 ViewModel 中启动 Kotlin 协程

[英]How to start Kotlin Coroutine in ViewModel with LiveData

I was trying to get all launchable installed apps asynchronously from ViewModel .我试图从ViewModel异步获取所有可启动的已安装应用程序。

This is my ViewModel class looks like:这是我的ViewModel class 看起来像:

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

There is no problem with loading the app list.加载应用列表没有问题。 But, it seems that my code still not run asynchronously because it still freezes for a very few seconds before moving to the app installed list activity.但是,我的代码似乎仍然没有异步运行,因为它在移动到应用程序安装列表活动之前仍然冻结了几秒钟。

And this is the code I use to observe the LiveData :这是我用来观察LiveData的代码:

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

I'm so confusing trying to use Kotlin on Android Development.尝试在 Android 开发中使用 Kotlin 让我很困惑。 This is my first time developing an app using Kotlin.这是我第一次使用 Kotlin 开发应用程序。 I don't even know if the code I wrote above is a good code in the context of Kotlin's style.我什至不知道我上面写的代码在 Kotlin 风格的上下文中是否是一个好的代码。

Please help me!请帮我! Thank you!谢谢!

It is because of executing the long-running job in Main dispatcher.这是因为在Main调度程序中执行了长时间运行的作业。 I highly recommend taking a look at Coroutines Dispatchers .我强烈推荐看看Coroutines Dispatchers

So, if you change the dispatcher to Dispatchers.Default , leading to fetch installed apps executing in a background thread, consequently, the UI will never be frozen.因此,如果您将调度程序更改为Dispatchers.Default ,导致获取已安装的应用程序在后台线程中执行,因此,UI 将永远不会被冻结。

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


More:更多的:

If you want to achieve a better structure, it could be even better not accessing the LiveData by calling a function:如果您想获得更好的结构,最好不要通过调用 function 来访问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
    }
}

Now, in the fragment:现在,在片段中:

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

appInstalledViewModel.reloadApps()

Using this structure, if you want to refresh the loaded data, it's just enough to call appInstalledViewModel.reloadApps() , then the list of apps gets updated.使用这种结构,如果要刷新加载的数据,只需调用appInstalledViewModel.reloadApps() ,然后更新应用列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM