Android协程——Retrofit&Coroutines

最近在新开的项目中,全面使用了Android的全新姿势——Kotlin、Jetpack等等,之后也对协程进行了一段时间的学习,体验了协程带来的编程快乐,我也忍不住对RxJava下了狠手。新项目中,我完全移除了RxJava,彻底的转入了协程时代。上周也是对原本的网络层进行了一次全新的封装,在这里对此进行一次总结。

和Google官宣的一样,Kotlin作为官方语言将会在新特性上得到优先支持,Retrofit2.6.0发布更新后,内置了对Kotlin Coroutines的支持,极大的简化了使用Retrofit和协程进行网络请求的过程。在这里我就不过多的去介绍协程的概念了,我们试着去使用Retrofit+协程来完成一次网络请求封装吧。

本篇基于Retrofit2.6.0进行,与之前版本的请求方式略有差别,请注意。

Demo地址:https://github.com/jotyy/coroutines-retrofit-example 欢迎交流和star,谢谢

一、创建Retrofit

第一步,我们当然是要创建Retrofit,这里先封装一个BaseRetrofitClient.

abstract class BaseRetrofitClient {
    companion object {
        private const val TIME_OUT = 6
    }
    
    private val client: OkHttpClient
        get() {
            val builder = OkHttpClient.Builder()
            val logging = HttpLoggingInterceptor()
            if (BuildConfig.DEBUG) {
                logging.level = HttpLoggingInterceptor.Level.BODY
            } else {
                logging.level = HttpLoggingInterceptor.Level.BASIC
            }

            builder.addInterceptor(logging)
            .connectTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)

            handleBuilder(builder)
            return builder.build()
        }
    
    protected abstract fun handleBuilder(builder: OkHttpClient.Builder)
    
    fun <S> getService(clazz: Class<S>, baseUrl: String): S {
        return Retrofit.Builder()
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(baseUrl)
                .build().create(clazz)
    }
    
}

之后我们可以针对自己的项目需要去重写handleBuilder方法,来创建我们需要的retrofit.

object MyRetrofitClient : BaseRetrofitClient() {
    val service by lazy {
        getService(MyApiService::class.java, MyApiService.BASE_URL)
    }
    
    override fun handleBuilder(builder: OkHttpClient.Builder){
        ...
    }
}

二、创建Service接口

interface MyApiService {
    @Post("/users")
    suspend fun getUsers(): MyResponse<User>
}

三、返回数据类封装

假定现在服务器返回接口数据的格式为:

{
    success: true
    returnCode: 10000,
    message: "请求成功",
    returnTime: 15000901291001,
    data: {
        ...
    }
}

其中data是我们想要获取的实体类数据,这时我们需要对外层的json进行一次封装,并根据returnCode处理不同的数据请求情况。

data class MyResponse<T> (
    val success: Boolean,
    val returnCode: Int,
    val message: String,
    val returnTime: Long,
    val data: T
)

四、统一处理请求

依照我们公司的内部约定,returnCode为10000时是正常的成功请求,如果请求是其它值则分别有对应的处理,例如:80000代表token失效。那么我们就想到,要对不同的情况进行统一处理,以此来避免在每次网络请求的时候对returnCode进行重复判断。

4.1 BaseRepository

open class BaseRepository {
    
    suspend fun <T: Any> apiCall(call: suspend() -> MyResponse<T>) : MyRespnse<T> {
        return withContext(IO) { call.invoke() }.apply {
            // 特殊处理
            when (returnCode) {
                99999 -> throw HttpException()
                80000 -> throw TokenInvalidException()
            }
        }
    }
    
    class TokenInvalidException(msg : String = "token invalid"): Exception(msg)
}

4.2 BaseViewModel

open class BaseViewModel : ViewModel() {
    val error by lazy { MutableLiveData<Exception>() }
    
    // UI
    fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.lauch {
        try {
            block()
        } catch (e: Exception){
            error.value = e
        }
    }

}

五、完成一次请求

做完以上的几件事之后,我们可以开始做一次简单的网络请求啦。

class UserRepository : BaseRepository() {
    suspend fun getUser(): MyResponse<User> {
        return apiCall(MyRetrofitClient.service.getUser())
    }
}

直接在UserViewModel中进行。

class UserViewModel : BaseViewModel() {
    val user by lazy { MutableLiveData<User>() }
    
    val repository = UserRepository()
    
    fun getUser(){
        launchUI{
            val result = repository.getUser()
            user.value = result
        }
    }
}

以上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容