赞
踩
我们都知道,通过以下代码就可以启动一个协程进行运行:
CoroutineScope(Dispatchers.IO).launch {
}
但是,这个跟我们启动线程一样,具有一个很大的问题,就是不可控性,我们知道它什么时候启动,却不知道什么时候销毁,就像我们跳转在一个页面进行数据加载的时候,可以使用协程去加载,但是,我们也希望当该页面销毁的时候,该协程也能够销毁。
其实,协程也提供这样的一种方式:
class MainActivity : AppCompatActivity() { var job: Job? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("onCreate 正在执行") job = CoroutineScope(Dispatchers.IO).launch { for(index in 0..10000000000){} println("Dispatchers.IO 执行完成") } } override fun onDestroy() { println("onDestroy 正在执行") job?.cancel() super.onDestroy() } }
其实就是把 CoroutineScope(Dispatchers.IO).launch
返回的 Job 对象保存起来,然后适时调用其 cancel()
方法。这样就可以取消该线程了。
我们来看下输出:
I/System.out: onCreate 正在执行
I/System.out: onDestroy 正在执行
I/System.out: Dispatchers.IO 执行完成
这是什么回事?不是说好可以取消的吗?怎么没有取消,还把任务执行完了???
其实,这个不难理解,因为 cancel()
的调用其实更像标识符,并不会真正的取消协程,原理跟这个类似 sleep()为什么要 try catch,有兴趣的可以看下。
所以,我们只需要这样更改即可:
job = CoroutineScope(Dispatchers.IO).launch {
for(index in 0..10000000000){}
if (isActive){
println("Dispatchers.IO 执行完成")
}
}
不过,这里又引出了另外一个问题,既然协程在适时需要取消,那有没有一种方式,像 LiveData 一样,能够自动监听生命周期,让其自动取消?
答案当然使用有的,而且 Google 官方还有几种方式,分别为:
GlobalScope 相当于 CoroutineScope(Dispatchers.Default)
,会自动开启一个线程去执行协程里面的代码。
使用方式为:
GlobalScope.launch{ }
所以,若要暂停该协程,还是需要存储 Job 对象,然后调用 cancel()
方法。
MainScope 可以理解为 CoroutineScope(Dispatchers.Main)
,所以,也可以通过以下方式进行使用:
MainScope().launch { }
不过,以这种方式进行调用,在 Activity 销毁的时候,其实也不会去取消协程,若要取消协程,还是得存储 Job 对象,然后调用 cancel()
方法。
为了方便调用 cancel()
,我们可以考虑将其放在基类中,例如 BaseActivity
中:
class BaseActivity : CoroutineScope by MainScope(){
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
if (isActive) { }
}
}
override fun onDestroy() {
cancel()
super.onDestroy()
}
}
使用 MainScope
能够比较方便的调用 cancel()
,但是,在含有生命周期的对象时,其实,我们更希望它自动去取消协程。而这时,我们可以考虑使用 LifecycleScope
。
LifecycleScope 的作用域默认是在主线程中,所以,我们可以把 LifecycleScope 理解为监听 LifeCycle + CoroutineScope(Dispatchers.Main)
。
使用 LifecycleScope
首先要添加依赖:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
然后我们就可以直接在 AppCompatActivity 和 Fragment 中使用 lifecycleScope 开启协程:
lifecycleScope.launch { }
也可以使用 LifecycleOwner.lifecycleScope,用法都是一样的,开启协程后,都无需自己去取消该协程,当 LifeCycle 回调 onDestroy()
的时候会自动取消协程。
当然,若想要自己去取消协程,也无需自己去保存 Job 对象去取消,而是直接这样调用即可:
lifecycleScope.cancel()
另外,lifecycleScope 还有一种特殊的用法,就是可以指定在什么哪个生命周期方法执行完后再 launch,使用方式:
lifecycleScope.launchWhenCreated { }
lifecycleScope.launchWhenStarted { }
lifecycleScope.launchWhenResumed { }
在这里,拿 launchWhenResumed
试验下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("onCreate 正在执行") lifecycleScope.launchWhenResumed { println("launchWhenResumed 正在执行") } } override fun onStart() { super.onStart() println("onStart 正在执行") } override fun onResume() { super.onResume() println("onResume 正在执行") } }
输出结果:
I/System.out: onCreate 正在执行
I/System.out: onStart 正在执行
I/System.out: onResume 正在执行
I/System.out: launchWhenResumed 正在执行
LifecycleScope 在 AppCompatActivity 和 Fragment 中使用已经十分方便了,不过,目前很多项目都开始使用 MVVM,更多逻辑操作都是在 ViewModel 层,这时,就需要用到 ViewModelScope 了,它会自动绑定 ViewModel 的生命周期。
若需要使用 ViewModelScope,我们需要添加依赖:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
创建一个 ViewModel :
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
println("viewModelScope launch --- threadName: ${Thread.currentThread().name}")
delay(5000)
if (isActive){
println("viewModelScope launch 执行完成")
}
}
}
}
为了方便使用 viewModels()
去生成 ViewModel,还需要添加依赖:
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.fragment:fragment-ktx:1.4.1"
在 Activity 中写测试代码:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("onCreate 正在执行") val model: MyViewModel by viewModels() model.toString() } override fun onDestroy() { super.onDestroy() println("onDestroy 正在执行") } }
运行,当 Activity 启动后按返回键关闭 Activity,即可看到以下输出结果:
I/System.out: onCreate 正在执行
I/System.out: viewModelScope launch --- threadName: main
I/System.out: onDestroy 正在执行
由此也可以看出,viewModelScope 默认执行的线程也是主线程,当 Activity 和 Fragment 销毁后,也会自动取消协程。
当然,假如你想要手动取消协程,也可以通过这种方式:
viewModelScope.cancel()
em…LiveData 跟上面所说的几种协程封装有些不太一样,大家都知道,LiveData 的核心功能并不是操控协程,所以,这里指的是 LiveData 与协程的一个协作。
首先,我们需要添加 LiveData 的依赖:
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
创建 LiveData:
private val userName: LiveData<String> = liveData {
val name = getName()
emit(name)
}
private suspend fun getName(): String{
delay(3000)
println("设置名字 thread: ${Thread.currentThread().name}")
return "不近视的猫"
}
这里需要注意的是,getName()
是个挂起函数,并且我在里面模拟了耗时操作,并且拿到值后,通过 emit(name)
去修改值
对于 LiveData 进行监听:
userName.observe(this){
println("名字更新了: $it")
}
运行结果:
I/System.out: 设置名字 thread: main
I/System.out: 名字更新了: 不近视的猫
LiveData 总结:
liveData{}
去创建 LiveData 对象,并且可以在里面执行挂起方法,在获取到值后,使用 emit()
去更新值。通过本文,相信大家对于 Kotlin 协程的结构化并发有着一定的理解了,但是,不知道大家有没有想过一个问题,那就是以上协程运行大多都是默认在主线程中,那有没有办法让其在其它线程执行?
答案当然是有的,而且也很简单,这里,我以 lifecycleScope
为例:
lifecycleScope.launch(Dispatchers.IO) {
println("1---thread: ${Thread.currentThread().name}")
delay(5000)
if (isActive){
println("2---thread: ${Thread.currentThread().name}")
}
}
启动后,按返回键关闭页面:
I/System.out: 1---thread: DefaultDispatcher-worker-1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。