Kotlin协程会阻止Android中的主线程

萨德克·沙贾里(Sadeq Shajary)

我是Kotlin和协程的新手。fun在活动中并在其中进行检查,请检查User用户名和密码,如果正确,请返回Users对象。
一切都好。但是当我按下按钮时,我的活动被阻止并等待Users登录的响应
我用这个有趣:

private fun checkLogin() : Boolean {           
        runBlocking {
            coroutineScope {
                launch {
                    user = viewModel.getUserAsync(login_username.text.toString(), login_password.text.toString()).await()
                }
            }
            if(user == null){
                return@runBlocking false
            }
            return@runBlocking true
        }
        return false
    }  

这是我的ViewModel:

class LoginViewModel(app: Application) : AndroidViewModel(app) {
    val context: Context = app.applicationContext
    private val userService = UsersService(context)

    fun getUserAsync(username: String, password: String) = GlobalScope.async {
        userService.checkLogin(username, password)
    }
}

UsersService:

class UsersService(ctx: Context) : IUsersService {
        private val db: Database = getDatabase(ctx)
        private val api = WebApiService.create()
        override fun insertUser(user: Users): Long {
            return db.usersDao().insertUser(user)
        }

        override suspend fun checkLogin(username: String, pass: String): Users? {
            return api.checkLogin(username, pass)
        }
    }

    interface IUsersService {
        fun insertUser(user: Users) : Long
        suspend fun checkLogin(username: String, pass: String): Users?
    }

这是我的apiInterface:

interface WebApiService {

    @GET("users/login")
    suspend fun checkLogin(@Query("username") username: String,
                   @Query("password")password: String) : Users

等待从服务器检索数据时,如何解决阻止活动的问题?

Tenfour04

您永远不要runBlocking在Android应用中使用。它仅用于mainJVM应用程序功能或测试中,以允许使用在应用程序退出之前完成的协程。否则,它会破坏协程的目的,因为它会阻塞直到所有的lambda返回为止。

您也不应该使用GlobalScope,因为在Activity关闭时,取消作业很棘手,并且它在后台线程而不是主线程中启动协程。您应该为活动使用本地范围。为此,您可以在活动(val scope = MainScope())中创建一个属性,然后在onDestroy()scope.cancel())中取消该属性或者,如果您使用androidx.lifecycle:lifecycle-runtime-ktx库,则可以仅使用现有lifecycleScope属性。

而且,如果您总是await在返回之前执行异步作业,那么整个函数将一直阻塞,直到获得结果为止,因此您已执行了后台任务并使它阻塞了主线程。

您可以通过几种方法解决此问题。

  1. 使ViewModel公开一个suspend函数,然后活动从协程调用它。
class LoginViewModel(app: Application) : AndroidViewModel(app) {
    //...

    // withContext(Dispatchers.Default) makes the suspend function do something
    // on a background thread and resumes the calling thread (usually the main 
    // thread) when the result is ready. This is the usual way to create a simple
    // suspend function. If you don't delegate to a different Dispatcher like this,
    // your suspend function runs its code in the same thread that called the function
    // which is not what you want for a background task.
    suspend fun getUser(username: String, password: String) = withContext(Dispatchers.Default) {
        userService.checkLogin(username, password)
    }
}

//In your activity somewhere:
lifecycleScope.launch {
    user = viewModel.getUser(login_username.text.toString(), login_password.text.toString())
    // do something with user
}
  1. 使用适当的viewmodel封装,Activity实际上不必启动这样的协程。user属性应该是活动可以观察到的ViewModel中的LiveData。因此,协程仅需要从ViewModel内部启动:
class LoginViewModel(app: Application) : AndroidViewModel(app) {
    //...
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user

    init {
        fetchUser()
    }

    private fun fetchUser(username: String, password: String) = viewModelScope.launch {
        val result = withContext(Dispatchers.Default) {
            userService.checkLogin(username, password)
        }
        _user.value = result
    }
}

//In your activity somewhere:
viewModel.user.observe(this) { user ->
    // do something with user
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

LifecycleScope中的Kotlin协程不会阻塞主线程

Kotlin的启动是否在主线程或后台线程中启动协程?

Android-处理IllegalStateException的Kotlin协程:无法访问主线程上的数据库

如何在主线程上使用Kotlin协程await()

Kotlin协程-在运行阻塞中使用主线程

Kotlin 协程如何设法在主线程上调度所有协程而不阻塞它?

为什么Kotlin协程会阻止此代码?

Kotlin协程中的“ +”?

Kotlin协程-嵌套协程是否是处理一个协程中不同线程的正确方法?

Android和Kotlin协程:是否可能用完线程?

如何从Android Kotlin协程获取结果到UI线程

异步/等待Kotlin协程阻止代码

即使协程上下文不是Dispatchers.Main,runBlocking(Dispatchers.IO)也会阻塞主线程吗?

如何在主线程上使用协程GlobalScope?

等待协程结果而不阻塞主线程

协程在主线程而不是后台上运行

当主线程退出时协程会发生什么?

在Kotlin中测试协程

kotlin 协程 - 为什么 Thread.sleep 不会在启动时暂停主线程?

Kotlin协程比线程花费的时间更长

Kotlin协程-线程切换

使用Kotlin协程进行多线程

Kotlin协程:等待多个线程完成

Kotlin协程在UI线程上等待

使用协程的Kotlin / Native多线程

Kotlin-协程通道-它们会阻塞调用线程吗?

如何在 Android 中管理依赖的 Kotlin 协程

如何在 Android 中管理依赖的 Kotlin 协程

为什么和如何Kotlin协程即使没有“ suspend”关键字也能阻止线程阻塞?