Retrofit + RxJava + OkHttp 的簡單封裝

第一次寫有點小激動 算是總結吧!

最近一直聽群友說Retrofit + Rxjava + OkHttp有多牛逼多好用多強大....讓我產生了很大興趣....然后去網絡上搜索了一翻....很多資料....但是對于基礎不是很好的我....很不友好....(資質太低不要嘲笑我)....看完基本還是沒明白....

首先推薦一篇看過覺得好的文章
給初學者的RxJava2.0教程 (作者Season_zlc)

閱讀以下內容 請先把Retrofit + RxJava + OkHttp先了解一下 這樣看下面的東西 才能對你理解起來有幫助

OkHttp

OkHttp是時下最火的HTTP請求框架指厌,別問我為什么诞丽,Google都放棄了httpclient,用OkHttp來替代他顷啼,你還要問么? so....學習一個新的請求框架荐虐,適應形勢七兜,還是非常有必要的!

Retrofit

Retrofit + OkHttp我之前學習的時候,看別人的代碼一直不知道Retrofit和OkHttp二者是有什么區(qū)別? 查閱資料后了解到福扬,OkHttp與HTTPClient腕铸、HTTPConnection是一個級別的,是系統(tǒng)提供的較為原裝的網絡請求庫铛碑。而Retrofit則是基于OkHttp的再次封裝過狠裹,與Volley這種已經被封裝過的網絡庫是一個級別的。其實就是套了一層把很多的坑都給你門填好了汽烦,而且最主要的是涛菠,做了很多安全上的改進,還有能更好的支持RxJava。

Rxjava

看完[Season_zlc]的講解后俗冻,讓我了解到了RxJava 是通過實現了線程調度器讓開發(fā)者更方便且自由的調度線程礁叔,被觀察者和訂閱者的方式和強大的操作符,還有引以為傲的鏈式調用迄薄,滿足者開發(fā)者各種需求琅关。

那為什么要RxJava + Retrofit + OkHttp要一起使用呢?

其實原因很簡單Retrofit用過的人都知道,Retrofit 負責請求讥蔽,OkHttp 負責請求過程死姚,Retrofit 和 OkHttp 2者結合都沒有實現結果成功后在UI線程回調,而是在IO線程回調的勤篮。那你肯定是不能更新控件,然道你要打算去寫handle來通知UI線程更新么色罚,你不覺得很low么? 而且代碼邏輯很亂么? 而使用Rxjava不僅讓你的代碼看起來更加優(yōu)雅易懂碰缔,并且Retrofit本身也是支持RxJava的。個人想法戳护,也許作者最初的目的本來就是為了和RxJava配套使用金抡。

以下代碼均取自Fazhi項目wanggang老師的教學代碼 最后我會提供代碼供大家下載 不用擔心!

一、首先我門定義API類

public interface Iapi {
    @POST("userLogin")
    Observable<BaseResponse<UserBean>> userLogin(@Body RequestBody params);
}

二腌且、初始化Retrofit + OkHttp的請求類 (已經按步驟在代碼里注釋好了)

這個類的代碼很明顯梗肝,我門先將繁瑣的Retrofit和OkHttp初始化,然后通過retrofit.create(Iapi.class)拿到最先定義的API類的實例類對象铺董,有了實例類對象后就可以將RequestBody請求體傳入發(fā)起請求了巫击。

public class NetRequest {

    private static final String TAG = "===NET REQUEST===";
    private static NetRequest instance;
    private OkHttpClient client;
    private Retrofit retrofit;
    private Iapi iapi;

    // 第一步 構造RequestBody請求體
    public static RequestBody generateReqBody(HashMap<String, Object> map){
        JSONObject params = new JSONObject();
        params.putAll(map);
        return RequestBody.create(NetConfig.TypeJSON, params.toJSONString());
    }

    // 第二步 構建單例模式
    public static NetRequest getInstance() {
        if (instance == null) {
            synchronized (NetRequest.class) {
                if (instance == null) {
                    instance = new NetRequest();
                }
            }
        }
        return instance;
    }

    // 第二步 1.獲得單例模式時會初始化該類 初始化時就構造好OkHttp和Retrofit2個對象 并且設置好參數
    public NetRequest() {
        client = getClient();
        retrofit = getRetrofit();
    }

    // 第二步 2.初始化OkHttp的參數 并拿到Client請求對象
    private OkHttpClient getClient() {
        // HttpLoggingInterceptor是Log攔截器
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.i(TAG, message);
            }
        });
        // 設置Log攔截器的攔截等級
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        return new OkHttpClient.Builder()
                               .addInterceptor(interceptor)// 攔截器設置給它
                               .connectTimeout(20, TimeUnit.SECONDS)// 請求超時時間
                               .readTimeout(20, TimeUnit.SECONDS)// 讀取超時時間
                               .writeTimeout(20, TimeUnit.SECONDS)// 寫入超時時間
                               .build();
    }

    // 第二步 3.初始化Retrofit的參數 并將Client設置給它
    private Retrofit getRetrofit() {
        return new Retrofit.Builder()
                           .client(client)// 傳入OKhttp的client對象
                           .addConverterFactory(GsonConverterFactory.create())// 添加gson解析的支持
                           .addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 添加支持observable類型
                           .baseUrl(NetConfig.BASE_URL)// 傳入URL
                           .build();
    }

    // 第四步 通過retrofit創(chuàng)建Iapi的實現類對象(retrofit已經幫你實現好了 不需要自己去實現 只要調用方法)
    // 待會model要用來調你寫好的Iapi里的接口 接口里定義好了方法為userLogin(放第一步獲取的請求體)
    public Iapi getApi() {
        if (iapi == null) {
            iapi = retrofit.create(Iapi.class);
        }
        return iapi;
    }
}

三、重寫了Subscriber訂閱者類

封裝了一些每次回調可以不變的代碼精续,并將onNext這個成功的回調丟給后面的繼承者
另外:
1.該類是一個抽象類坝锰,繼承Subscriber但并沒有重寫onNext()方法,因為后面要留給Presenter的實現類去回調成功后做各種操作重付,因此它并不屬于重復而又不變的代碼顷级,所以在該類里并沒有去實現它。
2.onError() 中我門做了統(tǒng)一的服務器錯誤代碼處理确垫,將錯誤代碼區(qū)分開來弓颈,這里分二種情況:

  1. 請求服務器失敗了,通常為網絡連接失敗404 500之類的
  2. 網絡通暢請求成功了删掀,但是給你返回了約定好的錯誤代碼翔冀,通常1000 1001之類的,比如帳號不存在 未注冊等等....
public abstract class NetSubscriber<T> extends Subscriber<T> {

    private BaseView view;

    @Override
    public void onStart() {
        super.onStart();
        view = getView();
        view.showLoading();
    }

    @Override
    public void onCompleted() {
        view.hideLoading();
    }

    @Override
    public void onError(Throwable e) {
        view.hideLoading();

        ExceptionHandle.ResponeThrowable throwable;
        //后臺邏輯執(zhí)行失敗披泪,拋出的異常
        if(e instanceof ExceptionHandle.ResponeThrowable){
            throwable = (ExceptionHandle.ResponeThrowable) e;
        } else {
            //網絡操作執(zhí)行的異常
            throwable = ExceptionHandle.handleException(e);
        }
        view.showError(throwable.message);
    }

    public abstract BaseView getView();
}

四橘蜜、封裝RxJava的請求參數

1.Presenter中RxJava每次都要寫初始化參數,比如設置請求時在IO線程,請求完后在UI線程计福。
2.設置攔截器好比Rxjava發(fā)了一個消息下來跌捆,中間被你攔截起來,按你設定的過濾規(guī)則決定讓消息走哪邊象颖。
例如我門下面代碼中佩厚,規(guī)則是:
如果服務器返回為true,我門放行讓訂閱者在onNext()接受到成功的json數據
如果服務器返回為false说订,我門讓他走另一條路exception抄瓦,讓他進入統(tǒng)一錯誤代碼處理中,然后回調訂閱者的onErrer()提示用戶陶冷,失敗了的原因钙姊。

public class RxHelper {

    // 設置請求時為IO線程,處理結果時在UI線程
    public static Observable.Transformer schedulers() {
        return new Observable.Transformer() {
            @Override
            public Object call(Object observable) {
                return ((Observable)observable)
                        .subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

    // 本方法為設置攔截器
    // 1.用了Map操作符 來攔截每一次的消息 過濾正確的放行給訂閱者 失敗的丟給錯誤碼統(tǒng)一處理類
    // 2.onErrorResumeNext設置如果是請求上的錯誤 比如網絡錯誤/連接超時之類的 直接丟給錯誤碼統(tǒng)一處理類
    public static <T> Observable.Transformer transform() {
        return new Observable.Transformer() {
            @Override
            public Object call(Object observable) {
                return ((Observable)observable)
                        .map(new TransformerFun())// 設置攔截服務器連接成功后返回的內容 然后篩選
                        .onErrorResumeNext(new ServerExceptionFun());// 設置請求直接失敗了的攔截
            }
        };
    }

    /**
     * 請求失敗 將錯誤代碼的回調轉接給統(tǒng)一處理類
     * @param <T>
     */
    static class ServerExceptionFun<T> implements Func1<Throwable,Observable<T>>{

        @Override
        public Observable<T> call(Throwable throwable) {
            return Observable.error(ExceptionHandle.handleException(throwable));
        }
    }

    /**
     * 攔截器的具體邏輯
     * @param <T>
     */
    static class TransformerFun<T> implements Func1<BaseResponse<T>,T>{

        @Override
        public T call(BaseResponse<T> baseResponse) {
            if(baseResponse.is_success()){
                return baseResponse.getData();// 正確返回json
            }
            // 如果不正確 丟給錯誤代碼統(tǒng)一處理類
            ExceptionHandle.ServerException exception = new ExceptionHandle.ServerException();
            exception.message = baseResponse.getError_content();
            throw exception;
        }
    }
}

五埂伦、錯誤代碼統(tǒng)一處理類

public class ExceptionHandle {
    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;

    public static ResponeThrowable handleException(Throwable e) {
        ResponeThrowable ex;
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            ex = new ResponeThrowable(e, ERROR.HTTP_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:
                    ex.message = "網絡錯誤";
                    break;
            }
            return ex;
        } else if (e instanceof ServerException) {
            ServerException resultException = (ServerException) e;
            ex = new ResponeThrowable(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
            ex.message = "解析錯誤";
            return ex;
        } else if (e instanceof ConnectException) {
            ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
            ex.message = "連接失敗";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
            ex.message = "證書驗證失敗";
            return ex;
        } else if (e instanceof ConnectTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "連接超時";
            return ex;
        } else if (e instanceof java.net.SocketTimeoutException) {
            ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
            ex.message = "連接超時";
            return ex;
        } else {
            ex = new ResponeThrowable(e, ERROR.UNKNOWN);
            ex.message = "未知錯誤";
            return ex;
        }
    }


    /**
     * 約定異常
     */
    class ERROR {
        /**
         * 未知錯誤
         */
        public static final int UNKNOWN = 1000;
        /**
         * 解析錯誤
         */
        public static final int PARSE_ERROR = 1001;
        /**
         * 網絡錯誤
         */
        public static final int NETWORD_ERROR = 1002;
        /**
         * 協議出錯
         */
        public static final int HTTP_ERROR = 1003;

        /**
         * 證書出錯
         */
        public static final int SSL_ERROR = 1005;

        /**
         * 連接超時
         */
        public static final int TIMEOUT_ERROR = 1006;
    }

    public static class ResponeThrowable extends Exception {
        public int code;
        public String message;

        public ResponeThrowable(Throwable throwable, int code) {
            super(throwable);
            this.code = code;

        }
    }

    public static class ServerException extends RuntimeException {
        public int code;
        public String message;
    }
}

六煞额、定義我門自己的BaseResponse成功響應的接收類

該類可有可無,主要用來替代Retrofit自帶的ResponseBody沾谜,因為我門做了過濾膊毁,要用到is_success,當然如果你不做攔截也可以用他原來的ResponseBody基跑。

public class BaseResponse<T> {

    private boolean is_success;
    private String error_content;
    private T data;

    public boolean is_success() {
        return is_success;
    }

    public void setIs_success(boolean is_success) {
        this.is_success = is_success;
    }

    public String getError_content() {
        return error_content;
    }

    public void setError_content(String error_content) {
        this.error_content = error_content;
    }

    public T getData() {
        return data;
    }

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

最后調用一下看看

HashMap<String, Object> map = new HashMap<>();
map.put("username", username);
map.put("password", passwrod);
RequestBody requestBody = NetRequest.generateReqBody(map);
Observable<BaseResponse<UserBean>> observable = NetRequest.getInstance().getApi() .userLogin(requestBody);
observable.compose(RxHelper.schedulers())
          .compose(RxHelper.transform())
          .subscribe(new NetSubscriber<UserBean>() {
               @Override
               public BaseView getView() { return view; }
               @Override
               public void onNext(UserBean userBean) {
                   // 成功的回調
               }
           });

對~老鐵....就是這樣....復制+粘貼....第一次....就這樣沒了

續(xù) 結合本封裝的MVP項目架構 讓你的Activity腰圍瘦下來

FaZhi項目框架 Git下載 (向作者wanggang老師致敬)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末婚温,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子媳否,更是在濱河造成了極大的恐慌栅螟,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篱竭,死亡現場離奇詭異嵌巷,居然都是意外死亡,警方通過查閱死者的電腦和手機室抽,發(fā)現死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門搪哪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坪圾,你說我怎么就攤上這事晓折。” “怎么了兽泄?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵漓概,是天一觀的道長。 經常有香客問我病梢,道長胃珍,這世上最難降的妖魔是什么梁肿? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮觅彰,結果婚禮上吩蔑,老公的妹妹穿的比我還像新娘。我一直安慰自己填抬,他們只是感情好烛芬,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著飒责,像睡著了一般赘娄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宏蛉,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天遣臼,我揣著相機與錄音,去河邊找鬼拾并。 笑死揍堰,一個胖子當著我的面吹牛,可吹牛的內容都是我干的辟灰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼篡石,長吁一口氣:“原來是場噩夢啊……” “哼芥喇!你這毒婦竟也來了?” 一聲冷哼從身側響起凰萨,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤继控,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胖眷,有當地人在樹林里發(fā)現了一具尸體武通,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年珊搀,在試婚紗的時候發(fā)現自己被綠了冶忱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡境析,死狀恐怖囚枪,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情劳淆,我是刑警寧澤链沼,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站沛鸵,受9級特大地震影響括勺,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一疾捍、第九天 我趴在偏房一處隱蔽的房頂上張望奈辰。 院中可真熱鬧,春花似錦拾氓、人聲如沸冯挎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽房官。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撮执,已是汗流浹背则吟。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜡峰,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓朗恳,卻偏偏與公主長得像湿颅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子粥诫,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容