上篇文章讲了MVVM入门,网络请求部分非常简单和原始,本篇则是上一篇的进阶,主要讲解如何在vm中使用协程结合Retrofit进行网络框架的封装。

GitHub完整版:https://github.com/baiyuliang/MVVM

Retrofit自不必说,非常优秀的网络请求框架,说到Retrofit就不得不提RxJava,

RxJava是什么?

官方定义:一个在jvm上使用可观测的序列来组成异步的,基于事件的程序的库,它具有良好的链式编程风格,以及强大的异步处理能力,在近几年的移动开发中异常火爆,常结合Retrofit,以及MVP设计模式组成移动开发架构,搜索一下就会有大量相关文章呈现在你的眼前,不可否认,它是那么的优秀!Rxjava最重要的思想,就是观察者模式,我敢打赌,到现在还会有很多同学仍然没有理解观察者,订阅者这些到底是什么玩意,即便你已经使用RxJava几年了。我在这里只简单介绍下,你就会很好的理解了:

首先,定义接口(一个简单的例子):
ApiService.java

   @GET("test")Observable<HttpResult> test(@QueryMap HashMap<String, String> options);

我们可以看到这个方法的返回值Observable,我们可以将其简单的理解为被观察者,这个被观察者 就是接口请求方法,观察者需要观察的就是你这个方法请求的结果的变动。

当我们用Presenter去请求接口时,会有一个中间过程,一般用来传参,大致类似于:

TestNetUtil.java

    public Observable<HttpResult> test(String id) {LinkedHashMap<String, String> params = new LinkedHashMap<>();params.put("id", id);return ApiUtil.getInstance().getApiService().test(params);}

注意看返回值,仍然是Observable,继续往下走就是在Presenter中调用了,大致类似于这样:
TestPresenter

    public void test(String id) {TestNetUtil.newInstance().test(id).subscribe(new Observer<HttpResult>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(HttpResult httpResult) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});}

这样就完成了一个接口请求,注意上面的方法,TestNetUtil.newInstance().test(id)我们知道,其返回值是Observable,我们说过这是一个被观察者,就是接口请求本身,subscribe订阅,该方法传入了一个Observer对象,Observer就是观察者,这个观察者订阅了(subscribe)被观察者(TestNetUtil.newInstance().test(id)),被观察者一旦接口请求结果完成,就会通知被观察者去处理结果,因为我订阅了你,你有任何变动我都会知道,这就是RxJava的观察者模式,而ViewModel中也是一样的道理!

好了,继续,本篇博客将不会去讲Rxjava与协程孰优孰劣,只讲如何使用协程,像RxJava一样,更高效的完成网络数据请求,当然,具体什么是协程,本篇也不会细讲,大家可自行查询相关文档!

网络请求框架封装第一步(老生常谈),创建ApiService:

interface ApiService {@GET("banner/json")suspend fun getBanner(): BaseResult<List<BannerBean>>@GET("article/listproject/{page}/json")suspend fun getArticleList(@Path("page") page: Int): BaseResult<ArticleListBean>}

注意suspend ,这个是协程的重要关键字,简单的理解就是,它的作用等同于一个方法的回调(Callbak),举个例子:


fun test1():String{var result//...return result
}
fun test2(param:String){var result//...//需要使用test1的返回的结果return result
}

看这两个方法,方法2需要使用方法1返回的结果,如果两个都是异步请求,通常的写法如下:

fun test(){test1(object:CallBack(param){//请求完成回调test2(param)//在回调中调用方法2,如果有方法3.4...,则进入了回调地狱})
}

而使用suspend:则只需

suspend fun test(){var param=test1()var result=test2(param)var xx=tests(result)
}

即可,大家可以搜索更多相关文章去深入理解,现在只要记住在接口方法前要加上这个字段!

第二步,配置Retrofit:

class RetrofitClient {companion object {fun getInstance() = SingletonHolder.INSTANCEprivate lateinit var retrofit: Retrofit}private object SingletonHolder {val INSTANCE = RetrofitClient()}init {retrofit = Retrofit.Builder().client(getOkHttpClient()).addConverterFactory(GsonConverterFactory.create()).baseUrl(BASE_URL).build()}private fun getOkHttpClient(): OkHttpClient {return OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).addInterceptor(LoggingInterceptor()).build()}fun create(): ApiService = retrofit.create(ApiService::class.java)}

第三步,封装接口调用工具类HttpUtil:

class HttpUtil {private val mService by lazy { RetrofitClient.getInstance().create() }suspend fun getBanner() = mService.getBanner()suspend fun getArticleList(page: Int) = mService.getArticleList(page)/*** 单例模式*/companion object {@Volatileprivate var httpUtil: HttpUtil? = nullfun getInstance() = httpUtil ?: synchronized(this) {httpUtil ?: HttpUtil().also { httpUtil = it }}}}

第四步,经过以上三步,其实已经初步完成了对Retrofit的封装,我们来在ViewModel中试着调用一下:

class MainViewModel : ViewModel() {//MutableLiveData<>传入的数据类型,要与Service中BaseResult<>保证一致var bannerData = MutableLiveData<List<BannerBean>>()var articlesData = MutableLiveData<ArticleListBean>()fun getBannerTest() {viewModelScope.launch {withContext(Dispatchers.IO) {//异步请求接口val result = HttpUtil.getInstance().getBanner()withContext(Dispatchers.Main) {if (result.errorCode == 0) {//请求成功bannerData.value = result.data}}}}}fun getArticleListTest(page:Int) {viewModelScope.launch {withContext(Dispatchers.IO) {//异步请求接口val result = HttpUtil.getInstance().getArticleList(page)withContext(Dispatchers.Main) {if (result.errorCode == 0) {//请求成功articlesData.value = result.data}}}}}}

虽然已经封装了HttpUtil,但我们可以看到,这样调用,每个方法中都要写launch ,以及对结果的处理,这并不是一个理想的网络框架封装,我们要做的是将launch部分提取出来,并对结果统一处理,那么写一个BaseViewModel,将网络请求过程放在BaseViewModel里面,不失为一个好办法。但是,要想写一个统一的网络请求方法,具体该怎么写呢?说到这里,就不得不说Kotlin语法的一个新特性,它可以将一个函数作为一个参数,传入另一个函数,跟js一毛一样,太方便了,举个例子:

    fun test() {log("test")}fun test2(t: () -> Unit) {t()}test2({test()})

调用结果会执行log(“test”)!
-> Unit表示该方法无返回值;
-> 具体数据类型,则该方法返回该数据类型数据;
例如:

    fun test() :String{log("test")return "result"}fun test2(t: () -> String) {var result=t()log(result)}

调用 test2({test()})结果:
test
result

因此,我们可以将接口请求,也就是“HttpUtil.getInstance().getBanner()”这一部分作为一个参数传入统一请求方法中,具体如下:

    fun launch(api: suspend CoroutineScope.() -> Unit) {viewModelScope.launch {withContext(Dispatchers.IO) {//异步请求接口val result = api()withContext(Dispatchers.Main) {//处理result}}}}

调用的时候,只需:

 launch { HttpUtil.getInstance().getBanner() }

是不是很简单,但还没有完成,因为我们没有对返回结果result进行处理,好办,再传入一个success方法作为结果处理回调:

    fun <T>launch(api: suspend CoroutineScope.() -> BaseResult<T>,success:CoroutineScope.(T) -> Unit) {viewModelScope.launch {withContext(Dispatchers.IO) {//异步请求接口val result = api()withContext(Dispatchers.Main) {result.data?.let { success(it) }}}}}

调用:

        launch({HttpUtil.getInstance().getBanner()},{bannerData.value=it})

注意泛型T,就是BaseResult中的data:

open class BaseResult<T> {val errorMsg: String? = nullval errorCode: Int = 0val data: T? = null
}

第二个参数方法,也就是success:

{bannerData.value=it}

中的it就是返回的data,貌似到此,ViewModel中的网络请求已经足够简单了,但是还不够理想,因为我连success回调都不想写(下面是最终完整版方法):

BaseViewModel:

open class BaseViewModel : ViewModel() {val httpUtil by lazy { HttpUtil.getInstance() }var isShowLoading = MutableLiveData<Boolean>()//是否显示loadingvar errorData = MutableLiveData<ErrorResult>()//错误信息private fun showLoading() {isShowLoading.value = true}private fun dismissLoading() {isShowLoading.value = false}private fun showError(error: ErrorResult) {errorData.value = error}private fun error(errorResult: ErrorResult) {showError(ErrorResult(errorResult.code, errorResult.errMsg))}/*** 注意此方法传入的参数:api是以函数作为参数传入* api:即接口调用方法* error:可以理解为接口请求失败回调* ->数据类型,表示方法返回该数据类型* ->Unit,表示方法不返回数据类型*/fun <T> launch(api: suspend CoroutineScope.() -> BaseResult<T>,//请求接口方法,T表示data实体泛型,调用时可将data对应的bean传入即可liveData: MutableLiveData<T>,isShowLoading: Boolean = false) {if (isShowLoading) showLoading()viewModelScope.launch {try {withContext(Dispatchers.IO) {//异步请求接口val result = api()withContext(Dispatchers.Main) {if (result.errorCode == 0) {//请求成功liveData.value = result.data} else {error(ErrorResult(result.errorCode, result.errorMsg))}}}} catch (e: Throwable) {//接口请求失败error(ErrorUtil.getError(e))} finally {//请求结束dismissLoading()}}}}

MainViewModel :

class MainViewModel : BaseViewModel() {//MutableLiveData<>传入的数据类型,要与Service中BaseResult<>保证一致var bannerData = MutableLiveData<List<BannerBean>>()var articlesData = MutableLiveData<ArticleListBean>()fun getBanner() {launch({ httpUtil.getBanner() }, bannerData)}fun getArticleList(page: Int) {launch({ httpUtil.getArticleList(page) }, articlesData, true)}}

到此,算是完成了一个真正具有使用价值的网络请求框架!

项目下载链接:https://download.csdn.net/download/baiyuliang2013/12361150

MVVM+Retrofit+Kotlin网络框架封装相关推荐

  1. 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  2. 上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一)

    上门洗车APP --- Android客户端开发 之 网络框架封装介绍(一) 上篇文章中给大家简单介绍了一些业务,上门洗车APP --- Android客户端开发 前言及业务简介,本篇文章给大家介绍下 ...

  3. iOS swift Alamofire+HandyJSON网络框架封装

    iOS swift Alamofire+HandyJSON网络框架封装 我们在学习Objective_C时使用的网络框架是AFNetworking+MJExtension,而在swift中Alamof ...

  4. Android之全面解析Retrofit网络框架封装库

    转载请标明出处:[顾林海的博客] 前言 Retrofit是Square公司推出的一个HTTP的框架,主要用于Android和Java,Retrofit会将每一个HTTP的API请求变成一个Java的接 ...

  5. Retrofit的网络框架介绍

    Retrofit简介 Retrofit是square开源的网络请求库,底层是使用OKHttp封装的,网络请求速度很快. 主要有一下几种请求方法 格式 含义 @GET 表示这是一个GET请求 @POST ...

  6. java retrofit_Android开发Retrofit2+Rxjava2+okHttp 网络框架封装

    释放双眼,带上耳机,听听看~! 说明 RxJava的概念其实很模糊,我对它的理解就是一个给你方便处理异步问题的框架,到底有多方便,体会过才知道... Retrofit就是对okhttp做了一层封装.把 ...

  7. Retrofit2+Rxjava2+okHttp 网络框架封装

    说明 RxJava的概念其实很模糊,我对它的理解就是一个给你方便处理异步问题的框架,到底有多方便,体会过才知道... Retrofit就是对okhttp做了一层封装.把网络请求都交给给了Okhttp, ...

  8. 最简易的网络框架封装(新手可看)

    网络通信在Android上的重要性就不多说了. demo就是获取到"https://www.baidu.com/"的HTML代码后显示在textview中,如下图:(源码在文章结尾 ...

  9. dio网络框架封装_Flutter 使用dio来发起网络请求以及Cookie管理

    前言 Flutter官方建议您使用 dio 来发起网络请求,在学习过程中,也尝试过用dart io中的HttpClient发起的请求,这里主要讲一下dio的使用以及CookieJar.CookieMa ...

最新文章

  1. 算法----- 在排序数组中查找元素的第一个和最后一个位置
  2. 2019 训练比赛 记录
  3. mysql now unixtime_MySQL时间函数from_unixtime()date_format()unix_timestamp()now()使用说明
  4. 蓝桥杯 子串分值 递推
  5. 【生活科普】这7个影视剧的经典桥段,骗了我们很多年……
  6. 一个社交电商小程序配套的平台接
  7. C语言 文件缓冲区
  8. pytorch—torch.tensor.scatter操作解析
  9. 【数学信号处理】基于matlab数字信号频谱分析【含Matlab源码 1544期】
  10. gns3中怎么把服务器虚拟化,GNS3使用详解(gns3如何模拟ids)
  11. 帝骑k触屏模拟器_假面骑士decade神主牌模拟器手机版下载|
  12. 阳历转阴历,阳历转中国农历
  13. 网卡的功能主要有两个
  14. 数据结构练习题――中序遍历二叉树
  15. dcat-admin oss图片上传
  16. (一)、写一个怪物的类,类中有属性姓名(name),攻击力(attack),有打人的方法(fight)。(方法的重写)...
  17. 2022年上半年软件设计师考试下午真题(专业解析+参考答案)
  18. const定义及初始化约束
  19. plot fplot ezplot
  20. 一个人并不寂寞,想一个人才寂寞:QQ空间情感日志

热门文章

  1. [ERP]ERP项目实施过程中的致命过失
  2. OSX下代码打开系统首选项(system preferences)
  3. 2021-天梯赛(cccc)比赛题解
  4. Python 命令行编程
  5. java全栈系列之JavaSE-面向对象(封装详解)034
  6. Vue2 -- 组件化
  7. Vue + Spring Boot 项目实战(三):前后端结合测试(登录页面开发)
  8. 程序员之路:Linux目录处理命令_彭世瑜_新浪博客
  9. Redis set 类型 查询命令
  10. python画图颜色填充_Python使用Turtle图形函数画图 颜色填充!(学习笔记)