前言
趁机学一下kotlin协程,笔记来自安卓官网。关于协程的一些理解,大家可以看 抛物线的视频,B站就有,讲的很透彻。这个主要对于官网学习网站的翻译,学习网站见后记。官网的是中文,就不写在这里了。写的有点乱,主要用于回顾。
理解
- 协程都要运行在CoroutineScope中,一个scope通过其工作控制协程的生命,当取消scope,就会取消在这个scope里的所有协程。
- 因为协程可以很容易的在任何时间切换线程,并且返回结果给源线程,所以在主线程开始一个UI关联的协程是个好方式。例如ROOM、retrofit这些库在使用协程时是主线程安全的,所以不需要使用者在网络或者数据库调用时去管理线程。
- viewmodelScope 是viewmodel的扩展,这个scope被绑定在Dispatchers.Main上,当viewmodel被清除时会自动cancel。
代码示例
-
viewModelScope的调度器是Dispatchers.Main,这里运行在主线程
最左边的箭头+波浪线的符号应该是用了协程的标志
2.使用协程
首先,使用suspend操作符告诉kotlin,这个方法和协程一起工作
suspend fun refreshTitle() {
delay(500)
}
viewModelScope.launch {
try {
_spinner.value = true
repository.refreshTitle()
} catch (error: TitleRefreshError) {
_snackBar.value = error.message
} finally {
_spinner.value = false
}
}
- 切换线程
1》之前主线程安全的方法,类似这样:
fun refreshTitleWithCallbacks(titleRefreshCallback: TitleRefreshCallback) {
Executors.newFixedThreadPool(2).submit {
try {
val result = network.fetchNextTitle().execute()
if (result.isSuccessful) {
titleDao.insertTitle(Title(result.body()!!))
titleRefreshCallback.onCompleted()
} else {
titleRefreshCallback.onError(
TitleRefreshError("Unable to refresh title", null))
}
} catch (cause: Throwable) {
titleRefreshCallback.onError(
TitleRefreshError("Unable to refresh title", cause))
}
}
}
那么协程中会如何做呢?
suspend fun refreshTitleWithCallbacks() {
withContext(Dispatchers.IO) {
val result = try {
network.fetchNextTitle().execute()
} catch (cause: Throwable) {
throw TitleRefreshError("Unable to refresh title", null)
}
if (result.isSuccessful) {
titleDao.insertTitle(Title(result.body()!!))
} else {
throw TitleRefreshError("Unable to refresh title", null)
}
}
}
注意:这里暂没有去做cancel操作,一般来说是需要在必要的时候明确cancel的。
- diapatcher 调度器介绍
Main:主线程 ;IO: 从网络或者数据库读取数据;Default:CPU密集型工作 - 在room和retrofit中
1>retrofit
interface MainNetwork {
@GET("next_title.json")
//改变前 fun fetchNextTitle(): Call<String>
suspend fun fetchNextTitle(): String
}
2>room
@Insert(onConflict = OnConflictStrategy.REPLACE)
//改变前 fun insertTitle(title: Title)
suspend fun insertTitle(title: Title)
3>方法
suspend fun refreshTitleWithCallbacks() {
try {
val result = network.fetchNextTitle()
titleDao.insertTitle(Title(result))
} catch (cause: Throwable) {
throw TitleRefreshError("Unable to refresh title", null)
}
}
如果设置5s请求超时
suspend fun refreshTitleWithCallbacks() {
try {
val result = withTimeout(5000){
network.fetchNextTitle()
}
titleDao.insertTitle(Title(result))
} catch (cause: Throwable) {
throw TitleRefreshError("Unable to refresh title", null)
}
}
- 抽suspend方法当参数进行封装
咱们使用2使用协程的代码来进行封装抽取
fun launchDataLoad(block: suspend () -> Unit): Job {
return viewModelScope.launch {
try {
_spinner.value = true
block()
} catch (error: TitleRefreshError) {
_snackBar.value = error.message
} finally {
_spinner.value = false
}
}
}
fun refreshTitle() {
launchDataLoad {
repository.refreshTitle()
}
}
学习网站
后记
携程取消方式
进阶 Part 1: Coroutines, Part 2: Cancellation in coroutines, and Part 3: Exceptions in coroutines
与 在livedata中使用 及其他进阶后续再写。
网友评论