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

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

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

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

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

好了啰啰嗦嗦的說了這么多廢話,下面進入正題!

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

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

寫這個大致分為以下幾步,先列出來后面會一步一步的講:

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

大致分為上面的幾個步驟,不要著急,容我倒杯茶慢慢道來...

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'

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

okhttp注釋下面的三個

前兩個不用多說,用過okhttp的自然都知道,需要注意的一點是第一個版本不能低于3.4.1,具體原因我也不是很清楚,有知道原因的煩請告訴我一聲.第三個顧名思義,是okhttp自己提供的log攔截器,方便我們在控制臺輸出okhttp信息

Retrofit2注釋下面的四個

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

RxJava注釋下面的兩個

第一個是RxJava 第二個是RxJava適配Android的

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

2. 封裝BaseActivity和App

因為我在項目里面多次用到Application的Context,以及我的工具類里面需要用到Context的地方也都是用的Application的,所以簡單寫了一下就是下面這個樣子的

public class App extends Application{

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

下面貼上我的T這個類,這個類主要是做一些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;
    }

    /**
     * 短時間顯示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é)點下面加入android:name=".App"這一句!

接下來是我自己的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();
    }
}

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

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

    @Override
    protected void initUIAndListener() {

    }

    @Override
    protected void initData() {

    }
}

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

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

說到攔截器,這個就不得不提一下okHttp的強大之處,此處的兩個攔截器一個攔截器是發(fā)送請求的時候的調(diào)用的,另一個是結(jié)果以jsonString返回回來的時候調(diào)用的,他們分別的用處我會在下面細講!先上代碼

RequestInterceptor
/**
 * 類名稱:請求前攔截器,這個攔截器會在okhttp請求之前攔截并做處理
 */
public class RequestInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        //請求定制:添加請求頭
        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的情況下,請求體定制:統(tǒng)一添加參數(shù),此處演示的是get請求,因此不做處理
        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請求的情況下在此處追加統(tǒng)一參數(shù)
//            String client = Constants.CONFIG_CLIENT;
//
//            newFormBody.add("client", client);

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

里面的注釋寫的個人覺得挺全的,有什么問題可以具體詳細再問!

ResponseInterceptor
/**
 * 結(jié)果攔截器,這個類的執(zhí)行時間是返回結(jié)果返回的時候,返回一個json的String,對里面一些特殊字符做處理
 * 主要用來處理一些后臺上會出現(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;
    }
}

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

bean包下HttpResult類

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

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

接下來看一下我針對這個接口做的HttpResult類

/**
 * Created by ziabo on 2017/5/9.
 * T就是傳遞過來的data的類型
 */

public class HttpResult<T> {

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

這個是要手寫的,下面這個是GsonFormat自動生成的,toString方法是我自己加進去的,方便打印.所有的變量我都聲明為public的方便存取.

/**
 * Created by ziabo on 2017/5/9.
 * 這個是實體類,里面只有我們關(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":"知識","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":"少食多餐在說什么丹允?","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":"健康運動踢毽子","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":"洗澡謹記五個不","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 : 知識
         * 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)我們使用的時候只需要這么拼,就是整個完整的JavaBean
HttpResult<DataBean>這些網(wǎng)上講的人很多,相信做過的肯定都了解,就不多說了!

api包下面的四個類(以及上面沒有說的HttpObserver)

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

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

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

}

不要嫌棄我畫工捉急,下面是一個圖解,簡單介紹一下


圖解

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

到這里也許有人會問,既然有了接口,那么接口的實現(xiàn)類呢?此處牽扯到Retrofit的原理,大家可以通過這個博客了解一二,四個字,動態(tài)代理!(其實我也不明所以....此處感謝博客的作者 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,我大致說一下吧<T>ObservableTransformer<T,T>這個是我們需要返回的類型,點擊去看源碼是這樣的ObservableTransformer<Upstream, Downstream>,返回的時候我們new一個ObservableTransformer的時候就會實現(xiàn)他內(nèi)部的apply方法,ObservableSource<Downstream> apply(Observable<Upstream> upstream);他所做的事情就是把我們輸入進來的Observable做了一些處理,具體做了哪些處理呢?我們具體看代碼,subscribeOn(Schedulers.io())這句話意思是在io線程建立連接(此處暫時用這個措辭,因為用訂閱老感覺不是很舒服),unsubscribeOn(Schedulers.io())這一句的意思是在io線程解除連接,observeOn(AndroidSchedulers.mainThread())這句話的意思是指定回調(diào)線程是Android的主線程,也就是我們常說的UI線程.

HttpResultFunc

這個類也是要針對接口進行編寫的

/**
 * Created by ziabo on 2017/5/9.
 * 類描述:用來統(tǒng)一處理Http的status,并將HttpResult的data部分剝離出來返回給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的時候是請求成功
            if (tHttpResult.msg!=null){//請求失敗的情況下吐司錯誤信息
                Toast.makeText(App.INSTANCE, tHttpResult.msg, Toast.LENGTH_SHORT).show();
            }
        }
        return tHttpResult.data;
    }
}

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

ApiService

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

/**
 * 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);
//      通過你當(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類是我們整個項目里面的常量池!此處我著重介紹一下我下面這個方法!

調(diào)用

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

HttpObserver

這個放在最后講,因為這個是整個框架成型的最后一步!不廢話,直接上代碼

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

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


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


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

    /**
     * 出現(xiàn)異常的時候會走這里,我們統(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);
        }
    }

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

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

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

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

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

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

最后再說兩句

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

在ApiInterface中添加接口具體如圖
接口
在ApiService中添加一個方法具體如圖
ApiService
調(diào)用的時候直接這么寫:
 private void getData() {
        Map<String,Object> map = new HashMap<>();
        map.put("currentPage",1);
        map.put("pageSize",20);
        //此處的new HttpObserver用了轉(zhuǎn)型,必須要這么寫,要不然里面是四個方法,而且我們自定義的HttpObserver也會顯得毫無用處
        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);
    }
    }

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坦辟,隨后出現(xiàn)的幾起案子刊侯,更是在濱河造成了極大的恐慌,老刑警劉巖锉走,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滨彻,死亡現(xiàn)場離奇詭異藕届,居然都是意外死亡,警方通過查閱死者的電腦和手機亭饵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門休偶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辜羊,你說我怎么就攤上這事踏兜。” “怎么了八秃?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵碱妆,是天一觀的道長。 經(jīng)常有香客問我昔驱,道長疹尾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任骤肛,我火速辦了婚禮纳本,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萌衬。我一直安慰自己饮醇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布秕豫。 她就那樣靜靜地躺著朴艰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪混移。 梳的紋絲不亂的頭發(fā)上祠墅,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音歌径,去河邊找鬼毁嗦。 笑死,一個胖子當(dāng)著我的面吹牛回铛,可吹牛的內(nèi)容都是我干的狗准。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼茵肃,長吁一口氣:“原來是場噩夢啊……” “哼腔长!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起验残,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤捞附,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸟召,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胆绊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了欧募。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片压状。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖槽片,靈堂內(nèi)的尸體忽然破棺而出何缓,到底是詐尸還是另有隱情,我是刑警寧澤还栓,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布碌廓,位于F島的核電站,受9級特大地震影響剩盒,放射性物質(zhì)發(fā)生泄漏谷婆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一辽聊、第九天 我趴在偏房一處隱蔽的房頂上張望纪挎。 院中可真熱鬧,春花似錦跟匆、人聲如沸异袄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烤蜕。三九已至,卻和暖如春迹冤,著一層夾襖步出監(jiān)牢的瞬間讽营,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工泡徙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留橱鹏,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓堪藐,卻偏偏與公主長得像莉兰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子礁竞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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