制作简单的站点推广方案,台州网站建设系统,wordpress登陆地址修改,电商网站开发人员配置协程
用同步的方式写出异步的效果协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式挂起函数(suspend)不一定就是在子线程中执行的#xff0c;但是通常在定义挂起函数时都会为它指定其他线程#xff0c;这样挂起才有意义解决多层嵌套回调
协程不是线程…协程
用同步的方式写出异步的效果协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式挂起函数(suspend)不一定就是在子线程中执行的但是通常在定义挂起函数时都会为它指定其他线程这样挂起才有意义解决多层嵌套回调
协程不是线程是基于线程封装的库可以使用协程库提供的API方便的灵活的指定协程中代码执行的线程、切换线程但不需要接触线程Thread类。类似于Android的AsyncTask或者RxJava的Schedulers,都解决了异步线程切换的问题然而协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式把原本运行在不通线程的代码写在一个代码块{}里开起来就像是同步代码。
launch和async之间的区别
launch是一种适用于“发射并忘记”场景的协程构建器当你不需要等待协程的结果时它非常有用。它允许你在后台异步执行任务而不会阻塞主线程从而提高应用的响应性。
示例使用launch
fun main() {println(Before launch)// 启动一个协程GlobalScope.launch {delay(1000)println(Inside launch)}println(After launch)Thread.sleep(2000)
}
在上面的示例中我们使用launch关键字在GlobalScope中启动了一个协程。协程通过delay()函数暂停了1000毫秒然后打印了Inside launch。同时主线程继续执行。输出结果为Before launch After launch Inside launch
async是一种用于异步执行任务并获取其结果的协程构建器。它返回一个DeferredT对象表示将来可用的值
示例使用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()// 处理获取到的数据kotlinprocessData(data1, data2)
}
在这个示例中我们使用async来并发地从多个URL获取数据。每个网络请求被封装在一个单独的async块中它返回一个DeferredString对象。然后我们使用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 MutableLiveDataPaginationArticle()fun getArticleList() {viewModelScope.launch { //主线程开启一个协程// 网络请求IO线程val tree: ApiResultMutableListTree RetrofitClient.apiService.getTreeByCoroutines()// 主线程val cid tree?.data?.get(0)?.idif (cid ! null) {// 网络请求IO线程val pageResult: ApiResultPaginationArticle RetrofitClient.apiService.getArticleListByCoroutines(0, cid)// 主线程page.value pageResult.data!!}}}
}/**接口定义Retrofit从2.6.0版本开始支持协程*/
interface ApiService {/*获取文章树结构*/GET(tree/json)suspend fun getTreeByCoroutines(): ApiResultMutableListTree/*根据数结构下某个分支id获取分支下的文章*/GET(article/list/{page}/json)suspend fun getArticleListByCoroutines(Path(page) page: Int,Query(cid) cid: Int): ApiResultPaginationArticle
} 在定义接口时方法前加了个 suspend 关键字调用接口的时候用viewModelScope.launch {} 包裹。既然可以运行成功就说明请求接口并不是在主线程中进行的然而有的同学不信他在getArticleList() 方法中的任意位置通过 Thread.currentThread() 打印的结果都Thread[main,5,main]这不就是在主线程中调用的吗上述协程中的代码是在主线程执行没错但是接口请求的方法却是在子线程中执行的。 原因就在于我们定义接口的时候使用了suspend 关键字它的意思是挂起、暂停函数被加了这个标记就称它为挂起函数需要注意的是suspend 关键字并没有其他重要的作用它仅仅标识某个函数是挂起函数可以在函数中调用其他挂起函数但是只能在协程中调用它。所以上面两个接口都被定义为挂起函数了挂起函数只能在协程中调用那谁是协程 其实在 kotlin 协程库中是有一个类 AbstractCoroutine 来表示协程的这个抽象类有很多子类代表不同的协程但是这些子类都是private 的并没有暴露给我们所以你在其他文章中看到别人说 viewModelScope.launch{} 包裹起来的闭包 ( 代码块 ) 就是协程也没问题但这个代码块的真正意义是协程需要执行的代码。当在协程中调用到挂起函数时协程就会在当前线程主线程中被挂起这就是协程中著名的 非阻塞式挂起主线程暂时停止执行这个协程中剩余的代码注意暂停并不是阻塞等待否则会ANR 而是主线程暂时从这个协程中被释放出来去处理其他 Handler 消息比如响应用户操作、绘制View 等等。 那挂起函数谁执行这得看挂起函数内部是否有切换线程如果没有切换线程当然就是主线程执行了所以挂起函数不一定就是在子线程中执行的但是通常在定义挂起函数时都会为它指定其他线程这样挂起才有意义。比如上面定义的suspend 的请求接口 Retorift 在执行请求的时候就切换到了子线程并挂起主线程当请求完成挂起函数执行完毕返回结果时会通知主线程我该干的都干完了下面的事你接着干吧主线程接到通知后就会拿到挂起函数返回的结果继续执行协程里面剩余的代码这叫做协程恢复(resume) 。如果又遇到挂起函数就会重复这个过程直到协程中的代码被执行完。 通过协程可以彻底去除回调使用同步的方式编写异步代码。什么是同步调用调用一个方法能直接拿到方法的返回值尽管这个方法是耗时的、在其他线程执行的也能直接得到它的返回值然后再执行下面的代码协程不是通过等待的方式实现同步而是通过非阻塞挂起实现看起来同步的效果。