协程
- 用同步的方式写出异步的效果
- 协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式
- 挂起函数(suspend)不一定就是在子线程中执行的,但是通常在定义挂起函数时都会为它指定其他线程,这样挂起才有意义。挂起函数只能在协程或其他挂起函数中调用。
- 解决多层嵌套回调
协程不是线程,是基于线程封装的库,可以使用协程库提供的API方便的灵活的指定协程中代码执行的线程、切换线程,但不需要接触线程Thread类。类似于Android的AsyncTask或者RxJava的Schedulers,都解决了异步线程切换的问题,然而协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式,把原本运行在不通线程的代码写在一个代码块{}里,开起来就像是同步代码。
launch
和async
之间的区别
launch
是一种适用于“发射并忘记”场景的协程构建器,当你不需要等待协程的结果时,它非常有用。它允许你在后台异步执行任务,而不会阻塞主线程,从而提高应用的响应性。
launch
函数返回一个Job
对象,我们可以使用这个对象来管理协程的生命周期。
示例:使用launch
fun main() {
println("Before launch")
// 启动一个协程
val job = GlobalScope.launch {
delay(1000)
println("Inside launch")
}
println("After launch")
Thread.sleep(2000)
job.cancel()//取消协程
}
在上面的示例中,我们使用launch
关键字在GlobalScope
中启动了一个协程。协程通过delay()
函数暂停了1000毫秒,然后打印了"Inside launch"。同时,主线程继续执行。输出结果为:Before launch After launch Inside launch
async
是一种用于异步执行任务并获取其结果的协程构建器。它返回一个Deferred<T>
对象,表示将来可用的值,这个对象表示一个可以延期获取结果的异步计算。
示例:使用async并发进行网络请求
suspend fun fetchDataFromAPI(url: String): String {
// 执行网络请求或其他耗时操作
return apiService.fetchData(url)
}
suspend fun fetchMultipleData() {
val deferredData1 = GlobalScope.async(Dispatchers.IO) {
fetchDataFromAPI("https://api.example.com/data1")
}
val deferredData2 = GlobalScope.async(Dispatchers.IO) {
fetchDataFromAPI("https://api.example.com/data2")
}
val data1 = deferredData1.await()
val data2 = deferredData2.await()
// 处理获取到的数据
```kotlin
processData(data1, data2)
}
在这个示例中,我们使用async
来并发地从多个URL获取数据。每个网络请求被封装在一个单独的async
块中,它返回一个Deferred<String>
对象。然后我们使用await()
来在结果可用时获取结果。获取到数据后,我们可以根据需要进行进一步处理。
总结选择launch
和async
的关键考虑因素:
-
当你想要执行一个异步任务而不需要等待结果时,例如进行网络请求或执行后台操作时,使用
launch
。 -
当你需要并发地执行多个异步任务并获取它们的结果以进行进一步处理时,例如并行网络请求或计算时,使用
async
。 -
要注意选择合适的上下文来启动协程,以确保正确的线程管理。对于与UI相关的操作,切换到
Dispatchers.Main
上下文来更新UI。 -
避免使用长时间运行的操作阻塞主UI线程。使用协程将这些任务转移到后台线程,从而确保响应性用户界面
suspend:withContext(Dispatchers.IO)异步线程
MainScope 需要销毁在onDestroy()方法中:main.cancel()
GlobalScope 全局作用域 默认是异步线程Dispatchers.IO
viewModelScope.launch 默认是主线程Dispatchers.Main
一个页面请求多个接口示例
class SystemViewModel : BaseViewModel() {
private val remoteRepository: SystemRemoteRepository by lazy {
SystemRemoteRepository()
}
val page = MutableLiveData<Pagination<Article>>()
fun getArticleList() {
viewModelScope.launch { //主线程开启一个协程
// 网络请求:IO线程
val tree: ApiResult<MutableList<Tree>> =
RetrofitClient.apiService.getTreeByCoroutines()
// 主线程
val cid = tree?.data?.get(0)?.id
if (cid != null) {
// 网络请求:IO线程
val pageResult: ApiResult<Pagination<Article>> =
RetrofitClient.apiService.getArticleListByCoroutines(0, cid)
// 主线程
page.value = pageResult.data!!
}
}
}
}
/**接口定义,Retrofit从2.6.0版本开始支持协程*/
interface ApiService {
/*获取文章树结构*/
@GET("tree/json")
suspend fun getTreeByCoroutines(): ApiResult<MutableList<Tree>>
/*根据数结构下某个分支id,获取分支下的文章*/
@GET("article/list/{page}/json")
suspend fun getArticleListByCoroutines(
@Path("page") page: Int,
@Query("cid") cid: Int
): ApiResult<Pagination<Article>>
}