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

1eb0eafdd2bd3cdf9b50ce0c9895521b.jpg

前言

android時下最流行的網(wǎng)絡(luò)框架莫過于 retrofit + rexjava +ok3
本文記錄下我的網(wǎng)絡(luò)架構(gòu)轉(zhuǎn)型記錄。我項(xiàng)目里面采用的是OK3作網(wǎng)絡(luò)連接層,但是資源的下載我是用的rexjava+OK3單獨(dú)寫的一套下載util,想法就是單獨(dú)調(diào)用解耦分離。我采用的就是 retrofit2 + rexjava2 +ok3這樣的一個組合方式。Retrofit實(shí)質(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)行操作,實(shí)現(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)為實(shí)體類所需依賴
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實(shí)例,
名字就叫RetrofitHelper。在構(gòu)造方法中我們就獲取到
okhttpclient對象,在OkHttpClientHelper的構(gòu)造方法中我們又
獲取到 CacheHelper的實(shí)例,這樣的好處方便擴(kuò)展維護(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單獨(dú)寫一個,最好的方式是在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里我只寫了一中方法,最近工作比較忙,擴(kuò)充類型等有時間再去弄了。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)的操作 比如后臺強(qiá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è)計模式有助于項(xiàng)目開發(fā)和維護(hù)提升性能,但是在單獨(dú)的網(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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