kotlin 协程的使用

254 阅读1分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

网络请求

现在比较流行的网络框架,就是retrofit,而且retrofit从2.6版本开始,实现了对协程的支持,其实可以理解为retrofit对suspend关键字的支持。 以前如果是使用retrofit来实现网络请求,一般都有这么几个步骤:

  1. 初始化retrofit
  2. 初始化Api代理接口
  3. 请求并在回调中处理结果
    private fun initRetrofit() {
        val okHttpClient = OkHttpClient.Builder().sslSocketFactory(
            TrustAllSSLSocketFactory.newInstance(),
            TrustAllSSLSocketFactory.TrustAllCertsManager()
        )
        retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .client(okHttpClient.build())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        api = retrofit.create(GitHubApi::class.java)
    }
interface GitHubApi {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user")user:String):Call<List<Repo>>
}
    private fun requestByNormal() {
        if (::retrofit.isInitialized && ::api.isInitialized) {
            api.listRepos("TonyDash")
                .enqueue(object : Callback<List<Repo>> {
                    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
                        textView.text = "requestByNormal onFailure"
                    }

                    override fun onResponse(
                        call: Call<List<Repo>>,
                        response: Response<List<Repo>>
                    ) {
                        textView.text = response.body()?.get(0)?.name
                    }

                })
        }
    }

这样就完成了一次网络请求了。那如果使用协程的话,需要怎么实现呢?其实,在上面的基础上稍作修改,就可以变成协程,初始化retrofit部分不需要改动,先改api接口的定义:

interface GitHubApi {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user")user:String):Call<List<Repo>>
    @GET("users/{user}/repos")
    suspend fun listReposKt(@Path("user")user:String):List<Repo>
}

listRepoKt就是支持协程的api,可以看到,区别就只有方法的前面多了一个suspend关键字,suspend的作用就是标记这个为挂起函数。最后来看请求部分:

    private fun requestByKt() {
        if (::retrofit.isInitialized && ::api.isInitialized) {
            GlobalScope.launch(Dispatchers.Main) {
                val repos = api.listReposKt("TonyDash")
                textView.text ="KT${repos[0].name}"
            }
        }
    }

这样就利用协程完成了一次网络请求了。可以明显看到,在请求数据的部分,少了回调,只需要肉眼看上去的按顺序写,就完成了异线程执行网络请求并主线程更新UI控件的工作,单个请求可能感觉差异不大,但是如果有一个需求你必须请求多个接口,并且多个接口还是有因果关系的,那就会有一种回调地狱的感觉,而且后期维护起来也相对麻烦,万一忘了以前是为什么这么写的呢?还有如果细心的话可以发现,利用回调处理结果的请求方法,有一个onFailure来处理请求的异常情况,那协程呢?协程怎么处理异常,下面我们单独讲。

异常处理

使用try catch来捕捉异常,由于kotlin取消了check exception机制,所以要捕捉异常,我们只能使用try catch来捕捉协程内的异常。

    private fun requestByKt() {
        if (::retrofit.isInitialized && ::api.isInitialized) {
            GlobalScope.launch(Dispatchers.Main) {
                try {
                    val repos = api.listReposKt("TonyDash")
                    textView.text = "KT${repos[0].name}"
                } catch (e: Exception) {
                    textView.text = e.message ?: "error"
                }
            }
        }
    }