Novate:Retrofit2.0和RxJava的又一次完美改進(jìn)加強(qiáng)?。ň牛?/h2>

作者/Tamic
http://www.itdecent.cn/p/d7734390895e

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

背景

Novate

用過(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)化。

感謝公司設(shè)計(jì)妹子提供的Logo, 加速標(biāo)識(shí)哦

結(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)!

開(kāi)發(fā)者技術(shù)前線
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容