Android 上的 Kotlin 协程
协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。
在 Android 上,协程有助于管理长时间运行的任务,如果管理不当,这些任务可能会阻塞主线程并导致应用无响应。使用协程的专业开发者中有超过 50% 的人反映使用协程提高了工作效率。本主题介绍如何使用 Kotlin 协程解决以下问题,从而让您能够编写出更清晰、更简洁的应用代码。
特点
协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括:
- 轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
- 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
- 内置取消支持:取消操作会自动在运行中的整个协程层次结构内传播。
- Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。
MVVM开发模式简述
View:Activity/Fragment
ViewModel:Jetpack ViewModel & Jetpack LiveData
Model:Repository仓库,包含本地持久性数据和服务端的数据
以下是Google Android官网对LiveData和ViewModel的定义
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
1、项目中使用到的库
// Kotlin
implementation "androidx.activity:activity-ktx:1.2.0"
implementation 'androidx.fragment:fragment-ktx:1.2.5'
//协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
2、创建retrofit和okhttpclient的静态类
object RetrofitClient {
private const val BASE_URL = "https://wanandroid.com/"
private var retrofit: Retrofit? = null
val service: HttpService by lazy {
getRetrofit().create(HttpService::class.java)
}
private fun getRetrofit(): Retrofit {
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(getOkHttpClent())
.addConverterFactory(GsonConverterFactory.create())
//.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
return retrofit!!
}
private fun getOkHttpClent(): OkHttpClient {
val builder = OkHttpClient().newBuilder()
val cacheFile = File(MyApplication.instance.cacheDir, "cache")
val cache = Cache(cacheFile, 1024 * 1024 * 50)// 50M 的缓存大小
builder.run {
cache(cache)
connectTimeout(60, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(60, TimeUnit.SECONDS)
retryOnConnectionFailure(true)//错误重连
}
return builder.build()
}
}
3、新建接收数据的基类
//接受数据的基类
data class ResponseData<out T>(
val errorCode: Int,
val errorMsg: String,
val data: T
)
//获取banner为例
data class Banner(
val desc: String,
val id: Int,
val imagePath: String,
val isVisible: Int,
val order: Int,
val title: String,
val type: Int,
val url: String
)
4、新建基类中间层BaseRepository,统一处理请求结果
open class BaseRepository {
suspend fun <T : Any> request(call: suspend () -> ResponseData<T>): ResponseData<T> {
return withContext(Dispatchers.IO) {
call.invoke()
}.apply {
LogUtil.e("接口返回数据---------->,${this}")
when (errorCode) {
0, 200 -> this
100, 401 -> ""
403 -> ""
404 -> ""
500 -> ""
else -> ""
}
}
}
}
5、新建ViewModel的基类BaseViewModel
open class BaseViewModel : ViewModel(), LifecycleObserver {
//运行在UI线程的协程
fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch {
try {
withTimeout(15 * 1000) {
block()
}
} catch (e: Exception) {
//此处接收到BaseRepository里的request抛出的异常
//根据业务逻辑自行处理代码...
}
}
}
6、新建BaseActivity,做一些简单的封装
abstract class BaseActivity<T : ViewBinding, VM : ViewModel> : AppCompatActivity() {
lateinit var binding: T
protected lateinit var viewModel: VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = getViewBinding()
providerVMClass()?.let {
viewModel = ViewModelProvider(this).get(it)
}
setContentView(binding.root)
initView()
setListener()
}
abstract fun providerVMClass(): Class<VM>?
protected abstract fun getViewBinding(): T
abstract fun initView()
abstract fun setListener()
}
7、以请求banner模块为例,撸一遍代码
interface HttpService {
@GET("banner/json")
suspend fun getBanner(): ResponseData<List<Banner>>
}
class BannerRepository : BaseRepository() {
suspend fun getBanner(): ResponseData<List<Banner>> = request {
RetrofitClient.service.getBanner()
}
}
class BannerViewModel : BaseViewModel() {
private val repository by lazy {
BannerRepository()
}
private val bannerData by lazy {
MutableLiveData<List<Banner>>()
}
fun getBanner(): LiveData<List<Banner>> {
launchUI {
val result = repository.getBanner()
bannerData.value = result.data
}
return bannerData
}
}
class BannerActivity : BaseActivity<ActivityBannerBinding, BannerViewModel>() {
override fun getViewBinding() = ActivityBannerBinding.inflate(layoutInflater)
override fun initView() {
viewModel.getBanner().observe(this, { it ->
it.forEach {
LogUtil.e(it.imagePath)
}
})
}
override fun setListener() {
}
override fun providerVMClass() = BannerViewModel::class.java
}
8、一个retrofit配合kotlin 协程的MVVM模式的网络请求框架搭建好了