Retrofit+RxJava 2.x 輕松實(shí)現(xiàn)app的網(wǎng)絡(luò)層

前言: 本人也是一個(gè)小菜鳥,寫這篇文章意在拋磚引玉,希望有的大神可以來(lái)看看我哪里有不足之處,幫我答疑解難!假如有剛接觸的小伙伴,也可以一起進(jìn)步......項(xiàng)目已經(jīng)上傳到github上面了,下載地址有興趣的可以來(lái)下載,歡迎issues

首先,看本博客之前你需要掌握以下技能:

1.你是一個(gè)Android開發(fā)工程師,且迫切希望改變自己項(xiàng)目里面的moudle層
2.你對(duì)java的解耦思想有一定了解,基礎(chǔ)相對(duì)較扎實(shí)
3.你要對(duì)Android的okHttp3有一定的了解,比如攔截器等...
4.對(duì)Retrofit有一定的了解,最起碼自己寫過(guò)Demo測(cè)試過(guò)
5.對(duì)java1.8的RetroLamada知道是什么
6.對(duì)RxJava有一定的了解,以及1.x升級(jí)到2.x做了什么改動(dòng)
7.對(duì)google的Gson熟練掌握
8.對(duì)以上我所說(shuō)的你確定你都達(dá)到了,當(dāng)然沒(méi)達(dá)到也沒(méi)關(guān)系,后面我會(huì)一點(diǎn)一點(diǎn)的講

首先先貼上一個(gè)maven倉(cāng)庫(kù)的地址,方便你查詢當(dāng)前maven倉(cāng)庫(kù)里面各種庫(kù)的最新版本.
然后是RxJava 的github地址俗話說(shuō)得好,任何不懂的問(wèn)題都可以通過(guò)查詢?cè)创a來(lái)解決,人家的注釋給你寫的很明白,英文水平?jīng)Q定了你的高度.
然后是Retrofit的github地址,個(gè)人一直比較喜歡Retrofit這個(gè)網(wǎng)絡(luò)加載框架,Retrofit的英文翻譯是改進(jìn),更新, 花樣翻新...我覺(jué)得他們起名字的時(shí)候更傾向的是第三種翻譯吧,哈哈...
還有okHttp的github地址,okHttp在HttpClient安卓棄用了以后(當(dāng)然也不能講棄用,是沒(méi)法用),是Android開發(fā)中的一個(gè)利器,簡(jiǎn)潔方便,但是就是使用原生的話有點(diǎn)費(fèi)勁,搭配Retrofit以后可以說(shuō)是如虎添翼.

好了啰啰嗦嗦的說(shuō)了這么多廢話,下面進(jìn)入正題!

首先是我的項(xiàng)目結(jié)構(gòu)圖

項(xiàng)目結(jié)構(gòu)

寫這個(gè)大致分為以下幾步,先列出來(lái)后面會(huì)一步一步的講:

  • 1 依賴倒入
  • 2 封裝BaseActivity和App(其實(shí)這一步每個(gè)人有每個(gè)人的想法,我只是建議這樣寫,不足之處望指出)
  • 3 net包下兩個(gè)攔截器以及自定義Observer
  • 4 bean包下HttpResult類(針對(duì)自己的接口編寫)
  • 5 api包下面的四個(gè)類

大致分為上面的幾個(gè)步驟,不要著急,容我倒杯茶慢慢道來(lái)...

1. 依賴導(dǎo)入

    //okhttp
    compile 'com.squareup.okhttp3:okhttp:3.7.0'
    compile 'com.squareup.okio:okio:1.12.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.7.0'
    //gson
    compile 'com.google.code.gson:gson:2.8.0'
    //retrofit2
    compile 'com.squareup.retrofit2:retrofit:2.2.0'
    compile 'com.squareup.retrofit2:converter-gson:2.2.0'
    compile 'com.squareup.retrofit2:converter-scalars:2.2.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
    //rxJava
    compile'io.reactivex.rxjava2:rxjava:2.0.1'
    compile'io.reactivex.rxjava2:rxandroid:2.0.1'

下面我依次介紹一下上面幾個(gè)依賴的具體用處

okhttp注釋下面的三個(gè)

前兩個(gè)不用多說(shuō),用過(guò)okhttp的自然都知道,需要注意的一點(diǎn)是第一個(gè)版本不能低于3.4.1,具體原因我也不是很清楚,有知道原因的煩請(qǐng)告訴我一聲.第三個(gè)顧名思義,是okhttp自己提供的log攔截器,方便我們?cè)诳刂婆_(tái)輸出okhttp信息

Retrofit2注釋下面的四個(gè)

第一個(gè)是不必多說(shuō),第二個(gè)作用是讓Retrofit支持gson,添加這個(gè)以后可以直接結(jié)果出來(lái)就生成我們想要的JavaBean.第三個(gè), <strong>A Converter which supports converting strings and both primitives and their boxed types to text/plain bodies.</strong>源碼里面是這么介紹的,意思自己理解,我就不關(guān)公門前耍大刀了,畢竟自己是四級(jí)425分的渣渣...最后一個(gè)是Retrofit適配RxJava2必須要添加的

RxJava注釋下面的兩個(gè)

第一個(gè)是RxJava 第二個(gè)是RxJava適配Android的

注:gson注釋下面的就不用我廢話了吧

2. 封裝BaseActivity和App

因?yàn)槲以陧?xiàng)目里面多次用到Application的Context,以及我的工具類里面需要用到Context的地方也都是用的Application的,所以簡(jiǎn)單寫了一下就是下面這個(gè)樣子的

public class App extends Application{

    public static Application INSTANCE;
    @Override
    public void onCreate() {
        super.onCreate();
        INSTANCE = this;
        T.register(this);
        NetUtils.register(this);
    }
}

下面貼上我的T這個(gè)類,這個(gè)類主要是做一些Toast的工作

/**
 * Toast統(tǒng)一管理類
 */
public class T {
    public static Context mContext;
    private static Toast toast;


    private T() {
        /* cannot be instantiated */
        throw new UnsupportedOperationException("cannot be instantiated");
    }


    public static void register(Context context) {
        mContext = context;
    }

    /**
     * 短時(shí)間顯示Toast
     */
    public static void showShort(CharSequence message) {
        if (mContext==null){
            throw new RuntimeException("unRegister Context in Application");
        }
        if (toast != null) {
            toast.cancel();
        }
        toast = Toast.makeText(mContext, message, Toast.LENGTH_LONG);
        toast.setText(message);
        toast.show();
    }

    public static void showShort(int resId) {
        if (mContext==null){
            throw new RuntimeException("unRegister Context in Application");
        }
        if (toast != null) {
            toast.cancel();
        }
        toast = Toast.makeText(mContext, mContext.getString(resId), Toast.LENGTH_LONG);
        toast.setText(mContext.getString(resId));
        toast.show();
    }

}

友情提示:自定義App以后不要忘了在Manifest.xml的application節(jié)點(diǎn)下面加入android:name=".App"這一句!

接下來(lái)是我自己的BaseActivity

/**
 * Created by ziabo on 2017/5/9.
 * Activity的Base類
 */

public abstract class BaseActivity extends AppCompatActivity{

    private CompositeDisposable mCompositeDisposable;
    private ApiService mApiService;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (mApiService == null){
            mApiService = ApiService.getApiService();
        }
        setContentView(initContentView());
        initUIAndListener();
        initData();
    }

    /**
     * 設(shè)置layout
     */
    protected abstract int initContentView();

    /**
     * 初始化UI和Listener
     */
    protected abstract void initUIAndListener();

    /**
     * 初始化數(shù)據(jù)
     */
    protected abstract void initData();

    /**
     * 管理所有建立的鏈接,在onDestroy中清空 mCompositeDisposable
     */
    protected void addDisposable(Disposable disposable){
        if (mCompositeDisposable==null){
            mCompositeDisposable = new CompositeDisposable();
        }
        mCompositeDisposable.add(disposable);
    }

    @Override
    protected void onDestroy() {
        if (mCompositeDisposable != null){
            mCompositeDisposable.clear();
        }
        super.onDestroy();
    }
}

里面有些看不懂的地方不要著急,可以先就看我那三個(gè)protected abstract的方法就好,這三個(gè)是強(qiáng)制要子類實(shí)現(xiàn)的,我們的Activity寫出來(lái)的時(shí)候就是下面這個(gè)樣子,比較簡(jiǎn)潔明了..

public class TestActivity extends BaseActivity{
    @Override
    public int initContentView() {
        return 0;//此處放上你的Layout的id
    }

    @Override
    protected void initUIAndListener() {

    }

    @Override
    protected void initData() {

    }
}

注: 一定要注意方法的先后執(zhí)行順序!

3. net包下兩個(gè)攔截器以及自定義Observer

說(shuō)到攔截器,這個(gè)就不得不提一下okHttp的強(qiáng)大之處,此處的兩個(gè)攔截器一個(gè)攔截器是發(fā)送請(qǐng)求的時(shí)候的調(diào)用的,另一個(gè)是結(jié)果以jsonString返回回來(lái)的時(shí)候調(diào)用的,他們分別的用處我會(huì)在下面細(xì)講!先上代碼

RequestInterceptor
/**
 * 類名稱:請(qǐng)求前攔截器,這個(gè)攔截器會(huì)在okhttp請(qǐng)求之前攔截并做處理
 */
public class RequestInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        //請(qǐng)求定制:添加請(qǐng)求頭
        Request.Builder requestBuilder = original
                .newBuilder()
                .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        //設(shè)置cookie
//        String cookie= App.getCookie();
//        if (StringUtil.checkStr(cookie)) {             //cookie判空檢查
//            requestBuilder.addHeader("Cookie", cookie);
//        }

        //如果是post的情況下,請(qǐng)求體定制:統(tǒng)一添加參數(shù),此處演示的是get請(qǐng)求,因此不做處理
        if (original.body() instanceof FormBody) {
            FormBody.Builder newFormBody = new FormBody.Builder();
            FormBody oidFormBody = (FormBody) original.body();
            for (int i = 0; i < oidFormBody.size(); i++) {
                newFormBody.addEncoded(oidFormBody.encodedName(i), oidFormBody.encodedValue(i));
            }
        //當(dāng)post請(qǐng)求的情況下在此處追加統(tǒng)一參數(shù)
//            String client = Constants.CONFIG_CLIENT;
//
//            newFormBody.add("client", client);

            requestBuilder.method(original.method(), newFormBody.build());
        }
        return chain.proceed(requestBuilder.build());
    }
}

里面的注釋寫的個(gè)人覺(jué)得挺全的,有什么問(wèn)題可以具體詳細(xì)再問(wèn)!

ResponseInterceptor
/**
 * 結(jié)果攔截器,這個(gè)類的執(zhí)行時(shí)間是返回結(jié)果返回的時(shí)候,返回一個(gè)json的String,對(duì)里面一些特殊字符做處理
 * 主要用來(lái)處理一些后臺(tái)上會(huì)出現(xiàn)的bug,比如下面聲明的這三種情況下統(tǒng)一替換為:null
 */
public class ResponseInterceptor implements Interceptor {
    private String emptyString = ":\"\"";
    private String emptyObject = ":{}";
    private String emptyArray = ":[]";
    private String newChars = ":null";

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        Response response = chain.proceed(request);
        ResponseBody responseBody = response.body();
        if (responseBody != null) {
            String json = responseBody.string();
            MediaType contentType = responseBody.contentType();
            if (!json.contains(emptyString)) {
                ResponseBody body = ResponseBody.create(contentType, json);
                return response.newBuilder().body(body).build();
            } else {
                String replace = json.replace(emptyString, newChars);
                String replace1 = replace.replace(emptyObject, newChars);
                String replace2 = replace1.replace(emptyArray, newChars);
                ResponseBody body = ResponseBody.create(contentType, replace2);
                return response.newBuilder().body(body).build();
            }
        }
        return response;
    }
}

這個(gè)注釋好像也挺全的,哈哈...
HttpObserver我放到講api包的時(shí)候再講!!!!

bean包下HttpResult類

一般來(lái)講,我們的接口請(qǐng)求下來(lái)的結(jié)構(gòu)大致都是這樣的

{
    "code":"noError",
    "data":{
        "banner":Array[3]
    },
    "msg":"",
    "result":true
}

接下來(lái)看一下我針對(duì)這個(gè)接口做的HttpResult類

/**
 * Created by ziabo on 2017/5/9.
 * T就是傳遞過(guò)來(lái)的data的類型
 */

public class HttpResult<T> {

    public String code;
    public String msg;
    public boolean result;
    public T data;
}

這個(gè)是要手寫的,下面這個(gè)是GsonFormat自動(dòng)生成的,toString方法是我自己加進(jìn)去的,方便打印.所有的變量我都聲明為public的方便存取.

/**
 * Created by ziabo on 2017/5/9.
 * 這個(gè)是實(shí)體類,里面只有我們關(guān)注的數(shù)據(jù),其他的都統(tǒng)一處理
 */

public class DataBean {

    /**
     * nextPage : 1
     * count : 6
     * pageSize : 20
     * prevPage : 1
     * currentPage : 1
     * pageNum : 1
     * healthInfo : [{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/0394155040297634.png","formatCreateDate":"03-30","formatUpdateDate":"04-01","index":1,"updateTime":"2017-04-01 16:01:44","title":"茶的物極必反","accountId":"4028817d549332dd015494a0edd80000","subTitle":"知識(shí)","createTime":"2017-03-30 16:19:34","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b180e49015b1e4c604800ad","account":"zkrj"},{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/1143420851997416.jpeg","formatCreateDate":"03-30","formatUpdateDate":"04-01","index":2,"updateTime":"2017-04-01 15:59:24","title":"少食多餐在說(shuō)什么?","accountId":"4028817d549332dd015494a0edd80000","subTitle":"糖尿病","createTime":"2017-03-30 16:12:03","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b180e49015b1e4581a100aa","account":"zkrj"},{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/7490881972133794.jpeg","formatCreateDate":"03-30","formatUpdateDate":"04-01","index":3,"updateTime":"2017-04-01 15:56:22","title":"健康運(yùn)動(dòng)踢毽子","accountId":"4028817d549332dd015494a0edd80000","subTitle":"高血壓","createTime":"2017-03-30 16:10:58","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b180e49015b1e4483c400a8","account":"zkrj"},{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/8457604241319624.jpeg","formatCreateDate":"03-30","formatUpdateDate":"04-01","index":4,"updateTime":"2017-04-01 15:54:19","title":"糖尿病病足的定義與預(yù)防","accountId":"4028817d549332dd015494a0edd80000","subTitle":"糖尿病","createTime":"2017-03-30 11:59:50","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b180e49015b1d5e9897009a","account":"zkrj"},{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/4638045747699966.jpeg","formatCreateDate":"03-27","formatUpdateDate":"04-01","index":5,"updateTime":"2017-04-01 15:50:28","title":"日常小事才不是小事","accountId":"4028817d549332dd015494a0edd80000","subTitle":"高血壓","createTime":"2017-03-27 11:13:05","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b09d7e0015b0dc0b4750005","account":"zkrj"},{"img":"http://test2.mb.zkrj.com/wlstatic/image/2017-04-01/9660687163661661.jpeg","formatCreateDate":"03-27","formatUpdateDate":"04-01","index":6,"updateTime":"2017-04-01 14:57:04","title":"洗澡謹(jǐn)記五個(gè)不","accountId":"4028817d549332dd015494a0edd80000","subTitle":"糖尿病","createTime":"2017-03-27 11:10:10","publish":true,"adminAccountId":"4028817d549332dd015494a0edd80000","orders":1,"id":"8a9a35085b09d7e0015b0dbe08e60003","account":"zkrj"}]
     */

    public int nextPage;
    public int count;
    public int pageSize;
    public int prevPage;
    public int currentPage;
    public int pageNum;
    public List<HealthInfoBean> healthInfo;

    public static class HealthInfoBean {
        /**
         * img : http://test2.mb.zkrj.com/wlstatic/image/2017-03-30/0394155040297634.png
         * formatCreateDate : 03-30
         * formatUpdateDate : 04-01
         * index : 1
         * updateTime : 2017-04-01 16:01:44
         * title : 茶的物極必反
         * accountId : 4028817d549332dd015494a0edd80000
         * subTitle : 知識(shí)
         * createTime : 2017-03-30 16:19:34
         * publish : true
         * adminAccountId : 4028817d549332dd015494a0edd80000
         * orders : 1
         * id : 8a9a35085b180e49015b1e4c604800ad
         * account : zkrj
         */

        public String img;
        public String formatCreateDate;
        public String formatUpdateDate;
        public int index;
        public String updateTime;
        public String title;
        public String accountId;
        public String subTitle;
        public String createTime;
        public boolean publish;
        public String adminAccountId;
        public int orders;
        public String id;
        public String account;

        @Override
        public String toString() {
            return "HealthInfoBean{" +
                    "img='" + img + '\'' +
                    ", formatCreateDate='" + formatCreateDate + '\'' +
                    ", formatUpdateDate='" + formatUpdateDate + '\'' +
                    ", index=" + index +
                    ", updateTime='" + updateTime + '\'' +
                    ", title='" + title + '\'' +
                    ", accountId='" + accountId + '\'' +
                    ", subTitle='" + subTitle + '\'' +
                    ", createTime='" + createTime + '\'' +
                    ", publish=" + publish +
                    ", adminAccountId='" + adminAccountId + '\'' +
                    ", orders=" + orders +
                    ", id='" + id + '\'' +
                    ", account='" + account + '\'' +
                    '}';
        }
    }

    @Override
    public String toString() {
        return "DataBean{" +
                "nextPage=" + nextPage +
                ", count=" + count +
                ", pageSize=" + pageSize +
                ", prevPage=" + prevPage +
                ", currentPage=" + currentPage +
                ", pageNum=" + pageNum +
                ", healthInfo=" + healthInfo +
                '}';
    }
}

當(dāng)我們使用的時(shí)候只需要這么拼,就是整個(gè)完整的JavaBean
HttpResult<DataBean>這些網(wǎng)上講的人很多,相信做過(guò)的肯定都了解,就不多說(shuō)了!

api包下面的四個(gè)類(以及上面沒(méi)有說(shuō)的HttpObserver)

這里面是整個(gè)部分的核心,相對(duì)而言也比較難理解,但是理解了以后就會(huì)發(fā)現(xiàn)豁然開朗!

首先是ApiInterface
/**
 * Created by ziabo on 2017/5/9.
 * 不懂的地方可以仔細(xì)研究Retrofit
 */
public interface ApiInterface {

    /**
     * 獲取健康信息
     */
    @GET("/rest/app/healthInfo")
    Observable<HttpResult<DataBean>> healthInfo(@QueryMap Map<String, Object> map);

}

不要嫌棄我畫工捉急,下面是一個(gè)圖解,簡(jiǎn)單介紹一下


圖解

1 規(guī)定了當(dāng)前的請(qǐng)求方式是GET請(qǐng)求(因?yàn)槲覍?shí)在沒(méi)有一個(gè)合適的接口,此處用的是我一個(gè)朋友友情提供的接口,非常感謝)
2 填的是你除了BaseUrl之外的部分
3 此處是與原生的Retrofit區(qū)別比較大的一點(diǎn),也是精髓之處,對(duì)RxJava有了解的肯定知道Observable是個(gè)什么東西,這里就不多解釋
4 同上,里面為什么這么寫上面也已經(jīng)解釋過(guò)了
5 Retrofit在GET請(qǐng)求時(shí)規(guī)定的,不多解釋
6 這個(gè)map的key是String,value是Object,你可以放任意數(shù)據(jù)類型

到這里也許有人會(huì)問(wèn),既然有了接口,那么接口的實(shí)現(xiàn)類呢?此處牽扯到Retrofit的原理,大家可以通過(guò)這個(gè)博客了解一二,四個(gè)字,動(dòng)態(tài)代理!(其實(shí)我也不明所以....此處感謝博客的作者 Alexclin ,讓我省了不少力,哈哈)

SchedulersTransformer

顧名思義:調(diào)度器,上完代碼再解釋,此處和RxJava 1.x有很大的不同

/**
 * Created by ziabo on 2017/5/9.
 * 線程調(diào)度器
 */

public class SchedulersTransformer{

    public static <T>ObservableTransformer<T,T> io_main(){
        return upstream ->
                upstream.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }
}

由于代碼有部分Lamada,我大致說(shuō)一下吧<T>ObservableTransformer<T,T>這個(gè)是我們需要返回的類型,點(diǎn)擊去看源碼是這樣的ObservableTransformer<Upstream, Downstream>,返回的時(shí)候我們new一個(gè)ObservableTransformer的時(shí)候就會(huì)實(shí)現(xiàn)他內(nèi)部的apply方法,ObservableSource<Downstream> apply(Observable<Upstream> upstream);他所做的事情就是把我們輸入進(jìn)來(lái)的Observable做了一些處理,具體做了哪些處理呢?我們具體看代碼,subscribeOn(Schedulers.io())這句話意思是在io線程建立連接(此處暫時(shí)用這個(gè)措辭,因?yàn)橛糜嗛喞细杏X(jué)不是很舒服),unsubscribeOn(Schedulers.io())這一句的意思是在io線程解除連接,observeOn(AndroidSchedulers.mainThread())這句話的意思是指定回調(diào)線程是Android的主線程,也就是我們常說(shuō)的UI線程.

HttpResultFunc

這個(gè)類也是要針對(duì)接口進(jìn)行編寫的

/**
 * Created by ziabo on 2017/5/9.
 * 類描述:用來(lái)統(tǒng)一處理Http的status,并將HttpResult的data部分剝離出來(lái)返回給subscriber
 * @param <T> data部分的數(shù)據(jù)模型
 */

public class HttpResultFunc<T> implements Function<HttpResult<T>,T>{

    @Override
    public T apply(HttpResult<T> tHttpResult) throws Exception {
        if (!tHttpResult.result){//假設(shè)當(dāng)結(jié)果為true的時(shí)候是請(qǐng)求成功
            if (tHttpResult.msg!=null){//請(qǐng)求失敗的情況下吐司錯(cuò)誤信息
                Toast.makeText(App.INSTANCE, tHttpResult.msg, Toast.LENGTH_SHORT).show();
            }
        }
        return tHttpResult.data;
    }
}

這個(gè)是在RxJava的map操作符里面放的,作用就是剝離公共區(qū)域的數(shù)據(jù)做處理,并且把數(shù)據(jù)轉(zhuǎn)換成我們想要的類型DataBean,這樣在成功的回調(diào)中就只有我們真正關(guān)心的數(shù)據(jù),其他的一些問(wèn)題都被統(tǒng)一處理了,具體看注釋,我大致是這么寫的,你也可以根據(jù)自己的業(yè)務(wù)邏輯做自己的操作!

ApiService

這個(gè)類就是所有準(zhǔn)備工作完了之后的最最核心的地方了,看到這里也許你累了,此時(shí)你可以起來(lái)活動(dòng)活動(dòng),喝杯咖啡打足精神,整理整理思路之后再來(lái)看這里!

/**
 * Created by ziabo on 2017/5/9.
 * ApiService
 */

public class ApiService {

    private ApiInterface mApiInterface;

    private ApiService() {
        //HTTP log
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        //RequestInterceptor
        RequestInterceptor requestInterceptor = new RequestInterceptor();

        //ResponseInterceptor
        ResponseInterceptor responseInterceptor = new ResponseInterceptor();

        //OkHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .addInterceptor(requestInterceptor)
                .addInterceptor(responseInterceptor);
//      通過(guò)你當(dāng)前的控制debug的全局常量控制是否打log
        if (Constants.DEBUG_MODE) {
            builder.addInterceptor(httpLoggingInterceptor);
        }
        OkHttpClient mOkHttpClient = builder.build();

        //Retrofit
        Retrofit mRetrofit = new Retrofit.Builder()
                .client(mOkHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl("http://test2.mb.zkrj.com/")//替換為你自己的BaseUrl
                .build();

        mApiInterface = mRetrofit.create(ApiInterface.class);
    }

    //單例
    private static class SingletonHolder {
        private static final ApiService INSTANCE = new ApiService();
    }

    //單例
    public static ApiService getApiService() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 獲取健康信息
     */
    public void get_health(Observer<DataBean> observer, Map<String, Object> map) {
        mApiInterface.healthInfo(map)
                .compose(SchedulersTransformer.io_main())
                .map(new HttpResultFunc<>())
                .subscribe(observer);
    }

}

里面把我們之前所有準(zhǔn)備的東西都用上了,Constants類是我們整個(gè)項(xiàng)目里面的常量池!此處我著重介紹一下我下面這個(gè)方法!

調(diào)用

這次箭頭是不是比上面好看了?哈哈
1 這個(gè)是RxJava 2.x中的觀察者,類似于1.x的SubScriber,有興趣的點(diǎn)進(jìn)去看看就知道了
2 ApiInterface接口的方法的調(diào)用
3 compose操作符,百度一搜一大堆,不多解釋,概念1.x和2.x基本沒(méi)有什么變化
4 map操作符,同上
5 建立連接

HttpObserver

這個(gè)放在最后講,因?yàn)檫@個(gè)是整個(gè)框架成型的最后一步!不廢話,直接上代碼

/**
 * Created by ziabo on 2017/5/9.
 * 結(jié)果回調(diào)回來(lái)之后的接口的實(shí)現(xiàn)類
 * 有興趣的話可以翻閱這里 http://reactivex.io/documentation/observable.html
 */

public abstract class HttpObserver<R> implements Observer<R> {


    /**
     * 建立鏈接的時(shí)候調(diào)用并生成Disposable對(duì)象,此處相當(dāng)于1.x的onStart()方法我做了如下處理
     * 有更好建議的可以私聊我,或者評(píng)論
     * @param d 鏈接狀態(tài)對(duì)象
     */
    @Override
    public void onSubscribe(Disposable d) {
        if (!NetUtils.isConnected()) {
            if (d!=null && !d.isDisposed()){
                d.dispose();
            }
            T.showShort("請(qǐng)檢查網(wǎng)絡(luò)連接后重試!");
            onFinished();
        }else{
            getDisposable(d);
        }
    }


    /**
     * 此處和1.x的onNext()基本沒(méi)有什么變化,所以我選擇注釋,讓實(shí)現(xiàn)類自己處理
     * 之前我是寫了的,看過(guò)這篇博客的應(yīng)該有印象
     * @param r 返回的結(jié)果,沒(méi)網(wǎng)絡(luò)時(shí)提示
     */
//    @Override
//    public void onNext(R r) {
//        onSuccess(r);
//    }
//
//    public abstract void onSuccess(R r);

    /**
     * 出現(xiàn)異常的時(shí)候會(huì)走這里,我們統(tǒng)一放在 onFinished();處理
     */
    @Override
    public void onError(Throwable e) {
        onFinished();
        if (e instanceof HttpException || e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof TimeoutException){
            onNetworkException(e);
        }else {
            onUnknownException(e);
        }
    }

    /**
     * 不管成功與失敗,這里都會(huì)走一次,所以加onFinished();方法
     */
    @Override
    public void onComplete() {
        onFinished();
    }

    /**
     * 請(qǐng)求結(jié)束之后的回調(diào),無(wú)論成功還是失敗,此處一般無(wú)邏輯代碼,經(jīng)常用來(lái)寫ProgressBar的dismiss
     */
    public abstract void onFinished();

    /**
     * 向子類暴露 Disposable
     */
    public abstract void getDisposable(Disposable disposable);

    private void onNetworkException(Throwable e) {
        e.printStackTrace();
        T.showShort("獲取數(shù)據(jù)失敗,請(qǐng)檢查網(wǎng)絡(luò)狀態(tài)");
    }

    private void onUnknownException(Throwable e) {
        e.printStackTrace();
    }
}

注釋寫的很詳細(xì)了,還有很多需要完善的地方,希望大家看到的能夠提出寶貴的意見(jiàn)!

最后再說(shuō)兩句

這個(gè)庫(kù)是我一個(gè)朋友(流風(fēng)夜雪)自己研究出來(lái)的,我只是在此基礎(chǔ)上做了RxJava部分的升級(jí),關(guān)于RxJava2.x還有很多很強(qiáng)大的地方我還沒(méi)有用到,有思路的希望給我提一下,在這里先說(shuō)聲謝謝!
這個(gè)庫(kù)的好處就是其他地方你都定好了以后,一個(gè)新的api,只要知道數(shù)據(jù)結(jié)構(gòu),就只需要做以下兩步:

在ApiInterface中添加接口具體如圖
接口
在ApiService中添加一個(gè)方法具體如圖
ApiService
調(diào)用的時(shí)候直接這么寫:
 private void getData() {
        Map<String,Object> map = new HashMap<>();
        map.put("currentPage",1);
        map.put("pageSize",20);
        //此處的new HttpObserver用了轉(zhuǎn)型,必須要這么寫,要不然里面是四個(gè)方法,而且我們自定義的HttpObserver也會(huì)顯得毫無(wú)用處
        ApiService.getApiService().get_health(new HttpObserver<DataBean>() {
            @Override
            public void onNext(DataBean dataBean) {
                T.showShort(dataBean.toString());
                Log.d("MainActivity", dataBean.toString());
            }

            @Override
            public void onFinished() {
                //不做任何處理
            }

            @Override
            public void getDisposable(Disposable disposable) {
                addDisposable(disposable);
            }
        },map);
    }
    }

后言:歷時(shí)兩天,這個(gè)博客終于完結(jié)了!希望大家不要吝嗇起碼給個(gè)贊吧,就沖我這排版看起來(lái)還不錯(cuò)呀!
項(xiàng)目源碼已經(jīng)上傳到github,我最近也會(huì)不斷做更新,歡迎吐槽!

此處只寫了BaseAvtivity,然而我們實(shí)際開發(fā)中不可能不會(huì)用到Fragmrnt,而且RxJava是一個(gè)容易造成內(nèi)存泄漏的庫(kù),接下來(lái)的一段時(shí)間我會(huì)研究一下rxlifecycle2.x 然后做一個(gè)防止內(nèi)存泄漏的較完善的東西!謝謝觀看

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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