封裝Retrofit2+RxJava2網(wǎng)絡(luò)請求框架

2021年1月18號補(bǔ)充
最近遇到一個5.0以下的設(shè)備無法請求問題,主要的原因都是Okhttp4不支持5.0以下的設(shè)備猖辫,這邊我們需要做下兼容酥泞,不過我的設(shè)想是通過Gradle去做兼容,暫時還沒有弄出來啃憎≈ザ冢可以看下這個博客:
https://blog.csdn.net/mr_tony/article/details/108776208
2021年8月19號

這個博客寫了很久了,陸續(xù)還有人在點(diǎn)贊羡藐,我還是比較高興的仆嗦,個人覺得ResponseTransformer那一塊代碼可能有點(diǎn)晦澀難懂瘩扼,有些讀者也問過我這個變化的問題邢隧,如果參考我的代碼覺得可以優(yōu)化掉那一塊代碼我覺得也行倒慧,畢竟代碼寫得簡潔和可維護(hù)最好纫谅。ResposeTransformer那邊實(shí)際上用的是Rxjava的一種變換付秕,主要是我好久不用Rxjava了询吴,突然看了下這塊代碼猛计,自己也有點(diǎn)懵逼奉瘤,不過代碼是可以跑通的盗温,而且也解釋了這個變換卖局,我覺得如果Rxjava懂的人應(yīng)該能看懂砚偶。謝謝大家支持蟹演。

因為Retrofit方便使用和支持RxJava酒请,所以Retrofit已經(jīng)成為了非常流行的網(wǎng)絡(luò)框架羞反。學(xué)會封裝和使用Retrofit網(wǎng)絡(luò)請求框架練練手是提升自己架構(gòu)水平一個非常好的示例昼窗。而且當(dāng)成功封裝第一個組件時澄惊,再次遇到需要封裝組件這樣的任務(wù)也會變得得心應(yīng)手掸驱。

1毕贼、封裝的主要邏輯

主要的邏輯

根據(jù)這個邏輯圖一步一步封裝網(wǎng)絡(luò)框架吧鬼癣。

1.1 導(dǎo)入依賴
    compile "io.reactivex.rxjava2:rxjava:2.1.0" // 必要rxjava2依賴
    compile "io.reactivex.rxjava2:rxandroid:2.0.1" // 必要rxandrroid依賴待秃,切線程時需要用到
    compile 'com.squareup.retrofit2:retrofit:2.3.0' // 必要retrofit依賴
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // 必要依賴锥余,和Rxjava結(jié)合必須用到驱犹,下面會提到
    compile 'com.squareup.retrofit2:converter-gson:2.3.0' // 必要依賴雄驹,解析json字符所用
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' //非必要依賴医舆, log依賴蔬将,如果需要打印OkHttpLog需要添加
1.2 新建Manger類霞怀,管理所需要的API

這里是一個千篇一律的套路,如果封裝全局使用的框架廉沮,并且需要和整個軟件有一樣長的生命周期滞时。那就有兩個特點(diǎn):

  • 在Application里面初始化坪稽。
  • 使用單例模式窒百。
  • 在一個"管理類"中初始化所需要的所有參數(shù)贝咙。
/**
 * Created by Zaifeng on 2018/2/28.
 * API初始化類
 */
public class NetWorkManager {

    private static NetWorkManager mInstance;
    private static Retrofit retrofit;
    private static volatile Request request = null;

    public static NetWorkManager getInstance() {
        if (mInstance == null) {
            synchronized (NetWorkManager.class) {
                if (mInstance == null) {
                    mInstance = new NetWorkManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化必要對象和參數(shù)
     */
    public void init() {
        // 初始化okhttp
        OkHttpClient client = new OkHttpClient.Builder()
                .build();

        // 初始化Retrofit
        retrofit = new Retrofit.Builder()
                .client(client)
                .baseUrl(Request.HOST)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    public static Request getRequest() {
        if (request == null) {
            synchronized (Request.class) {
                request = retrofit.create(Request.class);
            }
        }
        return request;
    }
}

這里新建了NetWorkManager 在init方法中初始化了OkHttp和Retrofit。兩者的初始化都是構(gòu)造者模式陈症。

初始化OKHttp的拓展
上面的代碼只是添加了必要的屬性趴腋,還可以對OkHttp和Retrofit添加更多的拓展论咏。比如:需要對OkHttp添加Log可以如下寫厅贪。

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(logging)
                    .build();

addInterceptor()還有很多的用法葵硕,比如:封裝一些公共的參數(shù)等等。參考如下博客:http://blog.csdn.net/jdsjlzx/article/details/52063950
這里就不多說明蜀变。

初始化Retrofit的時候的兩個必要配置:

  • 第一個配置:.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    這個是用來決定你的返回值是Observable還是Call库北。
    // 使用call的情況
    Call<String> login();  

    // 使用Observable的情況
    Observable<String> login();  

如果返回為Call那么可以不添加這個配置贤惯。如果使用Observable那就必須添加這個配置棒掠。否則就會請求的時候就會報錯烟很!
對于這個配置雾袱,詳細(xì)可以看下這篇博客芹橡。
http://blog.csdn.net/new_abc/article/details/53021387

  • 第二個配置:.addConverterFactory(GsonConverterFactory.create())
    這個配置是將服務(wù)器返回的json字符串轉(zhuǎn)化為對象林说。這個是可以自定義Converter來應(yīng)對服務(wù)器返回的不同的數(shù)據(jù)的腿箩。具體如何去自定義劣摇,這里也不做說明了(因為內(nèi)容比較多)钧惧,可以看下如下的博客浓瞪。
    http://www.reibang.com/p/5b8b1062866b

初始化Request
這里也初始化了Request追逮。如果有Retrofit基礎(chǔ)看到retrofit.create這個方法钮孵,就應(yīng)該知道Request是定義的請求的服務(wù)器的API封裝類巴席。里面通過注解的方式聲明所需要請求的接口漾唉,這里下面會講到分衫。

1.3 約定Response

在解析服務(wù)器返回的數(shù)據(jù)時般此,需要和服務(wù)器統(tǒng)一返回Json格式邀桑,這個在開發(fā)中經(jīng)常遇到科乎。一般服務(wù)器也不會隨便返回Json格式給前端捏萍,否則前端解析時將會非常麻煩玉吁。(所以這里的數(shù)據(jù)結(jié)構(gòu)需要前端和服務(wù)端約定一個固定的格式)
這里的固定json格式是:

{ret:0,data:"",msg:""}

所以這里定義的固定的Response為:

public class Response<T> {

    private int ret; // 返回的code
    private T data; // 具體的數(shù)據(jù)結(jié)果
    private String msg; // message 可用來返回接口的說明

    public int getCode() {
        return ret;
    }

    public void setCode(int code) {
        this.ret = code;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
1.4 定義Request

在前面NetWorkManager中已經(jīng)提到了这揣,這個是定義的請求服務(wù)器的API的接口。

public interface Request {

    // 填上需要訪問的服務(wù)器地址
    public static String HOST = "https://www.xxx.com/app_v5/";
    
    @POST("?service=sser.getList")
    Observable<Response<List<javaBean>>> getList(@Query("id") String id);
}

這里用@Post注解了一個Post請求片迅。

1.5 定義ResponseTransformer處理數(shù)據(jù)和異常

這個也是自己定義的類柑蛇,不使用這個自定義的類也是可以的耻台。但是為了封裝之后使用的便捷性蹋砚,還是建議封裝坝咐。ResponseTransformer實(shí)際上是Rxjava中的“變換”的封裝墨坚。
沒使用ResponseTransformer時的情況浦楣。

 model.getCarList("xxxxx")
                .compose(schedulerProvider.applySchedulers())
                .subscribe(new Consumer<Response<List<JavaBean>>>() {
                    @Override
                    public void accept(Response<List<JavaBean>> listResponse) throws Exception {
                        if(listResponse.getCode() == 200){
                            List<JavaBean> javaBeans = listResponse.getData();
                        }else{
                            // 異常處理
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                            // 異常處理
                    }
                });

使用了ResponseTransformer的情況:

 model.getCarList("xxxxx")
                .compose(ResponseTransformer.handleResult())
                .compose(schedulerProvider.applySchedulers())
                .subscribe(new Consumer<List<JavaBean>>() {
                               @Override
                               public void accept(List<JavaBean> carBeans) throws Exception {
                                   // 處理數(shù)據(jù) 直接獲取到List<JavaBean> carBeans
                               }
                           }, new Consumer<Throwable>() {
                               @Override
                               public void accept(Throwable throwable) throws Exception {
                                   // 處理異常
                               }
                           }

                );

沒有使用ResponseTransformer振劳,需要先判斷Response的code寸癌,然后將數(shù)據(jù)從Response中取出來蒸苇。比較繁瑣的就是判斷code==200的代碼吮旅,每次請求接口都需要去寫一遍溪烤。
使用了ResponseTransformer將解析的Response數(shù)據(jù)進(jìn)一步處理(將判斷code==200的代碼進(jìn)行封裝),直接將需要使用的數(shù)據(jù)處理提取出來庇勃,并且將Exception統(tǒng)一處理檬嘀!這個就是RxJava中“變換”的妙用,同時在使用時會和“compose”聯(lián)用责嚷,減少重復(fù)代碼鸳兽。
具體的ResponseTransformer代碼。

public class ResponseTransformer {

    public static <T> ObservableTransformer<Response<T>, T> handleResult() {
        return upstream -> upstream
                .onErrorResumeNext(new ErrorResumeFunction<>())
                .flatMap(new ResponseFunction<>());
    }


    /**
     * 非服務(wù)器產(chǎn)生的異常罕拂,比如本地?zé)o無網(wǎng)絡(luò)請求揍异,Json數(shù)據(jù)解析錯誤等等全陨。
     *
     * @param <T>
     */
    private static class ErrorResumeFunction<T> implements Function<Throwable, ObservableSource<? extends Response<T>>> {

        @Override
        public ObservableSource<? extends Response<T>> apply(Throwable throwable) throws Exception {
            return Observable.error(CustomException.handleException(throwable));
        }
    }

    /**
     * 服務(wù)其返回的數(shù)據(jù)解析
     * 正常服務(wù)器返回數(shù)據(jù)和服務(wù)器可能返回的exception
     *
     * @param <T>
     */
    private static class ResponseFunction<T> implements Function<Response<T>, ObservableSource<T>> {

        @Override
        public ObservableSource<T> apply(Response<T> tResponse) throws Exception {
            int code = tResponse.getCode();
            String message = tResponse.getMsg();
            if (code == 200) {
                return Observable.just(tResponse.getData());
            } else {
                return Observable.error(new ApiException(code, message));
            }
        }
    }
}

1.6渡处、 處理Exception

這里的Exception分為兩部分:

  • 自己本地產(chǎn)生的Exception,比如解析錯誤,網(wǎng)絡(luò)鏈接錯誤等等。
  • 服務(wù)器產(chǎn)生的Excption,比如404,503等等服務(wù)器返回的Excption。

定義ApiException統(tǒng)一處理:

public class ApiException extends Exception {
    private int code;
    private String displayMessage;

    public ApiException(int code, String displayMessage) {
        this.code = code;
        this.displayMessage = displayMessage;
    }

    public ApiException(int code, String message, String displayMessage) {
        super(message);
        this.code = code;
        this.displayMessage = displayMessage;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getDisplayMessage() {
        return displayMessage;
    }

    public void setDisplayMessage(String displayMessage) {
        this.displayMessage = displayMessage;
    }
}

對于本地產(chǎn)生的Exception如下處理:

public class CustomException {

    /**
     * 未知錯誤
     */
    public static final int UNKNOWN = 1000;

    /**
     * 解析錯誤
     */
    public static final int PARSE_ERROR = 1001;

    /**
     * 網(wǎng)絡(luò)錯誤
     */
    public static final int NETWORK_ERROR = 1002;

    /**
     * 協(xié)議錯誤
     */
    public static final int HTTP_ERROR = 1003;

    public static ApiException handleException(Throwable e) {
        ApiException ex;
        if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            //解析錯誤
            ex = new ApiException(PARSE_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof ConnectException) {
            //網(wǎng)絡(luò)錯誤
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) {
            //連接錯誤
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else {
            //未知錯誤
            ex = new ApiException(UNKNOWN, e.getMessage());
            return ex;
        }
    }
}

這些異常如何拋出呢 改含? 那就看下上面提到的 ResponseTransformer 中處理異常的代碼吧。兩種異常都通過Observable.error發(fā)射出去盗扇。

// 本地異常處理
Observable.error(CustomException.handleException(throwable));

// 對服務(wù)器放回的code進(jìn)行判斷
 int code = tResponse.getCode();
 String message = tResponse.getMsg();
 if (code == 200) {       
     return Observable.just(tResponse.getData());
 } else {
     return Observable.error(new ApiException(code, message));
  }
1.7斑鼻、線程切換

使用過Rxjava對Rxjava線程切換應(yīng)該比較熟悉荒叶,這里將線程切換單獨(dú)封裝成一個類再結(jié)合compose使用。

public class SchedulerProvider implements BaseSchedulerProvider {

    @Nullable
    private static SchedulerProvider INSTANCE;

    // Prevent direct instantiation.
    private SchedulerProvider() {
    }

    public static synchronized SchedulerProvider getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SchedulerProvider();
        }
        return INSTANCE;
    }

    @Override
    @NonNull
    public Scheduler computation() {
        return Schedulers.computation();
    }

    @Override
    @NonNull
    public Scheduler io() {
        return Schedulers.io();
    }

    @Override
    @NonNull
    public Scheduler ui() {
        return AndroidSchedulers.mainThread();
    }

    @NonNull
    @Override
    public <T> ObservableTransformer<T, T> applySchedulers() {
        return observable -> observable.subscribeOn(io())
                .observeOn(ui());
    }
}

2、實(shí)戰(zhàn)

封裝好之后,接下來就是實(shí)際使用了葬项。不過在實(shí)際開發(fā)中可能是先實(shí)現(xiàn)邏輯再去封裝,很多東西需要在代碼寫完之后才知道要去封裝起來的。

2.1、初始化

BaseApplication中初始化摊唇。

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        NetWorkManager.getInstance().init();
    }
}
2.2岛请、搭建好MVP結(jié)構(gòu)缩膝,在Presenter中使用。
    Disposable disposable = model.getCarList("xxxxxx")
                .compose(ResponseTransformer.handleResult())
                .compose(schedulerProvider.applySchedulers())
                .subscribe(carBeans -> {
                    // 處理數(shù)據(jù) 直接獲取到List<JavaBean> carBeans
                    view.getDataSuccess();
                }, throwable -> {
                    // 處理異常
                    view.getDataFail();
                });

        mDisposable.add(disposable);

兩個回調(diào)井厌,回調(diào)之后View層做出相應(yīng)的UI變化即可拳魁。
這里用了compose的使用就不多做解釋授舟。這里compose結(jié)合線程的切換封裝和變換封裝奢啥,減少了冗余代碼柬姚。
關(guān)于compose的鏈接:http://www.reibang.com/p/3d0bd54834b0
最后的目錄結(jié)構(gòu):

目錄結(jié)構(gòu)

3、需要注意的地方

1昧绣、很多時候需要實(shí)現(xiàn)效果才能再去進(jìn)行封裝税灌。
2狸窘、在封裝框架的時候封裝的是邏輯層牛哺,不要在邏輯層中寫業(yè)務(wù)層的代碼淳附。

源碼地址:https://github.com/AxeChen/retrofit2_rxjava2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炉菲,一起剝皮案震驚了整個濱河市越驻,隨后出現(xiàn)的幾起案子履澳,更是在濱河造成了極大的恐慌骇两,老刑警劉巖速种,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異低千,居然都是意外死亡配阵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門示血,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棋傍,“玉大人,你說我怎么就攤上這事难审∩嵘常” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵剔宪,是天一觀的道長。 經(jīng)常有香客問我,道長葱绒,這世上最難降的妖魔是什么感帅? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮地淀,結(jié)果婚禮上失球,老公的妹妹穿的比我還像新娘。我一直安慰自己帮毁,他們只是感情好实苞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烈疚,像睡著了一般黔牵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爷肝,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天猾浦,我揣著相機(jī)與錄音,去河邊找鬼灯抛。 笑死金赦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的对嚼。 我是一名探鬼主播夹抗,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纵竖!你這毒婦竟也來了漠烧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤磨确,失蹤者是張志新(化名)和其女友劉穎沽甥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乏奥,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摆舟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了邓了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恨诱。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骗炉,靈堂內(nèi)的尸體忽然破棺而出照宝,到底是詐尸還是另有隱情,我是刑警寧澤句葵,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布厕鹃,位于F島的核電站兢仰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剂碴。R本人自食惡果不足惜把将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忆矛。 院中可真熱鬧察蹲,春花似錦、人聲如沸催训。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漫拭。三九已至亚兄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫂侍,已是汗流浹背儿捧。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挑宠,地道東北人菲盾。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像各淀,于是被迫代替她去往敵國和親懒鉴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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