- 业务框架层指的是我们常用的协程函数
基础设施层(原生api)实现例子
private fun doContinuation() {
//协程体
val continuation = suspend {
println("协程执行中...")
"协程的返回值"
}.createCoroutine(object : Continuation<String> {
override fun resumeWith(result: Result<String>) {
//回调
println("协程执行结束: $result")
}
override val context: CoroutineContext = EmptyCoroutineContext
})
continuation.resume(Unit)
}
如上代码使用的是:import kotlin.coroutines.*
而kotlin协程业务框架层使用的是:import kotlinx.coroutines.*
Android中协程解决了什么问题?
-
处理耗时任务,这种任务常常会阻塞主线程
-
保证主线程安全,确保安全的在主线程调用suspend函数
在Android 11 谷歌建议使用协程来替代异步任务(asynctask)
协程的挂起与恢复
常规函数包括:invoke(call)和return,协程新增了suspend和resume
suspend:挂起或暂停,表示暂停执行当前协程,并保存所有局部变量
resume:让已暂停的协程从暂停处恢复执行
使用 suspend关键字修饰的函数叫挂起函数
挂起函数只能在协程体内或其他挂起函数内调用
挂起和阻塞
挂起:挂起点先记录下来,然后去做耗时任务,做完以后再恢复
阻塞:不做别的事情,一直等待
private fun doGlobalScope() {
//如果没有指定调度器,那么默认就是使用:Dispatchers.Default(default也是非主线程)
GlobalScope.launch(Dispatchers.Main) {
//挂起(不会阻塞主线程,比如按钮按下去会立马弹起来,然后6秒后打印)
delay(6000)
Log.v("zx", "${Thread.currentThread().name},挂起6秒后")
}
//阻塞(会阻塞主线程,比如按钮按下去要等5秒后才能弹起来)
Thread.sleep(5000)
Log.v("zx", "${Thread.currentThread().name},阻塞5秒后")
}
主线程在遇到挂起点后,可以不用等待直接更新UI,但是遇到阻塞就必须等待阻塞完成之后
协程的调度器 Dispatchers
所有协程必须的调度器中运行
Dispatchers.Main:主线程,处理UI交互和轻量级任务(调用suspend,调用UI函数,更新liveData)
Dispatchers.IO:非主线程,为磁盘和网络IO进行了优化(数据库,文件读写,网络请求)
Dispatchers.Default:非主线程,专为CPU密集型任务进行了优化(数组排序,json解析,差异判断)
任务泄露
当某个协程任务丢失,导致内存,cpu资源浪费,称为任务泄露,为了避免协程泄露,Kotlin引入了结构化并发机制。
结构化并发的作用:取消任务,追踪任务,发送错误信号
结构化并发
定义协程,必须指定其CoroutineScope,它会跟踪所有协程,还可以取消由它所启动的所有协程。
常用的api有:
GlobalScope:生命周期是process级别的,即使Acitivty和fragment已经销毁,协程仍然在执行,GlobalScope是一个顶级协程,不太建议直接用
MainScope:在Activity中使用,可以在onDestroy中取消协程
viewModelScope:只能在viewModel中使用,绑定viewModel的生命周期
lifecycleScope:只能在Activity和fragment中使用,会绑定Activity和Fragment的生命周期
MainScope使用案例:
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val mainScope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
doMainScope()
}
private fun doMainScope() {
//retrofit不用写withcontext(Dispatchers.IO)因为retrofit会自动侦察到,
//如果你是挂起函数会自动启用协程,创建一个异步线程去做操作
mainScope.launch {
//Toast.makeText(this@MainActivity, "MainScope", Toast.LENGTH_SHORT).show()
try {
//retrofit请求
delay(1000)
} catch (e: Exception) {
//调用 mainScope.cancel()取消协程会抛出异常
e.printStackTrace()
}
}
//MainActivity 继承了委托以后CoroutineScope by MainScope(),就可以直接使用launch了
launch {
}
}
override fun onDestroy() {
super.onDestroy()
mainScope.cancel()
//class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {}
//MainActivity 继承了委托以后CoroutineScope by MainScope(),就可以直接使用cancle
cancel()
}
}
viewModelScope使用案例
class MyViewModel:ViewModel() {
private val _count = MutableLiveData<Int>()
val count: LiveData<Int>
get() = _count
fun getUser(){
//因为继承了ViewModel,那么就可以直接使用viewModelScope
viewModelScope.launch {
//如果是耗时操作的话,retrofit会自动起一个io线程来执行,可以省略withContext(Dispatchers.IO)
delay(3000)
_count .value = 123
}
}
}
调用
class MainActivity : AppCompatActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val myViewModel by viewModels<MyViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initViewModelAndLiveData()
}
private fun initViewModelAndLiveData() {
myViewModel.count.observe(this, Observer {
binding.btnRandom.text = it.toString()
})
myViewModel.getUser()
}
}
=======================================================================
launch和async构建器都用来启动新协程
launch,返回一个job并且不附带任何结果值
async,返回一个Deferred,Deferred也是一个job,可以使用.await()在一个延期的值上得到它的最终结果
//等待一个作业:join与await
private fun runBlocking1(){
//runBlocking可以把主线程变成一个协程
//job1和job2是runBlocking的子协程
//runBlocking会等待job1和job2这两个子协程执行完毕,会阻塞主线程(阻塞:按钮按下不会立马弹起job1和job2执行完了才会弹起)
runBlocking {
val job1 = launch {
delay(2000)
Log.v("zx", "job1 to finish")
}
val job2 = async {
delay(2000)
Log.v("zx", "job2 to finish")
"job2 value"
}
//await可以得到返回值
val job2Result = job2.await()
Log.v("zx", "job2的返回值:$job2Result")
}
需求:等待job1执行完毕以后再执行job2和job3
如果通过launch来启动的话,用join函数
如果通过async来启动的话,用await函数
//join和await都是挂起函数,不会阻塞主线程
//如果通过launch来启动的话,用join函数
runBlocking {
val job1 = launch {
delay(2000)
Log.v("zx", "job1 to finish")
}
//这个函数会等待job1执行完后才会执行后面的
job1.join()
val job2 = launch {
delay(100)
Log.v("zx", "job2 to finish")
}
val job3 = launch {
delay(100)
Log.v("zx", "job3 to finish")
}
}
//如果通过async来启动的话,用await函数
runBlocking {
val job1 = async {
delay(2000)
Log.v("zx", "job1 to finish2")
}
//这个函数会等待job1执行完后才会执行后面的
job1.await()
val job2 = async {
delay(100)
Log.v("zx", "job2 to finish2")
}
val job3 = async {
delay(100)
Log.v("zx", "job3 to finish2")
}
}
}
需求:前面2个任务相加的结果给第三个任务(async结构化并发)
//runBlocking 在主线程中,子协程会继承父协程的上下文
//runBlocking是Dispatchers.Main中启动的,doOne和doTwo也会使用父协程的调度器Dispatchers.Main中启动
private fun runBlocking2() {
//前面2个任务相加的结果给第三个任务(async结构化并发)
runBlocking {
val time = measureTimeMillis {
//同步的
val one = doOne()
val two = doTwo()
Log.v("zx", "数据${one + two}")
}
Log.v("zx", "time = $time")
}
runBlocking {
val