kotlin 协程 lanch 详解
前言
使用纯代码 加 注释的方式,可以更快的理解源码
如果你喜欢,请点个赞,后期会不断的深入讲解
一、协程是什么?
1.大部分开发者们把协程比喻成:线程的封装框架,从宏观角度看,这有一定道理
2.协程有点像轻量级的线程
3.从包含关系上看,协程跟线程的关系,有点像“线程与进程的关系”,毕竟,协程不可能脱离线程运行
4.协程虽然不能脱离线程而运行,但可以在不同的线程之间切换
二、传统方式完成异步网络加载和协程的对比
2.1:传统方式完成异步任务网络加载
接下来通过一个接口的模拟登录,来实现一下传统方式的步任务网络加载登录场景
代码如下:LoginRegisterResponseWrapper
data class LoginRegisterResponseWrapper<T>(val data: T, val errorCode: Int, val errorMsg: String)
代码如下:LoginRegisterResponse
data class LoginRegisterResponse( val admin: Boolean, val chapterTops: List<*>, val collectIds: List<*>, val email: String?, val icon: String?, val id: String?, val nickname: String?, val password: String?, val publicName: String?, val token: String?, val type: Int, val username: String? )
定义一个接口 代码如下:WanAndroidAPI
interface WanAndroidAPI {
// TODO 下面是传统方式API /** https://www.wanandroid.com/blog/show/2 * 登录API * username=Derry-vip&password= */ @POST("/user/login") @FormUrlEncoded fun loginAction( @Field("username") username: String, @Field("password") password: String ) : Call<LoginRegisterResponseWrapper<LoginRegisterResponse>> // 返回值 }
接口的实现 代码如下:APIClient
class APIClient {
/** * 单例 */ private object Holder {
val INSTANCE = APIClient() } companion object {
// 派生 val instance = Holder.INSTANCE } fun <T> instanceRetrofit(apiInstance: Class<T>): T {
// OkHttpClient 请求服务器 val mOkHttpClient = OkHttpClient().newBuilder().apply {
readTimeout(10000, TimeUnit.SECONDS) //添加超时时间 connectTimeout(10000, TimeUnit.SECONDS) //添加连接超时时间 writeTimeout(10000, TimeUnit.SECONDS) // 添加写入超时时间 }.build() val retrofit = Retrofit.Builder() .baseUrl("https://www.wanandroid.com") // 请求方 ← .client(mOkHttpClient) // 响应方 → // Response的事情 回来 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // RxJava来处理 .addConverterFactory(GsonConverterFactory.create()) // Gson 来解析 --- JavaBean .build() return retrofit.create(apiInstance) } }
XML 代码如下
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity"> <TextView android:id="@+id/textview" android:text="启动线程" android:layout_width="match_parent" android:layout_height="wrap_content" tools:ignore="MissingConstraints" /> <Button android:id="@+id/button" android:onClick="startRequest" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="网络请求" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="OnClick" /> </androidx.constraintlayout.widget.ConstraintLayout>
具体功能实现,直接看代码吧,注释比较多
代码如下: MainActivity
class MainActivity : AppCompatActivity() {
private var mProgressDialog: ProgressDialog? = null override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun startRequest(view: View){
mProgressDialog = ProgressDialog(this) mProgressDialog?.setTitle("请求服务器中...") mProgressDialog?.show() // TODO 第一步:异步线程开启,请求服务器 object: Thread(){
override fun run() {
super.run() Thread.sleep(2000) // 模拟一个两秒的加载时间 val loginResult = APIClient.instance.instanceRetrofit(WanAndroidAPI::class.java) .loginAction("Derry-vip", "") val result: LoginRegisterResponseWrapper<LoginRegisterResponse>? = loginResult.execute().body() // 切换到主线程, 更新UI 把最终的javaBean 发送给Handler val msg = mHandler.obtainMessage() msg.obj = result mHandler.sendMessage(msg) } }.start() } // TODO 第二步:主线程更新UI val mHandler = Handler(Looper.getMainLooper()){
// as 类型转换 val result = it.obj as LoginRegisterResponseWrapper<LoginRegisterResponse> textview.text = result.data.toString() // 更新控件 UI mProgressDialog?.hide() false } }
上面的代码实现,是传统完成的异步加载操作。需要先开启一个异步线程,然后再把登录成功的数据,再交给主线线程去做UI的更新操作。
2.2:协程方式完成异步任务网络加载
再来实现一个协程的
修改 WanAndroidAPI 里面的接口,添加 suspend 关键字,代码如下:
@POST("/user/login") @FormUrlEncoded suspend fun loginActionCoroutine( @Field("username") username: String, @Field("password") password: String ) : LoginRegisterResponseWrapper<LoginRegisterResponse> // 返回值
修改 MainActivity 中的代码,代码如下:
class MainActivity1 : AppCompatActivity() {
private var mProgressDialog: ProgressDialog? = null private val main = MainScope() override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun startRequest(view: View) {
mProgressDialog = ProgressDialog(this) mProgressDialog?.setTitle("请求服务器中...") mProgressDialog?.show() // launch 在Android 中使用的时候,它默认的是 IO 线程,所以需要修改成Main 线程 (Dispatchers.Main ) main.launch(Dispatchers.Main) {
// 1.挂起出去执行异步线程 2. 操作完成之后,恢复主线程 val result = APIClient.instance.instanceRetrofit(WanAndroidAPI::class.java) .loginActionCoroutine("Derry-vip", "") // 更新UI textview.text = result.data.toString() mProgressDialog?.hide() } } override fun onDestroy() {
super.onDestroy() main.cancel() } }
是不是简单了很多呢?不再需要创建异步线程,然后再交给主线程去修改UI,而是直接使用 launch 挂起出去执行异步操作,操作完成后直接恢复到主线程
这只是一个简单的一层回调的例子,可以想象一些,如果老板让我们实现一个三层回调,30层回调的时候,我们使用传统的处理方式会有多疼苦,接下来我们来实现一下。
2.3:传统方式完成三层回调
先写个回调接口 代码如下 ResponseCallback
/** * 模拟请求服务器后,相应结果信息 */ interface ResponseCallback {
/** * 请求服务器 加载成功 */ fun responseSuccess(serverResponseInfo: String) /** * 请求服务器 加载失败 */ fun responseError(serverResponseErrorMsg: String) }
再写三个模拟数据请求的异步线程 代码如下:
/** * 第一层 * 请求加载 [用户数据] * * responseCallback [回调给外界的接口] */ private fun requestLoadUser(responseCallback: ResponseCallback) {
val isLoadSuccess = true // 加载成功 或 失败的标记 object : Thread() {
override fun run() {
super.run() try {
sleep(3000L) // 模拟请求 所造成的耗时 if (isLoadSuccess) {
responseCallback.responseSuccess("加载到[用户数据]信息集") } else {
responseCallback.responseSuccess("加载[用户数据],加载失败,服务器宕机了") } } catch (e: InstantiationException) {
e.printStackTrace() } } }.start() } /** * 第二层 * 请求加载 [用户资产数据] * * responseCallback [回调给外界的接口] */ private fun requestLoadUserAssets(responseCallback: ResponseCallback) {
val isLoadSuccess = true // 加载成功 或 失败的标记 object : Thread() {
override fun run() {
super.run() try {
sleep(3000L) if (isLoadSuccess) {
responseCallback.responseSuccess("加载到[用户资产数据]信息集") } else {
responseCallback.responseError("加载[用户资产数据],加载失败,服务器宕机了") } } catch (e: InstantiationException) {
e.printStackTrace() } } }.start() } /** * 第三层 * 请求加载 [用户资产详情数据] * * responseCallback [回调给外界的接口] */ private fun requestLoadUserAssetsDetails(responseCallback: ResponseCallback) {
val isLoadSuccess = true // 加载成功 或 失败的标记 object : Thread() {
override fun run() {
super.run() try {
sleep(3000L) if (isLoadSuccess) {
responseCallback.responseSuccess("加载到[用户资产数据]信息集") } else {
responseCallback.responseError("加载[用户资产数据],加载失败,服务器宕机了") } } catch (e: InstantiationException) {
e.printStackTrace() } } }.start() }
具体功能实现,直接看代码吧,没什么好讲的
代码如下: MainActivity
class MainActivity2 : AppCompatActivity() {
private var mProgressDialog: ProgressDialog? = null private val main = MainScope() override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun startRequest(view: View) {
mProgressDialog = ProgressDialog(this) mProgressDialog?.setTitle("请求服务器中...") mProgressDialog?.show() // TODO 先执行 异步请求 1 requestLoadUser(object : ResponseCallback {
override fun responseSuccess(serverResponseInfo: String) {
// 从异步 切换到主线程,更新UI val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg) textview.text = serverResponseInfo textview.setTextColor(Color.GRAY) // TODO 先执行 异步请求 2 requestLoadUserAssets(object : ResponseCallback {
override fun responseSuccess(serverResponseInfo: String) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg) textview.text = serverResponseInfo textview.setTextColor(Color.RED) // TODO 先执行 异步请求 3 requestLoadUserAssetsDetails(object : ResponseCallback {
override fun responseSuccess(serverResponseInfo: String) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg) textview.text = serverResponseInfo textview.setTextColor(Color.BLUE) mProgressDialog?.hide() } } handler.sendEmptyMessage(0) } override fun responseError(serverResponseErrorMsg: String) {
// TODO 失败的的逻辑就不写了 } }) } } handler.sendEmptyMessage(0) } override fun responseError(serverResponseErrorMsg: String) {
// TODO 失败的的逻辑就不写了 } }) } } handler.sendEmptyMessage(0) } override fun responseError(serverResponseErrorMsg: String) {
// TODO 失败的的逻辑就不写了 } }) } }
无限嵌套,让我想起了flutter 中的嵌套陷阱。
再看一下 使用协程 实现的三层回调
2.4:协程方式解决三层回调带
先定义三个方法,分别开启一个异步线程。 代码如下:
/** * 请求加载[用户数据] * * suspend 就是一个提醒的作用,提醒用户,当前函数是挂起的函数,可能执行异常操作 */ private suspend fun requestLoadUser(): String{
val isLoadSuccess = true // 加载成功,和,加载失败,的标记 // 此协程能够保证在异步执行 withContext(Dispatchers.IO){
delay(3000) //模拟请求服务器,造成的耗时 } if (isLoadSuccess){
return "加载到[用户数据]信息集" }else{
return "加载[用户数据],加载失败,服务器宕机了" } } /** * 请求加载[用户资产数据] */ private suspend fun requestLoadUserAssets(): String{
val isLoadSuccess = true // 加载成功,和,加载失败,的标记 // 此协程能够保证在异步执行 withContext(Dispatchers.IO){
delay(3000) //模拟请求服务器,造成的耗时 } if (isLoadSuccess){
return "加载到[用户资产数据]信息集" }else{
return "加载[用户资产数据],加载失败,服务器宕机了" } } /** * 请求加载[用户资产详情数据] */ private suspend fun requestLoadUserAssetsDetails(): String{
val isLoadSuccess = true // 加载成功,和,加载失败,的标记 // 此协程能够保证在异步执行 withContext(Dispatchers.IO){
delay(3000) //模拟请求服务器,造成的耗时 } if (isLoadSuccess){
return "加载到[用户资产详情数据]信息集" }else{
return "加载[用户资产详情数据],加载失败,服务器宕机了" } }
MainActivity 代码具体实现如下:
class MainActivity : AppCompatActivity() {
private var mProgressDialog: ProgressDialog? = null override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun startRequest(view: View) {
mProgressDialog = ProgressDialog(this) mProgressDialog?.setTitle("请求服务器中...") mProgressDialog?.show() // Dispatchers.Main 包裹一层 Handler 切换主线程 GlobalScope.launch(Dispatchers.Main) {
// TODO 先执行 异步请求1 var serverResponseInfo = requestLoadUser() textview.text = serverResponseInfo // 更新UI textview.setTextColor(Color.YELLOW) // 更新UI // TODO 更新UI完成后 异步请求2 serverResponseInfo = requestLoadUserAssets() textview.text = serverResponseInfo // 更新UI textview.setTextColor(Color.RED) // 更新UI // TODO 更新UI完成后 异步请求3 serverResponseInfo = requestLoadUserAssetsDetails() textview.text = serverResponseInfo // 更新UI textview.setTextColor(Color.BLUE) // 更新UI mProgressDialog?.hide() } } }
看到这里,是不是发现很简单呢?
总结
难道协程很 ”高效“,”轻量“我们就要用协程吗?
答:其实协程的真正魅力是,最大程度简化异步并发任务,用同步代码写出异步效果
🤩
🎉 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
🌟 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!
今天的文章
kotlin 协程 retrofit_kotlin和java哪个好分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/81139.html