class MediaSelectActivity : BaseActivity() {
private val videoInfoViewModel by lazy { ViewModelProvider(this).get(VideoInfoViewModel::class.java) }
private fun initData() {
videoInfoViewModel.getSystemVideos(contentResolver)
}
}
class VideoInfoViewModel : ViewModel() {
val getSystemVideosLiveData: MutableLiveData<List<MediaInfo>> by lazy { MutableLiveData<List<MediaInfo>>() }
private val model by lazy { VideoInfoModel() }
fun getSystemVideos(contentResolver: ContentResolver) {
viewModelScope.launch {
getSystemVideosLiveData.value = model.getSystemVideos(contentResolver)
}
}
}
class VideoInfoModel {
suspend fun getSystemVideos(contentResolver: ContentResolver) = withContext(Dispatchers.IO) {
VideoInfoUtils.getSystemVideos(contentResolver)
}
}
执行过程概述如下:
应用从主线程上的 View 层 MediaSelectActivity 调用 ViewModel 层 getSystemVideos 函数;
getSystemVideos 函数 会使用 viewModelScope 作用域 launch 创建一个新协程;
Model 层使用 withContext(Dispatchers.IO) 在为 I/O 操作预留的线程上,阻塞执行耗时请求;
suspend 方法执行结束后,将返回值返回给主线程上的 View 层调用处。
1 CoroutineScope 指定协程作用域
CoroutineScope 会跟踪它使用 launch 或 async 创建的所有协程,所有协程都必须在一个作用域内运行;
一个 CoroutineScope 管理一个或多个相关的协程;
与调度程序不同,CoroutineScope 不运行协程。
1.1 分类
GlobalScope:全局协程作用域,在这个范围内启动的协程可以一直运行直到应用停止运行;本身不会阻塞当前线程,且启动的协程相当于守护线程,不会阻止 JVM 结束运行
runBlocking:一个顶层函数,和 GlobalScope 不一样,它会阻塞当前线程直到其内部所有相同作用域的协程执行结束
coroutineScope:用于创建一个独立的协程作用域,直到所有启动的协程都完成后才结束自身;runBlocking 方法会阻塞当前线程,而 coroutineScope不会,而是会挂起并释放底层线程以供其它协程使用;runBlocking 是一个普通函数,而 coroutineScope 是一个挂起函数
CoroutineScope:自定义协程作用域,如:CoroutineScope(Dispatchers.Main).launch { }
lifecycleScope:Lifecycle 中使用,Activity 和 Fragment 等使用
viewModelScope:ViewModel中使用,预定义的CoroutineScope,包含在 KTX 扩展中
2 withContext 指定执行协程的线程
Dispatchers.Main - 在 Android 主线程上运行协程
Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络 I/O
Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作
如:withContext(Dispatchers.IO)创建一个在 IO 线程池中运行的块。
3 launch 启动协程
创建协程并将其函数主体的执行分派给相应的调度程序。
4 suspend 挂起函数
suspend 用于暂停执行当前协程,并保存所有局部变量
resume 用于让已暂停的协程从暂停处继续执行
只能从其他 suspend 函数进行调用,或通过使用协程构建器(例如 launch)来启动新的协程
挂起函数不会阻塞其所在线程,而是会将协程挂起,在特定的时候才再恢复执行
kotlinx.coroutines.Delay 下的 delay() 函数就是一个 suspend 修饰的挂起函数,如下:
public suspend fun delay(time: Long)
delay() 函数类似于 Java 中的 Thread.sleep(),但它和单纯的线程休眠不同的是,delay() 函数是非阻塞的。
例如,当在 ThreadA 上运行的 CoroutineA 调用了delay(1000L)函数指定延迟一秒后再运行,ThreadA 会转而去执行 CoroutineB,等到一秒后再来继续执行 CoroutineA。而使用Thread.sleep()的话,线程就只能干等着而不能去执行其它任务。
5 常见搭配
CoroutineScope(Dispatchers.Main).launch { }
GlobalScope.launch {}
lifecycleScope.launch { }
viewModelScope.launch { }