0. 前言
- 早期的安卓编程。资源的调度与释放从来都是一个不可避免的问题,开发者在onCreate()中调度资源,例如进行网络请求,进行磁盘IO操作,然后在onPause()或者onDestory()中手动释放或打断资源的使用。
- LifeCycle的引入。为了实现更简便的生命周期监听,谷歌推出了LifeCycle Support Library支持库(后被迁移到AndroidX中成为Jetpack的一部分),旨在让开发者能够更好地在资源调度时实现自动的生命周期感知
- 协程的引入。安卓中的协程依赖于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进行了整合,我们可以不用关心网络请求异常或者页面提前关闭造成的空指针、内存泄漏等问题