Novate网络库: Retrofit2.0和RxJava1.x的又一次完美封装

RxJava+Retrofit 系列文章

Posted by Tamic on 2016-08-10

作者/Tamic
http://www.tamicer.com

背景

Novate

用过RxJava和Retrofit的朋友,用久了就会发现Retrofit说难不难,说易亦不易,对于实际项目中,单纯的运用Retrofit做网络请求库,开发起来还是有很多不便,诸如必须要对请求头和参数处理,API接口数目众多的情况下处理起来也不便, 还有Https证书验签,cookie持久,错误结果码处理,统一操作加载过渡UI等也存在不便,因此我对Retrofit再次进行了封装,一直关注我的朋友以前看我封装的《 Retrofit 2.0 超能实践(六)基于Retrofit2.0+RxJava 封装的超好用的RetrofitClient工具类》的一文,已对Retrofit结合RxJava时遇到上面的问题进行了完整封装,很多场景还未做到全面,也不是和Retrofit源码一样的Builder模式,因此感觉还是不太完美,特此我进行了长达两个月Novate 框架的开发,给旧的HttpClent迁移到Retrofit2.0也是带来了福音(不需要开发者掌握RxJavaRetrofit)。

本框架从6月问世以来,无论对入门Rxjava还是实战retrofit的朋友,或多或少的带来的参考价值,无论你是copy源码还是采用maven形式。所以让我有动力进行下一版本(2.X)的强化!

为何起名为Novate

Novate的英文原意是用新事物代替 ,我开发目的是用新的东西来代替Retrofit结合Rxjava共同开发时的有些不易操作的地方,因此起名新奇的东西,所以结合了原来的HttpClient用法习惯,又加入了RetrofitRxJava的特性,因此起名 :Novate

进行下文前请先了解RetrofitRxjava,未阅读的请移步:

功能介绍

Novate的改进

  • 优化设计:加入基础API,减少Api冗余
  • 强大的缓存模式: 支持离线缓存, 无网络智能加载缓存,可配置是否需要缓存
  • cookie管理:自带cookie管理机制
  • 全方位请求模式:支持多种方式访问网络(get,put, post ,delete)
  • 轻送调用:支持表单,图文一起,json上传。
  • 文件传输:支持文件下载和上传
  • 动态添加:支持请求头和参数统一添加,分别添加。
  • 结果处理:支持对返回结果的统一处理
  • 扩展性强:支持自定义的扩展API,默认Api无法满足时可自定义自己的Service
  • 悠雅方便:支持统一请求访问网络的流程控制,以方便帮你完美加入Processbar进度。
  • RxJava结合: 结合RxJava,线程智能控制.

APi说明

  • 集成

依赖Gradle:

  • root:
 repositories {
        maven { url "https://jitpack.io" }
        jcenter()
}
  • app:
dependencies {
 .....
   compile 'com.tamic.novate:novate:1.5.0'
}

Snapshots of the development version are available in Sonatype’s snapshots repository.

Retrofit requires at minimum Java 7 or Android 2.3.

Laster vension: https://bintray.com/neglectedbyboss/maven/Novate 最新版本点击去查询

加入权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

混淆

-keep class com.tamic.novate.** {*;}

基本构建:

1
2
3
4
Novate novate = new Novate.Builder(this)
.baseUrl(baseUrl)
.build();

除了基本的构建还提供更了其他API

1
2
3
4
5
6
7
8
//构建你的header头
Map<String, String> headers = new HashMap<>();
headers.put("apikey", "4545sdsddfd7sds");
// 构建你的parameters参数
Map<String, String> parameters = new HashMap<>();
parameters.put("uid", "878787878sdsd");

实例化:

1
2
3
4
5
6
7
8
Novate novate = new Novate.Builder(this)
.addParameters(parameters)
.connectTimeout(20)
.baseUrl("you api baseUrl")
.addHeader(headers)
.addLog(true)
.build();

如果你需要接入自定义证书:

1
2
novate.addSSL(hosts, certificates)

怎么用?

1
2
3
4
int[] certificates = {R.raw.myssl1, R.raw.myssl2,......}
int[] hosts = {"https:// you hosturl2", "https:// you hosturl2",......}

还要说明?

certificates是你的ssl证书文件在raw文件下的的id数组,项目中请放到raw资源文件下, myssl.cer加证书,怎么生成?,这个就不是我这边讲的内容,请用pc浏览器自动导出证书,保存, 还不清楚的话,自行百度吧,我会醉醉。

想对某个api不想缓存,

1
2
novate.addCache(false)

想对某个api不想cookiet持久:

1
2
novate.addCookie(false)

同样很多人想问 我想对novate进行扩展,咋办,别担心,Novate也提供了以下方法

1
2
3
4
5
novate.addInterceptor()
.addCallAdapterFactory()
.proxy()
.callFactory()
.client()

上面只是列举了几个简单的api,更多的还是沿用的retrofit的方法名,Retrofit怎么使用,Novate就怎么用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
novate = new Novate.Builder(this)
.addHeader(headers) //添加公共请求头
.addParameters(parameters)//公共参数
.connectTimeout(10) //连接时间 可以忽略
.addCookie(false) //是否同步cooike 默认不同步
.addCache(true) //是否缓存 默认缓存
.addCache(cache, cacheTime) //自定义缓存
.baseUrl("Url") //base URL
.addLog(true) //是否开启log
.cookieManager(new NovateCookieManager()) // 自定义cooike,可以忽略
.addInterceptor() // 自定义Interceptor
.addNetworkInterceptor() // 自定义NetworkInterceptor
.proxy(proxy) //代理
.client(client) //clent 默认不需要
.build();

RxJava怎么处理?

1
2
3
4
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

内部统一已进行线程控制,所有请求都采用以上线程形式,无需你手动添加。

RxAPi

RxGet为例子:多种方式供你选择

String

novate.rxGet("path or url", parameters, new RxStringCallback() {


 });

Bean

novate.rxGet("path or url", parameters, new RxResultCallback<ResultModel>() {



   });

List

novate.rxGet("path or url", parameters, new RxListCallback<List<ResultModel>>() {


      ....

   });

File

novate.rxGet("path or url", null, new RxFileCallBack(filePath, "name.jpg") {



   });

其他方式RxApi同样的用法

#Get

提供了novate.executeGet()novate.get() 两个API
区别如下:

executeGet() 来执行get请求,此方法会解析返回你需要的bean实体,并进行对返回格式的处理,并做异常处理。
novate.get()不会帮你解析实体,并返回原生的ResponseBody,需要你自己对返回数据解析进行序列化。此方法定制化
低,可灵活使用!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
novate.executeGet("you url", parameters, new Novate.ResponseCallBack<ResultModel>() {
@Override
public void onError(Throwable e) {
}
@Override
public void onsuccess(int code, String msg, MyModel response, String originalResponse) {
// todo 这里novate已对数据进行解析返回,
}
});

如果你不需要Novate帮你解析返回的数据 则调用novate.get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
novate.get("you Url", parameters, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
@Override
public void onNext(ResponseBody responseBody) {
try {
String jstr = new String(responseBody.bytes());
Type type = new TypeToken<MovieModel>() {
}.getType();
// 这里需要对ResponseBody进行解析,novate并不会帮你解析
MovieModel response = new Gson().fromJson(jstr, type);
Toast.makeText(ExempleActivity.this, response.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
});

#Post

Novate提供了novate.post()novate.executePost()两个API

区别如下:

executePost来执行Post方式请求,此方法对结果解析返回你需要的bean实体,并对返回格式按code ,msgdata,进行检查,并对错误结果进行处理!默认业务成功码为:0 ,非零走失败方法
novate.post()不会帮你解析实体,需要你自己对返回数据解析进行序列化。 此api比较灵活,不会进行格式校验!

GetPost代码用法很相似,

novate.post()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
*
*
* 调用post需要你自己解析数据
*
* 如果需要解析后返回 则调用novate.executeGet()
* 参考 performGet()中的方式
*/
novate.post("service/getIpInfo.php", parameters, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody responseBody) {
try {
String jstr = new String(responseBody.bytes());
Type type = new TypeToken<NovateResponse<ResultModel>>() {
}.getType();
NovateResponse<ResultModel> response = new Gson().fromJson(jstr, type);
Toast.makeText(ExempleActivity.this, response.getData().toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
});

novate.executePost()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
novate.executePost("pathUrl", parameters, new Novate.ResponseCallBack<MyModel>() {
@Override
public void onError(Throwable e) {
// todo
}
@Override
public void onsuccess(int code, String msg, MyModel response, String originalResponse) {
// todo 这里novate已对数据进行解析返回,
}
});

V1.2.5-bata 版本提供了直接解析真实数据的功能,对api很灵活的项目,适合使用。 具体姿势:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
**novate.executePost()**
novate.executePost("pathUrl", parameters, new Novate.ResponseCallBack<MyModel>() {
@Override
public void onError(Throwable e) {
// todo
}
@Override
public void onSuccee(ResultModel data) {
// todo
//
}
});

仔细一对比,你会发现V1.2.5-bata除外的版本都是用NovateResponse<MyModel>进行包装下,如果你嫌包装蛮烦,可以选择这个版本开发,但是弊端的成功回调并不包含业务码。

此版本的其他execute的方法同以上其他get的区别。

Put

put方式则提供了novate.put()novate.executePut()两个API

区别和用法参考 上面GetPut

Delete

同样对Delete 提供了novate.delete()novate.executeDelete()两个API

区别和用法参考 上面Get和Put的介绍

BODY

假设你需要提交body, Novate也提供了novate.body(),需要直接将你bean对象加入即可,因此可以:

1
2
3
4
5
6
7
8
9
10
11
ovate.body(url, uesrBean, new BaseSubscriber<ResponseBody>() {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody responseBody) {
}
});

FORM

假设你以提交表单,请使用novate.form(),你可以:

1
2
3
4
5
6
7
8
9
10
11
12
novate.form(url, new HashMap<String, Object>(), new BaseSubscriber<ResponseBody>() {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody responseBody) {
}
});

JSON

假设你需要直接push json 字符串,你可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
novate.json(url, jsonString, new BaseSubscriber<ResponseBody>() {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody responseBody) {
}
});
```
# UpLoad
当然novate 特提供了可供上传图片的API `novate.upload()`
```
RequestBody requestFile =Utils.createFile(file);
novate.upload(url, requestFile, new BaseSubscriber<ResponseBody>{
@Override
public void onNext(ResponseBody responseBody) {
}
});

如果是单文件

1
2
3
4
5
6
7
8
novate.uploadFlie(url, requestFile, new BaseSubscriber<ResponseBody>{
@Override
public void onNext(ResponseBody responseBody) {
}
});

upLoadFiles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Map<String, RequestBody> map = new HashMap<>();
maps.put("file1", requestFile);
novate.uploadFlies(url, map, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
......
} );
```
#图文一起
同时提交图文:适合用户注册等场景 `novate.uploadupload`
**body模式**
```
//构建body 链式语法高逼格哦
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("name", "tamic")
.addFormDataPart("password", "12345")
.addFormDataPart("atavr", file.getName(), RequestBody.create(MediaType.parse("image/*"), file))
.build();
novate.upload(url, requestBody, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
.....
});
```
**利用part模式**
```
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body = Utils.createPart("hello 这是和后端约定好的key", file);
// add 描述
String descriptionString = "hello, 这是文件描述";
RequestBody description = Utils.createPartFromString(descriptionString);
// 执行
novate.uploadFlie(url, description, body, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
。。。。。
});

DownLoad

可以使用RxGet(),具体参考RXAPI中File. 如果想歌你更多下载功能可以使用下面方式

如果说有上传文件的接口,必定有下载文件的接口,同样novate也提供了下载文件API novate.download()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
novate.download(downUrl, new DownLoadCallBack() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onCancel() {
super.onCancel();
}
@Override
public void onProgress(long fileSizeDownloaded) {
super.onProgress(fileSizeDownloaded);
}
@Override
public void onSucess(String path, int progress, String name, long fileSize) {
}
});

通常还有同学要定制下载路径和文件名,当然也有API提供:

1
2
3
4
public void download(String url, String savePath, String name, DownLoadCallBack callBack) {
.......
}

downLoad 大文件

1
2
3
4
5
6
novate.download(downUrl, new DownLoadCallBack() {
''''''''''''
});

downLoad 小文件

1
2
3
4
5
novate.downloadMin(downUrl, new DownLoadCallBack() {
''''''''''''
});

Custom Api

以上方法默认会处理Novate自带的BaseApiService,如果默认的BaseApiService无法满足你的需求时,Novate同样支持你自己的ApiService

定义一个你自己的APi

MyAPI

1
2
3
4
5
6
7
8
public interface MyAPI {
@GET("url")
Observable<MyBean> getdata(@QueryMap Map<String, String> maps);
}

Execute Call

通过novate提供create()实例化你的API

1
MyAPI myAPI = novate.create(MyAPI.class);

通过novate.call()来执行你的接口,你也不用关心,novate内部同样已进行RxJava线程控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
novate.call(myAPI.getdata(parameters),
new BaseSubscriber<MyBean>(ExempleActivity.this) {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(MyBean MyBean) {
}
});
}

#取消

每执行novate.xxx()给上层返回了一个Subscription,上层可以调用unsubscribe()来进行取消!或者直接用RxApiManager.cancel()进行取消.

1
2
RxApiManager.get().cancel(Tag);

感谢

感谢一直以来提供思路,和测试的朋友,不断提供建议和思路,让我不断完善novate。 有的人看过或者读过后觉得此框架定制化严重,也有的人觉得挺好,所以提供了可配置方案:

如果你觉得此框架的业务码和错误码定的太死,其实框架已提供定制化方案,比如可以在你的项目中Assets中修改config文件:

如果想用自带的成功状态码0,不成功为非零的情况,可以直接copy demo中Assets 下的 config.json到你的项目Assets下,无需修改。

1
2
3
4
5
6
7
8
9
{
"sucessCode": [
"1",
"0",
"1001"
],
}

如果不想对结果格式化检查,请将isFormat设置为:false

如果想修改sucessCode的成功业务码,请将你的成功的业务码加入到sucessCode节点中。支持多个。

错误码

需要对错误码进行自定义翻译,请配置相关error信息,支持多个,1001是错误code,“网络异常”表示错误信息。具体可配置成:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"isFormat": "false",
"sucessCode": [
"1",
"0",
"1001"
],
"error": {
"1001": "网络异常"
"1002": "加入你的异常信息"
}
}

统一网络和Loading

继承Novate自带的的BaseSubscriber<T>,复写onStart()onCompleted() 前者显示loading,后者结束loading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onStart() {
super.onStart();
Log.v("Novate", "-->http is start");
// todo some common as show loadding and check netWork is NetworkAvailable
// if NetworkAvailable no ! must to call onCompleted
}
@Override
public void onCompleted() {
Log.v("Novate", "-->http is Complete");
// todo some common as dismiss loadding
}

待完善

目前1.X并没有完全运用RxJava2.0的新特性,笔者以开始联合@一叶扁舟 做兼容RxJava2.x的APi的工作, 目前Novate很遗憾无法为你提供压栈,背压式服务!
在连续异步多个api时,诸如指定序列请求网络的场景,大白话就是你要根据上一个api的返回值再执行下一个api的情况,Novate1.x只能是靠开发者在上层的成功回调中执行,如果是1.x是对retrofit的强化,那么novate2.x将是对RxJava的运用强化。

感谢公司设计妹子提供的Logo, 加速标识哦

结束

如果你对本框架有无法满足你的需求或有何更好的想法,请及时联系我进行交流,谢谢您的支持!欢迎您的star. Tamic期待更多人来pull共享!

GitHub: https://github.com/Tamicer/Novate




第一时间获资讯请关注微信公众号!

开发者技术前线