Kotlin封装RxJava与Retrofit
代码地址:https://github.com/DarkPointK/RxTrofit.git
前言
Retrofit是Square公司开发的一个类型安全的Java和Android 的REST客户端库。来自官网的介绍:
A type-safe HTTP client for Android and Java
Rest API是一种软件设计风格,服务器作为资源存放地。客户端去请求GET,PUT, POST,DELETE资源。并且是无状态的,没有session的参与。
- retrofit模型如下:
- POJO或模型实体类 : 从服务器获取的JSON数据将被填充到这种类的实例中。
- 接口 : 我们需要创建一个接口来管理像GET,POST...等请求的URL,这是一个服务类。
- RestAdapter类 : 这是一个REST客户端(Rest Client)类,retrofit中默认用的是Gson来解析JSON数据,你也可以设置自己的JSON解析器。
Retrofit是基于OkHttp的网络接口的封装,之所以被中小型项目所推行使用,还是得力于它的相对规范化、安全性、可操作性
RxJava 在 GitHub 主页上的用这样一句话形容自己是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准,但这句话更像是对RxJava的一个总结。其实, 简单来说RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。用我个人的话来说,RxJava既是AsyncTask 的终极形态。
准备工作
前面,我们简单的了解了一下Retrofit与RxJava,本文的着重点在大家已经会用的前提下,还是要和大家探讨如何去应用以及封装他们。想要在项目中使用他们,第一步便是在Gradle中添加相应的Dependencies:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'//retrofit2的Json转换器(默认用Gson)
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'//okhttp提供的请求日志拦截器
compile 'io.reactivex:rxjava:1.3.0'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
简单封装
接下来,创建必要的类与接口,当然,别忘了manifests中添加权限:
<uses-permission android:name="android.permission.INTERNET"/>
新建一个提供给Retrofit初始化,用于描述服务端接口的Interface,我在这里调用的是豆瓣的一个公开接口:
interface HttpInterface {
@GET("top250")
fun getTopMovie(@Query("start") start: Int, @Query("count") count: Int): Observable<GetMovie>
}
这里定义了一个GET目录top250,参数为start,count,返回值则是一个Observable绑定了一个接口返回数据的实体类
接着新建一个用于初始化Retrofit封装请求的方法类:
class HttpMethod {
companion object {
val httpMethod: HttpMethod = Holder.INSTANCE
}
private object Holder {
val INSTANCE = HttpMethod()
}
}
由于网络请求在业务逻辑中将频繁的被调用,所以需要我们在这里创建一个当前类的单例,以避免被多次实例化。
当我们创建好单例后,便可以放心的来初始化我们即将需要用到的3个关键的成员变量了,retrofit的回调适配器我们在这选用RxJavaCallAdapterFactory:
private var okclient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().
setLevel(HttpLoggingInterceptor.Level.BODY))
.addNetworkInterceptor(OAuthIntercepter())
connectTimeout(8, TimeUnit.SECONDS)
readTimeout(15, TimeUnit.SECONDS)
writeTimeout(15, TimeUnit.SECONDS)
build()
private var retrofit: Retrofit = Retrofit.Builder()
.client(okclient)
.baseUrl("https://api.douban.com/v2/movie/")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory
.create(GsonBuilder().serializeNulls().setLenient().create())
)
.build()
private var httpInterface = retrofit.create(HttpInterface::class.java)
我们首先Builder了一个OkhttpClient对象,并为其添加了一个请求日志的拦截器,紧接着初始化了Retrofit为其添加RxJavaCallAdapter,最后以HttpInterface为参数将retrofit创建为HttpInterface,这样我们就可以用httpInterface封装一个调用之前所描述的getTopMovie接口方法,在这个方法中我们会将Retrofit完成请求返回的被观察者订阅起来,在这个过程中我们可以对返回的数据流通过RxJava丰富的操作符进行自由的处理。
定义一个getTopMovie方法用于在业务逻辑中调用retrofit接口,这里我们需要三个参数,其中前两个为GET请求所需要的参数,而subscriber用来订阅retrofit接口返回的Observable:
fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<GetMovie>) {}
这样我们便可以在客户端的业务逻辑中通过调用HttpMethod. INSTANCE .getTopMovie(…)来请求接口,但是此时我们还没有将Observable订阅上,所以现在我们需要完善下这个方法:
fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<GetMovie>) {
toSubscriber(httpInterface.getTopMovie(start,count),subscriber)
}
可以看到我们在方法体中添加了一句toSubscriber(…),至此我们便将subscriber订阅上httpInterface.getTopMovie(…)返回的Observable了,至于这个subscriber参数具体如何实现,在下一节将会介绍,我们先来了解下这个toSubscriber的工作是如何实现的:
private fun toSubscriber (observable: Observable<T>, subscriber: Subscriber<T>) {
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError {}
.subscribe(subscriber)
}
熟悉RxJava的码友应该一眼变冷看出这是一段典型的订阅流程,是的,这是通过RxJava流式编程实现最简单的订阅事件。
至此,我们便已经可以再业务逻辑中正常使用getTopMovie(…)了:
HttpMethod.httpMethod.getTopMovie(1,1,object :Subscriber<GetMovie>(){
override fun onNext(t: GetMovie?) {
}
override fun onCompleted() {
}
override fun onError(e: Throwable?) {
}
})
进阶用法
我们已经知道如何简单的方式通过RxJava调用Retrofit接口,但这往往不能的满足我们的追求。
在项目中,往往我们会定义这样的返回体结构:
{
"code": 1,
"data": [ ],
"msg": "success"
}
我们会在所有接口得到3个全局相应参数,其中code代表结果码,msg代表附加信息,而data则是对应的响应数据会因接口而异,我们往往需要每个接口根据code去判断接下来的操作,那么我们应该怎样封装才能让每个统一进行处理呢,接下来我们改一改前面的订阅流程:
data class GetMovie<T>(
@SerializedName("count") var count: Int?, //
@SerializedName("start") var start: Int?, //
@SerializedName("total") var total: Int?, //
@SerializedName("subjects") var subjects: T?,
@SerializedName("title") var title: String? //豆瓣电影Top250
)
这里我们切当subjects以外的参数为固有的全局响应参数,而subjects的List元素类型则不定,我们需要显示的绑定GetMovie以在订阅时可以取得全局响应参数而不用关心subjects的类型,改动的包括接口定义在内用到实体类泛型的地方:
interface HttpInterface {
@GET("top250")
fun getTopMovie(@Query("start") start: Int, @Query("count") count: Int): Observable<GetMovie<List<Subject>>>
}
接口表示我们获取到的返回数据将会是GetMovie类的结构,并且泛型指出subjects的类型是个Subject的List,接下来在将HttpMethod中封装请求的方法也修改下:
fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<List<Subject>>) {
toSubscriber(httpInterface.getTopMovie(start, count), subscriber)
}
由于我们需要在RxJava处理数据的流程中,统一对全局响应参数进行判断,所以订阅的流程便成为了我们工作主要进行的场所:
private fun <T> toSubscriber(observable: Observable<GetMovie<T>>, subscriber: Subscriber<T>) {
observable
.compose {
it.flatMap { result ->
if (result.count!! > 0)
createData(result.subjects)
else
Observable.error(Throwable(""))
}
}
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError {}
.subscribe(subscriber)
}
可以看到这里我们使用了两个RxJava的操作符分别是compose与flatMap,经过flatMap的转换返回一个新的被观察者绑定的数据便是subjects:
private fun <T> createData(data: T): Observable<T> {
return Observable.create { subscriber ->
try {
subscriber.onNext(data)
subscriber.onCompleted()
} catch (e: Exception) {
subscriber.onError(e)
}
}
}
最后,业务逻辑中这样调用:
HttpMethod.httpMethod.getTopMovie(1,1,object :Subscriber<List<Subject>>(){
override fun onNext(t: List<Subject>?) {
sample_text.text =t?.get(0)?.originalTitle
}
override fun onCompleted() {
}
override fun onError(e: Throwable?) {
}
})
传入参数及Subscriber,经过compose转换在onNext中得到List<Subject>。
至此对RxJava与Retrofit的封装告一段落了,通过compose操作符,我们可以随心的去操作数据流,以得到我们想要的数据,并且还有其他丰富的操作符,例如repeatWhen,retryWhen ,doOnError,delay等等实用的方法,在稍后会和大家做进一步介绍。
Kotlin封装RxJava与Retrofit的更多相关文章
- RxJava 与 Retrofit 结合的最佳实践
转自:http://gank.io/post/56e80c2c677659311bed9841?from=timeline&isappinstalled=0&nsukey=g1D1Y6 ...
- RxJava整合Retrofit遇到的问题总结
一:初上手(填坑) Observable将事件序列执行完毕后,会回调Observe的onNext()方法和onCompleted()方法,当出现异常/错误时会调用onError()方法. 由此,我们推 ...
- (转载)RxJava 与 Retrofit 结合的最佳实践
RxJava 与 Retrofit 结合的最佳实践 作者:tough1985 感谢 DaoCloud 为作者提供的 500 RMB 写作赞助: 成为赞助方 /开始写作 前言 RxJava和Retrof ...
- MVP模式入门(结合Rxjava,Retrofit)
本文MVP的sample实现效果: github地址:https://github.com/xurui1995/MvpSample 老规矩,在说对MVP模式的理解之前还是要再谈谈MVC模式,了解了MV ...
- rxjava 调用retrofit执行网络请求的过程
retrofit流程图 -1.RxJava调用Retrofit,从requestGtPushSaeUserInfo()中获得被观察者observable,然后new一个观察者向它订阅 0.从业务中 ...
- Rxjava, RxAndroid, Retrofit 等库的使用
RxJava的基本用法: 关于 unSubscribe() 的调用问题: There is no need to unsubscribe in onCompleted. Take a look at ...
- RxJava开发精要8 – 与REST无缝结合-RxJava和Retrofit
原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...
- [Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩
原文url: http://blog.iliyun.net/2016/11/20/%E6%A1%86%E6%9E%B6%E5%B0%81%E8%A3%85/ 这几年来android的网络请求技术层出不 ...
- 《Android进阶之光》--RxJava结合Retrofit访问网络
1)配置 dependencies{ ... compile 'io.reactivex:rxjava:1.2.0' compile 'io.reactivex:rxandroid:1.2.1' co ...
随机推荐
- 转 Caffe学习系列(4):激活层(Activiation Layers)及参数
在激活层中,对输入数据进行激活操作(实际上就是一种函数变换),是逐元素进行运算的.从bottom得到一个blob数据输入,运算后,从top输入一个blob数据.在运算过程中,没有改变数据的大小,即输入 ...
- 电脑中dll文件丢失怎么恢复?
DLL文件是Windows系统中的动态链接文件,我们在运行程序时都必须链接到dll文件,如果缺少了则无法正常运行,相信大家都会遇到dll文件缺失的情况,那么电脑中dll文件丢失怎么恢复?下面装机之家分 ...
- 3.3.2 PCI设备对不可Cache的存储器空间进行DMA读写
在x86处理器和PowerPC处理器中,PCI设备对"不可Cache的存储器空间"进行DMA读写的过程并不相同.其中PowerPC处理器对"不可Cache的存储器空间&q ...
- 运行Java Web项目报错
运行Java Web项目报错 今天,我启动服务,发现项目报错,并且是在打开登录界面时报错. 具体错误如下: org.apache.catalina.core.Standard WrapperValue ...
- TypeError: Error #1034: 强制转换类型失败:无法将 "0.49" 转换为 mx.graphics.IFill。
1.错误描述 TypeError: Error #1034: 强制转换类型失败:无法将 "0.49" 转换为 mx.graphics.IFill. at mx.charts.ser ...
- C#图解教程 第十七章 泛型
泛型 什么是泛型 一个栈的示例 C#中的泛型 继续栈示例 泛型类声明泛型类创建构造类型创建变量和实例 使用泛型的栈的示例比较泛型和非泛型栈 类型参数的约束 Where子句约束类型和次序 泛型方法 声明 ...
- 第八篇:支持向量机 (Support Vector Machine)
前言 本文讲解如何使用R语言中e1071包中的SVM函数进行分类操作,并以一个关于鸢尾花分类的实例演示具体分类步骤. 分析总体流程 1. 载入并了解数据集:2. 对数据集进行训练并生成模型:3. 在此 ...
- js常见排序
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
- 【洛谷1026】【NOIP2001】统计单词个数
题面 题目描述 给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个).要求将此字母串分成k份(1<k<=40),且每份中包 ...
- [BZOJ1083] [SCOI2005] 繁忙的都市 (kruskal)
Description 城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造.城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两个交叉路口 ...