协程的魅力你感受到了吗?-传统异步任务和协程的使用对比

示例1 登录并返回用户信息

传统异步方式

使用Retrofit+Handler

1、引入Retrofit依赖

// Retrofit库
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:retrofit-mock:2.7.2"
implementation "com.squareup.retrofit2:converter-gson:2.7.2"
implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
implementation "com.squareup.retrofit2:converter-scalars:2.7.2"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.7.2"

2、定义接口

interface WanAndroidApi {
    @POST("/user/login")
    @FormUrlEncoded
    fun loginAction(@Field("username") username: String, @Field("password") password: String):
            Call>
}

3、相应数据实体

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?
)

4、响应数据包装类

data class ResponseWrapper(val data: T, val errorCode: Int, val errorMsg: String)

5、数据请求Client

class ApiClient {

    private object Holder {
        val INSTANCE = ApiClient()
    }

    companion object {
        val instance = Holder.INSTANCE
    }

    fun  instanceRetrofit(apiInterface: Class): T {
        //OkHttpClient请求服务器
        val mOkhttpClient = OkHttpClient().newBuilder().apply {
            readTimeout(10000, TimeUnit.SECONDS)//读取超时时间
            connectTimeout(10000, TimeUnit.SECONDS)//连接超时时间
            writeTimeout(10000, TimeUnit.SECONDS)//写入超时时间
        }.build()

        val retrofit: Retrofit = Retrofit.Builder().baseUrl("https://www.wanandroid.com")
            //请求方
            .client(mOkhttpClient)
            //响应方
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RxJava处理
            .addConverterFactory(GsonConverterFactory.create())//Gson解析
            .build()

        return retrofit.create(apiInterface)
    }
}

6、view层请开启子线程发起请求,然后通过Handler切换到主线程更新UI

//第二步:用Handler接收请求结果并更新UI
private val mHandler = Handler(Looper.getMainLooper()) {
    val result = it.obj as ResponseWrapper<*>
    tv_response.text = result.data.toString()
    
    mProgressDialog?.dismiss()
    
    false
}

//第一步:开启异步线程,请求服务器
thread {
    val loginResult = ApiClient.instance.instanceRetrofit(WanAndroidApi::class.java)
        .loginAction("Derry-vip", "123456")
        
    val result = loginResult.execute().body()
    
    //切换到主线程更新UI,把请求结果发送给Handler
    val msg = mHandler.obtainMessage()
    msg.obj = result
    mHandler.sendMessage(msg)
}

携程方式

使用Retrofit+协程,数据实体、包装类,请求Client同上。

1、定义接口

interface WanAndroidApi {
    @POST("/user/login")
    @FormUrlEncoded
    suspend fun loginActionCoroutine(
        @Field("username") username: String,
        @Field("password") password: String
    ): ResponseWrapper
}

使用suspend关键字修饰,起到一个提示的作用,表示该函数会使用协程挂起,有一个代码颜色规则。用suspend修饰的方法中需要使用协程,比如withContext(),否则suspend关键字没有意义,并且会置灰。而一旦使用了协程,该方法就必须用suspend来修饰。

2、使用协程发起请求

//使用协程登录 同步代码实现异步效果
//GlobalScope.launch()默认为IO线程,需要用GlobalScope.launch(Dispatchers.Main)切换到主线程
GlobalScope.launch(Dispatchers.Main) {
    //UI线程
    val result = ApiClient.instance.instanceRetrofit(WanAndroidApi::class.java)
        .loginActionCoroutine("Derry-vip", "123456")//1、挂起出去执行异步操作 2、操作完成后恢复主线程
        
    //应为{}内为主线程,所以可以直接更新UI
    tv_response.text = result.data.toString()
    
    mProgressDialog?.dismiss()
}

注意: GlobalScope.launch()默认为异步(IO)线程,需要用GlobalScope.launch(Dispatchers.Main)切换到主线程,并且实际开发中很少直接用GlobalScope,因为它是作用于全局的,只有在进程被kill的时候才会销毁。实际开发可以使用viewModelScope等。

对比

1、使用传统的异步请求,代码量比较大,很繁琐,使用协程代码非常简洁。

2、使用传统的异步请,请求完数据再反过来发送给Handler去处理代码的逻辑无法按照人类正常的思维认知的顺序去写;而使用协程,用同步代码实现效果,所有逻辑都是从上到下很流畅,符合人类的认知顺序。

示例2 模拟按顺序调用3个接口并返回信息

传统异步方式

1、定义接口回调,回调请求结果

/**
 * 模拟请求结果回调
 */
interface ResponseCallBack {

    /**
     * 服务器请求成功
     * @param successInfo 成功回调信息
     */
    fun onSuccess(successInfo: String)

    /**
     * 服务器请求失败
     * @param errorInfo 失败回调信息
     */
    fun onError(errorInfo: String)

}

2、定义3个接口的请求方法

//第一步 请求用户数据
private fun requestUserInfo(responseCallBack: ResponseCallBack) {
    val requestSuccess = true

    //开启异步线程,加载服务器数据
    thread {
        Thread.sleep(2000)

        if (requestSuccess) {
            responseCallBack.onSuccess("请求用户数据 成功")
        } else {
            responseCallBack.onError("请求用户数据 失败")
        }

    }
}

//第二步 请求课程数据
private fun requestLessonInfo(responseCallBack: ResponseCallBack) {
    val requestSuccess = true

    thread {
        Thread.sleep(2000)

        if (requestSuccess) {
            responseCallBack.onSuccess("请求课程信息 成功")
        } else {
            responseCallBack.onError("请求课程数据 失败")
        }
    }
}

//第二步 请求课程详情数据
private fun requestLessonDetailInfo(responseCallBack: ResponseCallBack) {
    val requestSuccess = true

    thread {
        Thread.sleep(2000)

        if (requestSuccess) {
            responseCallBack.onSuccess("请求课程详情信息 成功")
        } else {
            responseCallBack.onError("请求课程详情数据 失败")
        }
    }
}

3、按顺序发起请求,并回调结果更新UI

private fun requestData() {
    mProgressDialog = ProgressDialog(this)
    mProgressDialog?.setTitle("loading...")
    mProgressDialog?.show()
    
    //请求用户信息
    requestUserInfo(object : ResponseCallBack {
        override fun onSuccess(successInfo: String) {
            //用户信息请求成功
            val handler = object : Handler(Looper.getMainLooper()) {
                
                override fun handleMessage(msg: Message) {
                    super.handleMessage(msg)
                    
                    //更新UI
                    tv_response.text = successInfo
                    tv_response.setTextColor(Color.RED)
                    
                    //请求课程信息
                    requestLessonInfo(object : ResponseCallBack {
                        override fun onSuccess(successInfo: String) {
                            //课程信息请求成功
                            val handler = object : Handler(Looper.getMainLooper()) {
                                override fun handleMessage(msg: Message) {
                                    super.handleMessage(msg)
                                    
                                    //更新UI
                                    tv_response.text = successInfo
                                    tv_response.setTextColor(Color.BLUE)
                                    
                                    //请求课程详情信息
                                    requestLessonDetailInfo(object : ResponseCallBack {
                                        override fun onSuccess(successInfo: String) {
                                            //课程详情信息请求成功
                                            val handler =
                                            object : Handler(Looper.getMainLooper()) {
                                                override fun handleMessage(msg: Message) {
                                                    super.handleMessage(msg)
                                                    
                                                    //更新UI
                                                    tv_response.text = successInfo
                                                    tv_response.setTextColor(Color.GREEN)
                                                    
                                                    mProgressDialog?.dismiss()
                                                    
                                                }
                                            }
                                            handler.sendEmptyMessage(0)
                                        }
                                        
                                        override fun onError(errorInfo: String) {
                                            
                                        }
                                    })
                                }
                            }
                            handler.sendEmptyMessage(0)
                        }
                        
                        override fun onError(errorInfo: String) {
                            
                        }
                    })
                }
                
            }
            handler.sendEmptyMessage(0)
        }
        
        override fun onError(errorInfo: String) {
            
        }
    })
    }

携程方式

1、定义3个接口的请求方法

//第一步 请求用户数据
//suspend 就是一个提醒作用,提醒用户 当前的函数,是挂起函数,可能会执行异常操作
private suspend fun requestUserInfo(): String {
    val requestSuccess = true

    withContext(Dispatchers.IO) {
        delay(2000L)
    }

    return if (requestSuccess) "请求用户数据 成功" else "请求用户数据 失败"
}

//第二步 请求课程数据
private suspend fun requestLessonInfo(): String {
    val requestSuccess = true

    withContext(Dispatchers.IO) {
        delay(2000L)
    }

    return if (requestSuccess) "请求课程信息 成功" else "请求课程数据 失败"
}

//第二步 请求课程详情数据
private suspend fun requestLessonDetailInfo(): String {
    val requestSuccess = true

    withContext(Dispatchers.IO) {
        delay(2000L)
    }

    return if (requestSuccess) "请求课程详情信息 成功" else "请求课程详情数据 失败"
}

2、使用协程发起请求,并更新UI

private fun requestData() {
    val progressDialog = ProgressDialog(this)
    progressDialog.setTitle("loading...")
    progressDialog.show()
    
    //一般不用GlobalScope GlobalScope是全局的作用于,只有进程被kill的时候才会销毁
    GlobalScope.launch(Dispatchers.Main) {
        
        val result1 = requestUserInfo()
        tv_response.text = result1
        tv_response.setTextColor(Color.RED)
        
        val result2 = requestLessonInfo()
        tv_response.text = result2
        tv_response.setTextColor(Color.BLUE)
        
        val result3 = requestLessonDetailInfo()
        tv_response.text = result3
        tv_response.setTextColor(Color.GREEN)
        
        progressDialog.dismiss()
    }
}

对比

传统方式代码量很大,还需要进行大量的嵌套,代码阅读困难;携程方式代码极度简洁,很容易阅读。

关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新技术干货。

你可能感兴趣的:(协程的魅力你感受到了吗?-传统异步任务和协程的使用对比)