MainScope

在Android中使用协程框架,除了要引入协程框架的核心模块以外,还需要 引入以下依赖:org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3,它提供了Dispatchers.Main在Android上的实现。

需要把协程与UI的生命 周期关联起来,避免内存泄露。框架中还有一个函数MainScope,它可以创建一个基于UI调度器的主从作用域:

class ScopedActivity:  AppCompatActivity(){
     private val mainScope by lazy { MainScope() }

     override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_scoped)

       button.setOnClickListener {
           mainScope.launch {
               ...// 调度到UI线程 
           }
        } 
    }

    override fun onDestroy() { 
        super.onDestroy() 
        //用完销毁 
        mainScope.cancel()
    } 
}

作用域的好处就是可以方便地绑定到UI组件的生命 周期上,在Activity销毁的时候直接取消,所有该作用域启动的协程 就会被取消。

lifecycleScope

KTX为Jetpack的Lifecycle相关组件都提供了已经绑定了UI生命周期的作用域供直接使用,添加Lifecycle相应的基础组件之后,再添加以下组件: androidx.lifecycle:lifecycle-runtime-ktx:2.2.0,lifecycle-runtime-ktx提供了LifecycleCoroutineScope类及其获得方式,可以直接在Activity中使用lifecycleScope来获取这个实例:

class MainActivity : AppCompatActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       button.setOnClickListener {
         lifecycleScope.launch {
            ...// 执行协程体 
         }
       } 
    }
}

这是因为MainActivity的父类实现了LifecycleOwner接口,而lifecycleScope则是它的扩展成员。

viewModelScope

在ViewModel中使用作用域,需要再添加以下依赖: androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0:

class MainViewModel : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            ... // 执行协程体 
        }
    } 
}

ViewModel的作用域会在它的clear函数调用时取消。

repeatOnLifecycle

lifecycleScope虽然解决了内存泄漏问题, 但是 lifecycleScope.launch 会立即启动协程,之后一直运行直到协程销毁,无法像 LiveData 仅当 UI 处于前台才执行,对资源的浪费比较大。

repeatOnLifecycle‌ 则确保协程仅在目标生命周期状态(如 STARTED 或 RESUMED)时运行,并在退出该状态时自动取消,避免资源浪费。

核心特性

  • 生命周期感知‌:仅当 Lifecycle 至少处于指定状态(如 STARTED)时执行代码块;低于该状态时自动取消。
  • 可重启行为‌:每次进入目标状态都会启动新协程,退出时彻底取消,适合需要“重新开始”的场景(如网络请求、数据库订阅)。
  • 结构化并发‌:作为挂起函数,天然支持协程取消传播,父协程取消时子协程也会被取消。
  • 线程安全‌:内部使用 Dispatchers.Main.immediate,适合在主线程操作 UI。

https://developer.aliyun.com/article/940765

ORM框架Room中的协程扩展

Room是Jetpack中的ORM框架,它提供了对事务的支持及DAO的生成 机制等能力,主要用来简化Android中对SQLite的访问。

  @Entity(tableName = "user", primaryKeys = ["id"])
   data class User(
     @ColumnInfo(name="id") val id: Long,
     @ColumnInfo(name="name") val name: String,
     @ColumnInfo(name="age") val age: Int)

定义一个User类来对应数据库的user表,再定义一个UserDao 接口来提供操作数据库的能力。


   @Dao
   interface UserDao {
     @Insert
     suspend fun insert(user: User)
     @Query("SELECT * from user")
     fun listUsers(): List<User>
   }

UserDao的实现类不需要开发者自己定义,编译时Room会使用注解 处理器自动生成。由于数据库读写是I/O操作,可能会阻塞,因此 UserDao的函数不能在UI线程上运行。Room支持挂起函数,对于可挂起的接口函数,生成的实现与普通函数是不同的。

网络框架Retrofit中的协程扩展

Retrofit(https://gihub.com/square/retrofit)是最早一批开始支持Kotlin协程的框架。

协程风格的对话框

切换线程一定产生异步,但不切换线程切换函数调用栈也可以产生异步。对话框作为一个UI组件必须运行在UI线程上,却因为需要等待用户操作而提供了取消、确认等回调,这些同样可以用协程来简化。

   suspend fun Context.alert(title: String, message: String): Boolean =
     suspendCancellableCoroutine {continuation ->
       AlertDialog.Builder(this)
         .setNegativeButton("No"){ dialog, which ->
           dialog.dismiss()
           continuation.resume(false)
         }.setPositiveButton("Yes"){
           dialog, which ->
           dialog.dismiss()
           continuation.resume(true)
         }.setTitle(title)
         .setMessage(message)
         .setOnCancelListener {
           continuation.resume(false)
         }.create()
         .also { dialog ->
           continuation.invokeOnCancellation {
             dialog.dismiss()
           }
        }.show() 
    }

协程版的对话框的使用

   lifecycleScope.launch {
     val myChoice = alert("Warning!", "Do you want this?")
toast("My choice is: $myChoice")
   }
lifecycleScope.launch {
    val myChoice = alert("Warning!", "Do you want this?")
    toast("My choice is: $myChoice")
}

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐