背景
搜索了很多资料,花了几天时间看完,却发现 依然不知道怎么在我的项目中使用协程.
原因有两个:
1.学习的时候目标不明确,没有带着问题去学.
2.一个陌生的知识点,和Java的编程思维还不太一样,了解到熟练有一个过程.
解决办法
1. 快速启动协程
目标: 用协程替换一个子线程执行的场景.
5S后子线程发送一个消息给主线程.
Thread() {
mHandler.sendEmptyMessageDelayed(SKIP_MAIN, 5 * 1000.toLong())
}.start()
改写后:
CoroutineScope(Dispatchers.Default).launch {
delay(5000)
mHandler.sendMessage(Message.obtain().apply { what = SKIP_MAIN })
}
- 启动协程的示例有三个runBlocking GlobalScope CoroutineScope 选哪一个?
选CoroutineScope,因为runBlocking阻塞 阻塞了为什么还要用协程呢. GlobalScope全局.全局意味着存在不可控的风险.
- Dispatchers的三个模式IO,Default,Main怎么用?
Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。此调度程序只能用于与界面交互和执行快速工作。示例包括调用 suspend 函数、运行 Android 界面框架操作,以及更新 LiveData 对象。
Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络 I/O。示例包括使用 Room 组件、从文件中读取数据或向文件中写入数据,以及运行任何网络操作。
Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。
2. 替换回调
目标: 用协程替换一个回调执行的场景
参考这儿: [Kotlin协程] 回调地狱的一种解决思路
3. 解决内存泄露
目标: 用协程解决子线程造成内存泄露的一个场景
在一个Activity中
class MyHandler : Handler {
var context: WeakReference<Context>
constructor(context: Context) {
this.context = WeakReference(context)
}
override fun handleMessage(msg: Message) {
when (msg.what) {
SKIP_MAIN -> {
context.get()?.apply {
startActivity(Intent(this, MainActivity::class.java))
}
}
else -> throw IllegalArgumentException()
}
super.handleMessage(msg)
}
}
private val mHandler = MyHandler(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Thread() {
mHandler.sendEmptyMessageDelayed(SKIP_MAIN, 5 * 1000.toLong())
}.start()
// CoroutineScope(Dispatchers.Default).launch {
// delay(5000)
// mHandler.sendMessage(Message.obtain().apply { what = SKIP_MAIN })
// }
}
很容易看出,如果5S内我退出了App,5S后还是会打开另一个Activity;
- 修改方式1 直接给mHander在onDestory的时候赋空. 阻止其发送消息
- 修改方式2 在mHander的handleMessage方法中判断当前Activity的状态. 如果是isFinishing 就不执行跳转
上诉两种方式修改跳转的问题是OK的,但是并没有取消thread的执行. 如果使用协程(也就是上诉我注释掉的代码)并且在onDestory执行取消方法即可解决.
- 修改方式3. 使用 CoroutineScope()方法
val coroutineScope = CoroutineScope(Dispatchers.Default);
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
coroutineScope.launch {
delay(5000)
mHandler.sendMessage(Message.obtain().apply { what = SKIP_MAIN })
}
}
override fun onDestroy() {
super.onDestroy()
coroutineScope.cancel()
}
4. 解决业务耦合
这个Activity用到协程,需要在destory中取消,那么其他Activity中肯定也需要用到. 将代码抽取到BaseActivity中.
abstract class BaseActivity : AppCompatActivity(), CoroutineScope {
protected lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
setContentView(LayoutInflater.from(this).inflate(bindLayoutId(), null))
initData(savedInstanceState)
}
override fun onDestroy() {
job.cancel()
super.onDestroy()
}
protected abstract fun initData(savedInstanceState: Bundle?)
}
那么子Activity只需要直接执行,不要关心创建和取消任务的逻辑.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
delay(5000)
mHandler.sendMessage(Message.obtain().apply { what = SKIP_MAIN })
}
}
总结
带着问题去学习,思考下这个的作用是什么?可以解决实际项目中的哪些问题? 你也可以试试这个方法,尝试写下这四个demo,可能比枯燥看书来得更适用一些.
- 用协程替换new Thead执行
- 用协程的通道替换异步回调
- 用协程的cancel()解决一个异步任务造成内存泄露的case
- 用CoroutineScope接口 + Job剥离协程和业务代码的耦合