浅谈安卓协程Android coroutine,及其与Retrofit的简单结合与使用

806 阅读3分钟

0. 前言

  1. 早期的安卓编程。资源的调度与释放从来都是一个不可避免的问题,开发者在onCreate()中调度资源,例如进行网络请求,进行磁盘IO操作,然后在onPause()或者onDestory()中手动释放或打断资源的使用。
  2. LifeCycle的引入。为了实现更简便的生命周期监听,谷歌推出了LifeCycle Support Library支持库(后被迁移到AndroidX中成为Jetpack的一部分),旨在让开发者能够更好地在资源调度时实现自动的生命周期感知
  3. 协程的引入。安卓中的协程依赖于Kotlin语言,编译产物仍是.class文件,最终仍是运行在类JVM虚拟机上,因此协程的实现还是基于线程,与其他的语言(例如Lua、Golang)所带的原生协程特性无法比拟,用通俗的话来说,安卓中的协程更像是用Kotlin语言实现的一套线程池+线程调度框架,而其非阻塞编程、挂起等概念简单的说就是将任务分配到了线程池中进行执行,并在合适的时候将任务结果送回到主线程上,从而达到不阻塞主线程的目的

1. 引入依赖项

	// 文件名:build.gradle(:app)
	// 协程
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
    // lifecycleScope
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'
    // Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

在gradle中添加以上依赖项并同步后,即可使用协程进行编程,此处为简化,去除了ViewModel概念,我们的业务代码将直接写在Activity/Fragment上

2. Retrofit的简单封装

object RetrofitClient {
	private val okHttpClient: OkHttpClient = OkHttpClient.Builder().build()
    
    var api: APIS = initApi()
    
    fun initApi(): APIS {
        return Retrofit.Builder()
                .baseUrl(https://xxxx.com/api/)
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build().create(APIS::class.java)
    }
    
	suspend fun <T> httpCall(block: suspend RetrofitClient.() -> ResultBean<T>): ResultBean<T> {
        var resultBean: ResultBean<T> = ResultBean()
        try {
            resultBean = block.invoke(this)
        } catch (e: Exception) {
            e.printStackTrace()
            // 出错时构造一个本地的结果返回
            resultBean = ResultBean<T>().apply {
                data = null
                code = "-1"
                message = if (e is ConnectException) "网络连接异常, 请检查您的网络" else "接口请求异常"
            }
        } finally {
            return resultBean
        }
    }
}

其中ResultBean为网络响应的格式

class ResultBean<T> {
    var code: String? = null
    var message: String? = null
    var data: T? = null
}

Retrofit API接口类

interface APIS {
	@POST("getUserInfo")
    suspend fun getUserInfo(@Body request: GetUserInfoRequest): ResultBean<GetUserInfoResult>
}

请求体

class GetUserInfoRequest {
    var token: String = ""
    var userId: String = ""
}

进一步的,将httpCall( )方法放在BaseActivity以及BaseFragment中

// BaseActivity.kt、BaseFragment.kt
 	suspend fun <T> httpCall(block: suspend RetrofitClient.() -> ResultBean<T>): ResultBean<T> {
        return RetrofitClient.httpCall(block)
    }

3. 在Activity中调用

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...
    val id = intent.getStringExtra("ID") ?: return
    // 获取当前协程域,并在当前Activity生命周期内执行,由于lifecycleScope会在onDesotry时自动停止其中的代码运行,因此无需手动进行请求的取消,资源释放等操作
    lifecycleScope.launchWhenCreated {
    	// 网络请求,获取个人信息
        val userInfo = httpCall {
            api.getUserInfo(GetUserInfoRequest().apply { this.userId = id })
        }
        // 协程处于激活态,且返回码正常时,显示用户名到textView上
        if (isActive && userInfo.code != "-1" ) {
            tvTitle.text = userInfo.data?.userName
        }
    }
}

4. 总结

可以看到,协程能够将网络调用从以前的“异步、回调”的形式写成“同步、上下顺序”的形式,从而消除回调嵌套;并且与LifeCycle进行了整合,我们可以不用关心网络请求异常或者页面提前关闭造成的空指针、内存泄漏等问题