本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布
背景

用過(guò)RxJava和Retrofit的朋友,用久了就會(huì)發(fā)現(xiàn)Retrofit說(shuō)難不難,說(shuō)易亦不易,對(duì)于實(shí)際項(xiàng)目中,單純的運(yùn)用Retrofit做網(wǎng)絡(luò)請(qǐng)求庫(kù),開(kāi)發(fā)起來(lái)還是有很多不便,諸如必須要對(duì)請(qǐng)求頭和參數(shù)處理,API接口數(shù)目眾多的情況下處理起來(lái)也不便, 還有Https證書(shū)驗(yàn)簽,cookie持久,錯(cuò)誤結(jié)果碼處理,統(tǒng)一操作加載過(guò)渡UI等也存在不便,因此我對(duì)Retrofit再次進(jìn)行了封裝,一直關(guān)注我的朋友以前看我封裝的《 基于Retrofit2.0 封裝的超好用的RetrofitClient工具類》的一文,已對(duì)Retrofit結(jié)合RxJava時(shí)遇到上面的問(wèn)題進(jìn)行了完整封裝,很多場(chǎng)景還未做到全面,也不是和Retrofit源碼一樣的Builder模式,因此感覺(jué)還是不太完美,特此我進(jìn)行了長(zhǎng)達(dá)兩個(gè)月Novate 框架的開(kāi)發(fā),給舊的HttpClent遷移到Retrofit2.0也是帶來(lái)了福音(不需要開(kāi)發(fā)者掌握RxJava和Retrofit)。
本框架從6月問(wèn)世以來(lái),無(wú)論對(duì)入門Rxjava還是實(shí)戰(zhàn)retrofit的朋友,或多或少的帶來(lái)的參考價(jià)值,無(wú)論你是copy源碼還是采用maven形式。所以讓我有動(dòng)力進(jìn)行下一版本(2.X)的強(qiáng)化!
為何起名為Novate?**
Novate的英文原意是用新事物代替 ,我開(kāi)發(fā)目的是用新的東西來(lái)代替Retrofit結(jié)合Rxjava共同開(kāi)發(fā)時(shí)的有些不易操作的地方,因此起名新奇的東西,所以結(jié)合了原來(lái)的HttpClient用法習(xí)慣,又加入了Retrofit和RxJava的特性,因此起名 :Novate
進(jìn)行下文前請(qǐng)先了解Retrofit和Rxjava,未閱讀的請(qǐng)移步:
系列導(dǎo)讀:
介紹
Novate的改進(jìn):
- 優(yōu)化設(shè)計(jì):加入基礎(chǔ)API,減少Api冗余
- 強(qiáng)大的緩存模式: 支持離線緩存, 無(wú)網(wǎng)絡(luò)智能加載緩存,可配置是否需要緩存.
- cookie管理:自帶cookie管理機(jī)制.
- 全方位請(qǐng)求模式:支持多種方式訪問(wèn)網(wǎng)絡(luò)(get, put, post ,delete).
- 輕送調(diào)用:支持表單,圖文一起,json上傳。
- 文件傳輸:支持文件下載和上傳,并支持進(jìn)度。
- 動(dòng)態(tài)添加:支持請(qǐng)求頭和參數(shù)統(tǒng)一添加,分別添加。
- 結(jié)果處理:支持對(duì)返回結(jié)果的統(tǒng)一處理。
- 擴(kuò)展性強(qiáng):支持自定義的擴(kuò)展API,默認(rèn)Api無(wú)法滿足時(shí)可自定義自己的Service
- 悠雅方便:支持統(tǒng)一請(qǐng)求訪問(wèn)網(wǎng)絡(luò)的流程控制,以方便幫你完美加入Processbar進(jìn)度。
- RxJava結(jié)合: 結(jié)合RxJava,線程智能控制.
- 兼容retrofitAPI,兼容okhttp API
- 強(qiáng)大的泛型支持,自動(dòng)解析各種格式復(fù)雜數(shù)據(jù)。
API
依賴Gradle:
- root:
repositories {
maven { url "https://jitpack.io" }
jcenter()
}
- app:
dependencies {
.....
compile 'com.tamic.novate:novate:1.5.3.2'
}
基本構(gòu)建:
Novate novate = new Novate.Builder(this)
.baseUrl(baseUrl)
.build();
除了基本的構(gòu)建還提供更了其他API
構(gòu)建你的header頭和參數(shù)
Map<String, String> headers = new HashMap<>();
headers.put("apikey", "4545sdsddfd7sds");
Map<String, String> parameters = new HashMap<>();
parameters.put("uid", "878787878sdsd");
實(shí)例化:
Novate novate = new Novate.Builder(this)
.addParameters(parameters)
.connectTimeout(8)
.baseUrl("you api url")
.addHeader(headers)
.addLog(true)
.build();
更多:
novate = new Novate.Builder(this)
.addHeader(headers) //添加公共請(qǐng)求頭
.addParameters(parameters)//公共參數(shù)
.connectTimeout(10) //連接時(shí)間 可以忽略
.readTimeout(10) //讀取 可以忽略
.addCookie(false) //是否同步cooike 默認(rèn)不同步
.addCache(true) //是否緩存 默認(rèn)緩存
.addCache(cache, cacheTime) //自定義緩存
.baseUrl("Url") //base URL
.addLog(true) //是否開(kāi)啟log
.cookieManager(new NovateCookieManager()) // 自定義cooike,可以忽略
.addInterceptor() // 自定義Interceptor
.addNetworkInterceptor() // 自定義NetworkInterceptor
.proxy(proxy) // 設(shè)置代理
.client(client) //clent 默認(rèn)不需要
.build();
如果你支持https需要接入證書(shū):
novate.addSSL(hosts, certificates)
怎么用?
準(zhǔn)備證書(shū)文件數(shù)組和host 白名單;
int[] certificates = {R.raw.myssl1, R.raw.myssl2,......}
int[] hosts = {"https:// you hosturl2", "https:// you hosturl2",......}
還要說(shuō)明?
certificates是你的ssl證書(shū)文件的id,項(xiàng)目中請(qǐng)放到raw資源文件下, myssl.cer怎么生成, 請(qǐng)用pc瀏覽器自動(dòng)導(dǎo)出證書(shū),保存, 還不清楚的話,我會(huì)醉醉。
想對(duì)某個(gè)api不想緩存:
novate.addCache(false)
想對(duì)某個(gè)api不想cookie持久 :
novate.addCookie(false)
同樣很多人想問(wèn) 我想對(duì)novate進(jìn)行擴(kuò)展,咋辦?別擔(dān)心,Novate也提供了以下方法:
novate.addInterceptor()
.addCallAdapterFactory()
.callFactory()
.proxy()
.client()
上面只是列舉了幾個(gè)簡(jiǎn)單的api,更多的還是沿用的Retrofit的方法名,Retrofit怎么使用,Novate就怎么用.
RxJava怎么處理?
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
內(nèi)部統(tǒng)一已進(jìn)行線程控制,所有請(qǐng)求都采用以上線程形式,無(wú)需你手動(dòng)添加。
RxApi
主要處理請(qǐng)求的API,
包含RxGet, RxPost, RxDelete,RxPut, RxBody,RxFrom, RxUpLoad,RxDownLoad.
使用基本APi之前 請(qǐng)閱讀對(duì)RxCallBack的介紹。
RxGet
進(jìn)行g(shù)et方式的請(qǐng)求調(diào)用,多種返回結(jié)果的方式供你選擇,返回不同的數(shù)據(jù)類型參考請(qǐng)看RxCallBack的介紹。
基礎(chǔ)使用:
返回String
new Novate.Builder(this)
.baseUrl("http://ip.taobao.com/")
.build()
.rxGet("service/getIpInfo.php", parameters, new RxStringCallback() {
@Override
public void onStart(Object tag) {
super.onStart(tag);
}
@Override
public void onNext(Object tag, String response) {
}
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
});
返回Bean
novate.rxGet("path or url", parameters, new RxResultCallback<ResultModel>() {
});
返回List
new Novate.Builder(this)
.baseUrl("http://xxx.com/")
.build()
.rxGet("service/getList", parameters, new RxListCallback<List<MusicBookCategory>>() {
@Override
public void onNext(Object tag, int code, String message, List<MusicBookCategory> response) {
}
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
});
返回File
novate.rxGet("path or url", null, new RxFileCallBack(filePath, "name.jpg") {
});
RxPost:
進(jìn)行Post方式的請(qǐng)求調(diào)用
返回String
novate.rxPost("path or url", parameters, new RxStringCallback() {
});
返回Bean
novate.rxPost("path or url", parameters, new RxResultCallback<ResultModel>() {
});
返回List
novate.rxPost("path or url", parameters, new RxListCallback<List<ResultModel>>() {
....
});
返回File
novate.rxPost("path or url", null, new RxFileCallBack(filePath, "name.jpg") {
});
RxUpLoad
以Body方式post數(shù)據(jù),可以上報(bào)載文件,圖片,多媒體文件等。
Novate提供了2種方式上傳文件。body和part模式,Body不包含key值,part包含key值。
RxUploadWithBody
以Body方式post數(shù)據(jù),可以上報(bào)文件,圖片等。
String mPath = uploadPath; //"you File path ";
String url = "http:/xxx.com";
novate.rxUploadWithBody(url, new File(mPath), new RxStringCallback() {
@Override
public void onNext(Object tag, String response) {
}
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
});
}
RxUploadWithPart
**上傳文件,默認(rèn)的key是 image **
String mPath = uploadPath; //"you File path ";
String url = "http:/xxx.com";
File file = new File(mPath);
novate.rxUploadWithPart(url, file, new RxStringCallback() {
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
@Override
public void onNext(Object tag, String response) {
}
});
如果自定義key 請(qǐng)看下面
String mPath = uploadPath; //"you File path ";
String url = "http:/xxx.com";
File file = new File(mPath);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data; charset=utf-8"), file);
final NovateRequestBody requestBody = Utils.createNovateRequestBody(requestFile, new UpLoadCallback() {
@Override
public void onProgress(Object tag, int progress, long speed, boolean done) {
updateProgressDialog(progress);
}
});
MultipartBody.Part body2 =
MultipartBody.Part.createFormData("image", file.getName(), requestBody);
//請(qǐng)將image改成你和服務(wù)器約定好的key
novate.rxUploadWithPart(url, body2, new RxStringCallback() {
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
@Override
public void onNext(Object tag, String response) {
}
});
上傳多文件:
rxUploadWithPartListByFile:
List<File> fileList = new ArrayList<>();
fileList.add(file);
fileList.add(file);
fileList.add(file);
novate.rxUploadWithPartListByFile(url, fileList, new RxStringCallback() {
@Override
public void onStart(Object tag) {
super.onStart(tag);
}
@Override
public void onNext(Object tag, String response) {
}
@Override
public void onError(Object tag, Throwable e) {
}
});
RxDownLoad
以get方式下載數(shù)據(jù),可以下載文件,圖片,多媒體文件。
使用rxGet()實(shí)現(xiàn)下載:
String downUrl = "http://wap.dl.pinyin.sogou.com/wapdl/hole/201512/03/SogouInput_android_v7.11_sweb.apk";
novate.rxGet(downUrl, parameters, new RxFileCallBack(FileUtil.getBasePath(this), "test.apk") {
@Override
public void onStart(Object tag) {
super.onStart(tag);
showPressDialog();
}
@Override
public void onNext(Object tag, File file) {
dismissProgressDialog();
}
@Override
public void onProgress(Object tag, float progress, long downloaded, long total) {
updateProgressDialog((int) progress);
}
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
@Override
public void onCompleted(Object tag) {
super.onCompleted(tag);
}
});
RxDown()下載
String downUrl = "http://wap.dl.pinyin.sogou.com/wapdl/hole/201512/03/SogouInput_android_v7.11_sweb.apk";
new Novate.Builder(this)
.connectTimeout(20)
.writeTimeout(15)
.baseUrl(baseUrl)
.build()
.rxDownload(downUrl, new RxFileCallBack(FileUtil.getBasePath(this), "test.apk") {
@Override
public void onStart(Object tag) {
super.onStart(tag);
showPressDialog();
}
@Override
public void onNext(Object tag, File file) {
dismissProgressDialog();
Toast.makeText(ExampleActivity.this, "下載成功!", Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(Object tag, float progress, long downloaded, long total) {
updateProgressDialog((int) progress);
}
@Override
public void onProgress(Object tag, int progress, long speed, long transfered, long total) {
super.onProgress(tag, progress, speed, transfered, total);
updateProgressDialog((int) progress);
}
@Override
public void onError(Object tag, Throwable e) {
}
@Override
public void onCancel(Object tag, Throwable e) {
}
@Override
public void onCompleted(Object tag) {
super.onCompleted(tag);
dismissProgressDialog();
}
});
Custom Api
以上方法默認(rèn)會(huì)處理Novate自帶的BaseApiService,如果默認(rèn)的BaseApiService無(wú)法滿足你的需求時(shí),Novate同樣支持你自己的ApiService。
定義一個(gè)你自己的API:
MyAPI
public interface MyAPI {
@GET("url")
Observable<MyBean> getdata(@QueryMap Map<String, String> maps);
}
Execute Call
通過(guò)novate提供create()實(shí)例化你的API
MyAPI myAPI = novate.create(MyAPI.class);
通過(guò)novate.call()來(lái)執(zhí)行你的接口,你也不用關(guān)心,novate內(nèi)部同樣已進(jìn)行RxJava線程控制。
novate.call(myAPI.getdata(parameters),
new BaseSubscriber<MyBean>(ExempleActivity.this) {
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(MyBean MyBean) {
}
});
}
取消
每執(zhí)行novate.xxx() 給上層返回了一個(gè)Subscription,上層可以調(diào)用unsubscribe()來(lái)進(jìn)行取消!
if (!subscription.isUnsubscribed()) {
subscription.unsubscribe();
}
感謝
感謝一直以來(lái)提供思路,和測(cè)試的朋友,不斷提供建議和思路,讓我不斷完善novate,。 有的人看過(guò)或者讀過(guò)后覺(jué)得此框架定制化嚴(yán)重,也有的人覺(jué)得挺好,所以提供了可配置方案:
如果你覺(jué)得此框架的業(yè)務(wù)碼和錯(cuò)誤碼定的太死,其實(shí)框架已提供定制化方案,比如可以在你的項(xiàng)目中Assets中修改config文件:
如果想用自帶的成功狀態(tài)碼0,不成功為非零的情況,可忽略一下配置。
{ "isFormat": "false", "sucessCode": [ "1", "0", "1001" ], "error": { "1001": "網(wǎng)絡(luò)異常" } }
如果不想對(duì)結(jié)果格式化檢查,請(qǐng)將isFormat設(shè)置為:false
將修改sucessCode的成功業(yè)務(wù)嗎,請(qǐng)將你的成功的業(yè)務(wù)碼加入到sucessCode節(jié)點(diǎn)中。
錯(cuò)誤碼
需要對(duì)錯(cuò)誤碼進(jìn)行自定義翻譯,請(qǐng)配置相關(guān)error信息,具體可配置成:
`{
"isFormat": "false",
"sucessCode": [
"1",
"0",
"1001"
],
"error": {
"1001": "網(wǎng)絡(luò)異常",
"1002": "加入你的異常信息"
}
}
缺陷
目前1.X并沒(méi)有完全運(yùn)用RxJava2.0的新特性,筆者以開(kāi)始聯(lián)合@一葉扁舟 做兼容RxJava2.x的APi的工作, 目前Novate很遺憾無(wú)法為你提供壓棧,背壓式服務(wù)!
在連續(xù)異步多個(gè)api時(shí),諸如指定序列請(qǐng)求網(wǎng)絡(luò)的場(chǎng)景,大白話就是你要根據(jù)上一個(gè)api的返回值再執(zhí)行下一個(gè)api的情況,Novate1.x只能是靠開(kāi)發(fā)者在上層的成功回調(diào)中執(zhí)行,如果是1.x是對(duì)retrofit的強(qiáng)化,那么novate2.x將是對(duì)RxJava的運(yùn)用強(qiáng)化。

結(jié)束
如果你對(duì)本框架有無(wú)法滿足你的需求或有何更好的想法,請(qǐng)及時(shí)聯(lián)系我進(jìn)行交流,謝謝您的支持!歡迎您的star. Tamic期待更多的技術(shù)大牛開(kāi)車指導(dǎo)!
GitHub: https://github.com/Tamicer/Novate
第一時(shí)間獲取各位大佬的技術(shù)文章和資訊請(qǐng)關(guān)注微信公眾號(hào)!
