Android Retrofit2+RexJava2+OK3網(wǎng)絡(luò)框架封裝實現(xiàn)

1eb0eafdd2bd3cdf9b50ce0c9895521b.jpg

前言

android時下最流行的網(wǎng)絡(luò)框架莫過于 retrofit + rexjava +ok3
本文記錄下我的網(wǎng)絡(luò)架構(gòu)轉(zhuǎn)型記錄渊跋。我項目里面采用的是OK3作網(wǎng)絡(luò)連接層,但是資源的下載我是用的rexjava+OK3單獨寫的一套下載util芋齿,想法就是單獨調(diào)用解耦分離。我采用的就是 retrofit2 + rexjava2 +ok3這樣的一個組合方式。Retrofit實質(zhì)上就是對okHttp的封裝,使用面向接口的方式進(jìn)行網(wǎng)絡(luò)請求欧募,利用動態(tài)生成的代理類封裝了網(wǎng)絡(luò)接口請求的底層,其將請求返回javaBean,對網(wǎng)絡(luò)認(rèn)證 REST API進(jìn)行了很好對支持仆抵。

  1. 關(guān)于Retrofit 大家可以看下官網(wǎng)的介紹跟继,A type-safe HTTP client for Android and Java。是一個Android和Java安全的httpclient镣丑。
  2. Retrofit2比Retrofit1在效率的提升是很高效的主要在硬性依賴和抽象舔糖。在Retrofit2中提供okhttp以及okio依賴,OkHttp的類型基本上已經(jīng)以更好更簡潔的 API 替代 Retrofit 1.0 的一些接口。好了暫時介紹這些莺匠,大家可以去看Jake Wharton的retrofit2的介紹以及工作原理金吗。

REST

REST(REpresentational State Transfer)指的是一組架構(gòu)約束條件和原則。滿足這些約束條件和原則的應(yīng)用程序或設(shè)計就是RESTful:

(1)資源(Resources)每一個URI代表一種資源趣竣;

(2)表現(xiàn)層(Representation)客戶端和服務(wù)器之間辽聊,傳遞這種資源的某種表現(xiàn)層;

(3)狀態(tài)轉(zhuǎn)化(State Transfer)客戶端通過四個HTTP動詞期贫,對服務(wù)器端資源進(jìn)行操作跟匆,實現(xiàn)"表現(xiàn)層狀態(tài)轉(zhuǎn)化"。

Retrofit2+Rexjava+Okhttp

在Android gradle中添加retrofithe rexjava okhttp添加依賴通砍。

`

implementation "io.reactivex.rxjava2:rxjava:2.1.1"http://RxJava2.0所需依賴
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'//Rxandroid2.0線程調(diào)度依賴
implementation 'com.squareup.retrofit2:retrofit:2.3.0'//Retrofit2.0所需依賴
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'//結(jié)果轉(zhuǎn)為實體類所需依賴
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'//OKHttp優(yōu)化策略依賴

`

OkHttpClientHelper

首先我們設(shè)計出okhttpclient對象為創(chuàng)建Retrofit提供關(guān)聯(lián)對象OkHttpClientHelper,還有緩存對象CacheHelper玛臂,這里只是貼出相關(guān)的代碼吧。

private OkHttpClientHelper() {
    cache = CacheHelper.getInstance().getCache();
}

 //單例模式
 public static OkHttpClientHelper getInstance() {
    if (clientHelper == null) {
        synchronized (OkHttpClientHelper.class) {
            if (clientHelper == null) {
                clientHelper = new OkHttpClientHelper();
            }
        }
    }
    return clientHelper;
}



//創(chuàng)建OKHttpClicent對象 配置header頭信息等
public OkHttpClient getOkHttpClient() {
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    if (mClient == null) {
        mClient = new OkHttpClient.Builder()
                .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                .cache(cache)      //設(shè)置緩存
                .addInterceptor(loggingInterceptor)     //配置攔截器log信息
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request()//配置添加header信息
                                .newBuilder()
                                .addHeader("source-terminal", "Android")  //操作系統(tǒng)名稱(ios,android)//設(shè)備型號
                                .addHeader("device-model", Build.MODEL)         //設(shè)備型號
                                .addHeader("os-version", Build.VERSION.RELEASE) //操作系統(tǒng)版本號
                                //添加cookie 這里需要全局觀察下用戶信息--比如cookie可以是uid也可以是usertoken等
                                .addHeader("Cookie", "add cookies here")  //這里可以添加cookie 也可以在設(shè)計的webview中設(shè)置cookie的存儲
                                .build();
                        if (API.mode == API.Mode.debug || API.mode == API.Mode.prerelease) {
                            Logger.e("header=>source-terminal", "Android--" + request.url());
                            Logger.e("header=>device-model", Build.MODEL);
                            Logger.e("header=>os-version", Build.VERSION.RELEASE);
                            Logger.e("header=>Cookie", "");
                        }
                        return chain.proceed(request);
                    }
                })
                .build();
    }
    return mClient;
}

相信在看這篇文章的朋友對okhttp應(yīng)該是很了解了封孙,那么就不多過解釋迹冤,創(chuàng)建okclient對象,設(shè)置緩存虎忌,設(shè)置超時讀寫時間泡徙,添加攔截器,添加請求頭信息膜蠢。返回的我們需要的client對象.

RetrofitHelper

上面有了okclient對象堪藐,現(xiàn)在我們可以設(shè)計我們的Retrofit實例,
名字就叫RetrofitHelper挑围。在構(gòu)造方法中我們就獲取到
okhttpclient對象礁竞,在OkHttpClientHelper的構(gòu)造方法中我們又
獲取到 CacheHelper的實例,這樣的好處方便擴展維護(hù)吧杉辙。

`
private RetrofitHelper() {
    mClient = OkHttpClientHelper.getInstance().getOkHttpClient();
}

//單例模式 對象唯一
public static RetrofitHelper getInstance() {
    if (helper == null) {
        synchronized (RetrofitHelper.class) {
            if (helper == null) {
                helper = new RetrofitHelper();
            }
        }
    }
    return helper;
}

`

現(xiàn)在我們需要創(chuàng)建Retrofit對象模捂,Retrofit2和1是有一些區(qū)別的。

具體的區(qū)別大家可以去看下square公司開發(fā)者作者Jake Wharton的介紹用 Retrofit 2 簡化 HTTP 請求)

`

//構(gòu)造Retrofit對象 設(shè)置基礎(chǔ)域名
public Retrofit getRetrofit() {
    if (mRetrofit == null) {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(API.DOMAIN)                                       //域名訪問地址 這里只是為了方便demo單獨寫一個,最好的方式是在builderconfig里面配置狂男,只要修改一下Build Varilant 就可以切換生產(chǎn)環(huán)境
                .addConverterFactory(ResponseConverterFactory.create())    // 轉(zhuǎn)換器 添加gson支持 在和后臺配合開發(fā)的過程中 設(shè)計返回數(shù)據(jù)模型解決解析異常
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())  //添加RxJava支持
                .client(mClient)                                           //關(guān)聯(lián)ok3 設(shè)置client
                .build();
    }
    return mRetrofit;
}

我們調(diào)用的方式就比較簡單了综看,直接設(shè)置service

//獲取服務(wù)service對象 對應(yīng)每一個接口都是一個微服務(wù)
public static <T> T getService(Class<T> classz) {
    return RetrofitHelper.getInstance()
            .getRetrofit()
            .create(classz);
}

`
在調(diào)用返回的地方設(shè)置T泛型方便我們使用;添加Rxjava支持,在使用RxJava過程中會發(fā)現(xiàn)RxJava2和RxJava1在語法上有一些區(qū)別,推薦閱讀學(xué)習(xí)這個Rexjavagithub;在添加Converter的時候(Convert objects to and from their representation in HTTP.)可以添加多種序列化Factory岖食,但是GsonConverterFactory必須放在最后,否則會拋出異常寓搬,我們配合后臺在開發(fā)的過程中通常都會設(shè)計好返回的數(shù)據(jù)規(guī)范格式。所以這里我們自己做一下數(shù)據(jù)的解析處理和加解密的操作县耽。這里就不多介紹了句喷,在demo中的我寫了三個類型,GSON FastJson 加解密JSON類型兔毙。有需要的朋友可以針對性看一下就可以了唾琼。

ApiService

 api服務(wù)注解方式 這個是官方給開發(fā)者學(xué)習(xí)使用的類型

方法注解包含 :
@GET、@POST澎剥、@PUT锡溯、@DELETE、
@PATH哑姚、@HEAD祭饭、@OPTIONS、@HTTP叙量。
標(biāo)記注解包含:
@FormUrlEncoded倡蝙、@Multipart、@Streaming绞佩。
參數(shù)注解包含:
@Query,@QueryMap寺鸥、@Body、@Field品山,@FieldMap胆建、@Part,@PartMap肘交。
其他注解包含:
@Path笆载、@Header,@Headers、@Url

 這里舉一個我們的例子
  @GET("/v1/resource/search/{searchname}?suggest=true")
Observable<SearchDataList.DataBean> getSearchVideo(@Path("searchname") String searchname, @Query("rank") String rank, @Query("size") String size);

NetWorkUtil

 網(wǎng)絡(luò)調(diào)用方法util,demo里我只寫了一中方法涯呻,最近工作比較忙凉驻,擴充類型等有時間再去弄了。rexjava2訂閱綁定設(shè)置io
 
 ‘
public class NetWorkUtil {
//對應(yīng)HTTP的狀態(tài)碼
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int REQUEST_TIMEOUT = 408;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;

//Post方式
public static <T> void requestPost(Observable observable, final OnResultListener resultListener) {

    setSubscriber(observable, new Observer<T>() {

        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onNext(T t) {
            if (resultListener != null) {//找一些接口試一下就好了
                resultListener.onSuccess(t);
            }
 //                if (t instanceof BaseResponseBean) {
 //                    //后臺可配置code碼 根據(jù)不同的code可以做相應(yīng)的操作 比如后臺強制拋出錯誤等等 這里我注釋掉魄懂,給大家個樣板而已
 //                    BaseResponseBean success = (BaseResponseBean) t;
 //                    if (success.code == 0) {
 //                        if (resultListener != null) {
 //                            resultListener.onSuccess(success.data);
 //                        }
 //                    } else {
 //                        if (resultListener != null) {
 //                            resultListener.onError(success.errormessage);
 //                        }
 //                    }
 //                } else {
 //                    if (resultListener != null) {
 //                        resultListener.onError("數(shù)據(jù)異常了");
 //                    }
 //                }
        }

        @Override
        public void onError(Throwable error) {
            if (error != null && resultListener != null) {
                resultListener.onError(error.getMessage());
            } else if (resultListener != null) {
                resultListener.onError("兄弟 網(wǎng)絡(luò)不給力啊");
                return;
            }
            String e = error.getMessage();
            if (error instanceof HttpException) {//HTTP錯誤
                HttpException httpException = (HttpException) error;
                switch (httpException.code()) {
                    case UNAUTHORIZED:
                    case FORBIDDEN:
                    case NOT_FOUND:
                    case REQUEST_TIMEOUT:
                    case GATEWAY_TIMEOUT:
                    case INTERNAL_SERVER_ERROR:
                    case BAD_GATEWAY:
                    case SERVICE_UNAVAILABLE:
                    default:
                        //Toast.makeText(App.getInstance(), "網(wǎng)絡(luò)異常", Toast.LENGTH_SHORT).show();
                        break;
                }
            } else if (error instanceof SocketTimeoutException) {
            } else if (error instanceof JsonParseException || error instanceof JSONException || error instanceof ParseException) {
            } else if (error instanceof ResultException) {//服務(wù)器返回的錯誤
            } else if (error instanceof ConnectException) {
            } else {//未知錯誤
            }
            resultListener.onError("兄弟 網(wǎng)絡(luò)不給力啊");
        }

        @Override
        public void onComplete() {
 //    Logger.d("request", "讀取完成");
        }
    });

}

//Get方式
public static void requestGet(Observable observable, final OnResultListener resultListener) {
    requestPost(observable, resultListener);
}

//訂閱事件
public static <T> void setSubscriber(Observable<T> observable, Observer<T> subscriber) {
    observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

//網(wǎng)絡(luò)訪問回調(diào)接口
public interface OnResultListener<T> {

    void onSuccess(T t);

    void onError(String msg);
}
}

 ’

可以看到我們在apiservice里面定義了一個服務(wù)(接口方法)返回Observable泛型內(nèi)容是我們想要的對象沿侈。在上面NetWorkUtil里面調(diào)用方法中,rexjava2的方法就大家自行學(xué)習(xí)了市栗。在這里面有共用的網(wǎng)絡(luò)錯誤類型。已經(jīng)在retrofit幫助類里面設(shè)置ConverterFactory里面定義解析失敗的類型。到此就全部描述完這個工作流程填帽。下面舉一個具體調(diào)用的示例蛛淋。(為什么沒有封裝成mvp模式呢,我覺得我們不是為了設(shè)計模式而設(shè)計模式篡腌,在部分需求以及開發(fā)中我覺得采用適當(dāng)?shù)脑O(shè)計模式有助于項目開發(fā)和維護(hù)提升性能褐荷,但是在單獨的網(wǎng)絡(luò)層中,我覺得自己做自己的事情更加解耦性)

`

public void getData() {
    //簡單好用嘹悼,看log就明白了
    Observable<SearchDataList.DataBean> searchVideo = RetrofitHelper.getService(ApiService.class).getSearchVideo("秋冬編發(fā)大全", "0", "20");
    //這個直接返回來數(shù)據(jù)對象
    NetWorkUtil.requestGet(searchVideo, new NetWorkUtil.OnResultListener() {
        @Override
        public void onSuccess(Object o) {
            SearchDataList.DataBean bean = (SearchDataList.DataBean) o;
            setdata(bean);
        }

        @Override
        public void onError(String msg) {

        }
    });
}

`

到這里就差不多介紹完了下面給出git鏈接 下載看這里 覺得還行記得star 感謝各位叛甫,語言組織不好還請諒解。杨伙。其监。各位看官小手抖一抖雙擊666

258387103fc76f4e361b43d2167db2e5.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市限匣,隨后出現(xiàn)的幾起案子抖苦,更是在濱河造成了極大的恐慌,老刑警劉巖米死,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锌历,死亡現(xiàn)場離奇詭異,居然都是意外死亡峦筒,警方通過查閱死者的電腦和手機究西,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來物喷,“玉大人怔揩,你說我怎么就攤上這事「浚” “怎么了商膊?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宠进。 經(jīng)常有香客問我晕拆,道長,這世上最難降的妖魔是什么材蹬? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任实幕,我火速辦了婚禮,結(jié)果婚禮上堤器,老公的妹妹穿的比我還像新娘昆庇。我一直安慰自己,他們只是感情好闸溃,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布整吆。 她就那樣靜靜地躺著拱撵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪表蝙。 梳的紋絲不亂的頭發(fā)上拴测,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音府蛇,去河邊找鬼集索。 笑死,一個胖子當(dāng)著我的面吹牛汇跨,可吹牛的內(nèi)容都是我干的务荆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼穷遂,長吁一口氣:“原來是場噩夢啊……” “哼函匕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起塞颁,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤浦箱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后祠锣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酷窥,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年伴网,在試婚紗的時候發(fā)現(xiàn)自己被綠了蓬推。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡澡腾,死狀恐怖沸伏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情动分,我是刑警寧澤毅糟,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站澜公,受9級特大地震影響姆另,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坟乾,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一迹辐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧甚侣,春花似錦明吩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽低葫。三九已至,卻和暖如春躏鱼,著一層夾襖步出監(jiān)牢的瞬間氮采,已是汗流浹背殷绍。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工染苛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人主到。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓茶行,卻偏偏與公主長得像,于是被迫代替她去往敵國和親登钥。 傳聞我的和親對象是個殘疾皇子畔师,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容