赞
踩
在Android中,官方推荐和Jetpack架构组件一起使用
(1)引入协程
- implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3’
(2)引入Jetpack架构组件的KTX扩展
- 对于 ViewModelScope,请使用 androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01 或更高版本。
- 对于 LifecycleScope,请使用 androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01 或更高版本。
- 对于 liveData,请使用 androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01 或更高版本。
应用中的每个 ViewModel 定义了 ViewModelScope。如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。如果您具有仅在 ViewModel 处于活动状态时才需要完成的工作,此时协程非常有用。例如,如果要为布局计算某些数据,则应将工作范围限定至 ViewModel,以便在 ViewModel 清除后,系统会自动取消工作以避免消耗资源。
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
为每个 Lifecycle 对象定义了 LifecycleScope。在此范围内启动的协程会在 Lifecycle 被销毁时取消。您可以通过 lifecycle.coroutineScope 或 lifecycleOwner.lifecycleScope 属性访问 Lifecycle 的 CoroutineScope。
以下示例演示了如何使用 lifecycleOwner.lifecycleScope 异步创建预计算文本:
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}
Lifecycle 提供了其他方法:lifecycle.whenCreated、lifecycle.whenStarted 和 lifecycle.whenResumed。如果 Lifecycle 未至少处于所需的最低状态,则会暂停在这些块内运行的任何协程。
以下示例包含仅当关联的 Lifecycle 至少处于 STARTED 状态时才会运行的代码块:
class MyFragment: Fragment { init { // Notice that we can safely launch in the constructor of the Fragment. lifecycleScope.launch { whenStarted { // The block inside will run only when Lifecycle is at least STARTED. // It will start executing when fragment is started and // can call other suspend methods. loadingView.visibility = View.VISIBLE val canAccess = withContext(Dispatchers.IO) { checkUserAccess() } // When checkUserAccess returns, the next line is automatically // suspended if the Lifecycle is not *at least* STARTED. // We could safely run fragment transactions because we know the // code won't run unless the lifecycle is at least STARTED. loadingView.visibility = View.GONE if (canAccess == false) { findNavController().popBackStack() } else { showContent() } } // This line runs only after the whenStarted block above has completed. } } }
如果在协程处于活动状态时通过某种 when 方法销毁了 Lifecycle,协程会自动取消。在以下示例中,一旦 Lifecycle 状态变为 DESTROYED,finally 块即会运行:
class MyFragment: Fragment {
init {
lifecycleScope.launchWhenStarted {
try {
// Call some suspend functions.
} finally {
// This line might execute after Lifecycle is DESTROYED.
if (lifecycle.state >= STARTED) {
// Here, since we've checked, it is safe to run any
// Fragment transactions.
}
}
}
}
}
LifecycleScope + 生命周期状态 的使用总结
通过一种声明式的写法(即在Activity或者Fragment的初始化时,声明好各个生命周期需要做的事情),而不是程序性的写法(即在各个生命周期回调方法中处理相应逻辑),更有利于整体代码结构的把握,更易于理解和维护。所以建议大家多使用这种具有声明式的写法处理业务逻辑。
您可以使用 liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送。
在以下示例中,loadUser() 是在其他位置声明的暂停函数。使用 liveData 构建器函数异步调用 loadUser(),然后使用 emit() 发出结果:
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
当 LiveData 变为活动状态时,代码块开始执行;当 LiveData 变为非活动状态时,代码块会在可配置的超时过后自动取消。如果代码块在完成前取消,则会在 LiveData 再次变为活动状态后重启;如果在上次运行中成功完成,则不会重启。请注意,代码块只有在自动取消的情况下才会重启。如果代码块由于任何其他原因(例如,抛出 CancelationException)而取消,则不会重启。
您还可以从代码块中发出多个值。每次 emit() 调用都会暂停执行代码块:
val user: LiveData<Result> = liveData {
emit(Result.loading())
try {
emit(Result.success(fetchUser()))
} catch(ioException: Exception) {
emit(Result.error(ioException))
}
}
您也可以将 liveData 与 Transformations 结合使用,如以下示例所示:
class MyViewModel: ViewModel() {
private val userId: LiveData<String> = MutableLiveData()
val user = userId.switchMap { id ->
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
emit(database.loadUserById(id))
}
}
}
您可以从 LiveData 中发出多个值,方法是在每次想要发出新值时调用 emitSource() 函数。请注意,每次调用 emit() 或 emitSource() 都会移除之前添加的来源。
class UserDao: Dao { @Query("SELECT * FROM User WHERE id = :id") fun getUser(id: String): LiveData<User> } class MyRepository { fun getUser(id: String) = liveData<User> { val disposable = emitSource( userDao.getUser(id).map { Result.loading(it) } ) try { val user = webservice.fetchUser(id) // Stop the previous emission to avoid dispatching the updated user // as `loading`. disposable.dispose() // Update the database. userDao.insert(user) // Re-establish the emission with success type. emitSource( userDao.getUser(id).map { Result.success(it) } ) } catch(exception: IOException) { // Any call to `emit` disposes the previous one automatically so we don't // need to dispose it here as we didn't get an updated value. emitSource( userDao.getUser(id).map { Result.error(exception, it) } ) } } }
参考:
Android官网介绍:
利用 Kotlin 协程提升应用性能
将 Kotlin 协程与架构组件一起使用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。