Android Kotlin Coroutines + Retrofit + MVVM 简单实现

前言

  • 这是一篇随记,想尝试下写文章。
  • 这里基本不会深刻讨论各个知识点,如需了解更多能够参考 Benny's BlogKotlin官方文档

为何使用协程

  • 协程比线程小

我: 为何已经有Rx了还要在这里用协程?
某同事: 由于协程比线程小,能够开不少个。
我:...... 为何比线程小?html

  我维基了一下,确实有说比线程更小。
  可是看了一些源码,也是线程池 + 线程实现的,这时就开始有了疑惑,为何一样是线程,怎么就说是比线程小的东西呢?
  直到看到了Benny大佬的文章 协程为何被称为『轻量级线程』解释,我清晰了。经过测验,确实启动成千上万个协程也不会出现OOM或者其余问题。android

  • 固然最主要的仍是代码上的体验 如今Kotlin愈来愈广泛,各类inline函数,操做符也都基本能够替换Rx的经常使用操做符了。因此在写代码上体验仍是相对比较好的。
    (备注:我的以为协程小不小对于Android开发真的没多大区别,最主要仍是写代码和代码美观性)

1、添加依赖

implementation"com.squareup.retrofit2:retrofit:2.6.2"
implementation"com.squareup.retrofit2:converter-gson:2.6.2"

// Coroutines 
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
复制代码

记得使用retrofit 2.6.0 或以上git

2、准备网络请求接口

  • 建立接口
interface GitApi {
    @GET("/users/{username}/{module}")
    suspend fun repos( @Path("username") username: String, @Path("module") module: String, @Query("page") currPage: Int ): List<RepoInfo>

    @GET("/search/repositories")
    suspend fun searchRepos( @Query("q") key: String, @Query("sort") sort: String? = "updated", @Query("order") order: String? = "desc", @Query("page") currPage: Int ): SearchResponse
}
复制代码
  • 建立Retrofit实例
fun buildRetrofit(): Retrofit {
        builder.addInterceptor(HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
            override fun log(message: String) {
                Timber.d(message)
            }
        }).apply {
            level = HttpLoggingInterceptor.Level.BODY
        }).addInterceptor(object : Interceptor {
            override fun intercept(chain: Interceptor.Chain): Response {
                val userCredentials = "$username:$password"
                val basicAuth =
                    "Basic ${String(Base64.encode(userCredentials.toByteArray(), Base64.DEFAULT))}"
                val original = chain.request()
                val requestBuilder = original.newBuilder()
                    .header("Authorization", basicAuth.trim { it <= ' ' })
                val request = requestBuilder.build()
                return chain.proceed(request)
            }
        })
        return Retrofit.Builder()
            .client(builder.build())
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
复制代码

3、 在ViewModel中使用

kotlinx.coroutines.CoroutineScope 接口中的注释有这么一句话
github

image.png
因此启动一个协程必定须要一个 Scope,也就是说每一个suspend函数都有一个 CoroutineScope,若是没有必定会报错。而Android的ViewModel中有扩展了一个 viewModelScope,而且跟 lifecycle绑定了,因此在ViewModel的onCleared方法上会自动帮咱们cancel掉这个viewModelScope的全部Jobs。
图1
为了方便使用,封装了一个BaseViewModel

open class BaseViewModel : ViewModel() {
    fun <T> request( onError: (error: Throwable) -> Unit = {}, // 不须要处理Error能够不传
        execute: suspend CoroutineScope.() -> T
    ) {
        viewModelScope.launch(errorHandler { onError.invoke(it) }) {
            launch(Dispatchers.IO) {
                execute()
            }
        }
    }

    private fun errorHandler(onError: (error: Throwable) -> Unit): CoroutineExceptionHandler {
        return CoroutineExceptionHandler { _, throwable ->
            Timber.d(throwable)
            onError.invoke(throwable)
        }
    }
}
复制代码

使用继承 BaseViewModel 调用 request 方法便可。网络

class RepoViewModel(
    private val userRepo: UserDataSource,
    private val gitApi: GitApi
) : BaseViewModel() {
    private val _reposResult = BaseLiveData<List<RepoInfo>>()
    val repoResult: BaseLiveData<List<RepoInfo>>
        get() = _reposResult

    fun fetchRepos(module: String) {
        request {
            userRepo.currUser()?.let {
                val result = gitApi.repos(it.nickname, module, 1)
                _reposResult.update(result)
            }
        }
    }
}
复制代码

ORapp

fun fetchRepos(module: String) {
        request(
            onError = {
                // handle error
            },
            execute = {
                userRepo.currUser()?.let {
                    val result = gitApi.repos(it.nickname, module, 1)
                    _reposResult.update(result)
                }
            }
        )
    }
复制代码

剩下的基本是LiveData和Fragment之间的订阅上的逻辑实现了。ide

结语

最近在学习,了解也不是很深,欢迎评论补充和提建议。 学习项目地址 Dithub函数

相关文章
相关标签/搜索