Retrofit+RxJava2的封裝使用

公司上個項目用的是rxjava1.0,最近看了各種rxjava2.0的介紹块请,自己也摸索著來宵统,所以打算封裝一下优妙,用到項目中子巾,與時俱進嘛胚宦!先不多逼逼了脊凰,直截了當,上貨......
先看一下關(guān)于怎么配置Retrofit箭券,這個大家都差不多净捅,就不多解釋了。辩块。蛔六。base_url直接換成自己公司服務(wù)器的地址就可以了
github地址:https://github.com/Veken/RxJava2Retrofit
1 封裝

Retrofit配置類 
/**
 * Retrofit配置
 */
public class RetrofitConnect {
    private Retrofit retrofit;
    private Service service;
    /**
     * 網(wǎng)絡(luò)請求超時時間毫秒
     */
    int DEFAULT_TIMEOUT = 20000;

    /**
     *   你們公司自己的服務(wù)器地址
     */
    public static String BASE_URL = "";

    private RetrofitConnect() {
       /* HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);*/

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                try {
                    String text = URLDecoder.decode(message, "utf-8");
                    LogUtils.e("OKHttp-----", text);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    LogUtils.e("OKHttp-----", message);
                }
            }
        });
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        File cacheFile = new File(Utils.getContext().getCacheDir(), "cache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
                .addInterceptor(interceptor)
                .addNetworkInterceptor(new HttpCacheInterceptor())
                .cache(cache)
                .build();

        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();

        retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
        service = retrofit.create(Service.class);
    }

    //  創(chuàng)建單例
    private static class SingletonHolder {
        private static final RetrofitConnect INSTANCE = new RetrofitConnect();
    }
    public static Service getApiService() {
        return SingletonHolder.INSTANCE.service;
    }

    class HttpCacheInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!NetworkUtils.isConnected()) {  //沒網(wǎng)強制從緩存讀取
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
                LogUtils.d("Okhttp", "no network");
            }


            Response originalResponse = chain.proceed(request);
            if (NetworkUtils.isConnected()) {
                //有網(wǎng)的時候讀接口上的@Headers里的配置,你可以在這里進行統(tǒng)一的設(shè)置
                String cacheControl = request.cacheControl().toString();
                return originalResponse.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
            } else {
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                        .removeHeader("Pragma")
                        .build();
            }
        }
    }
}

接下來第一步封裝废亭,把請求的一些網(wǎng)絡(luò)線程等等封裝在一個類SubScriberHandler里面
并且把post請求的參數(shù)進行sha加密国章,參數(shù)可以根據(jù)公司自己的要求,自己更改

public class SubScriberHandler {

    /**
     * @param o
     * @param <T>
     */
    public <T> void toSubscribe(Observable<T> o, DefaultObserver observer) {
        o.subscribeOn(Schedulers.io())      //網(wǎng)絡(luò)耗時操作在io線程處理
                .unsubscribeOn(Schedulers.io()) //網(wǎng)絡(luò)耗時操作在io線程處理
                .observeOn(AndroidSchedulers.mainThread())  //更新數(shù)據(jù)在主線程
                .subscribe(observer);
    }

    /**
     * 添加共同參數(shù) SHA1加密
     * @param fields
     */
    public void handleFields(Map<String, Object> fields) {
        fields.put("appKey", "00001");
        fields.put("v", "1.0");
        String sessionId = SpConfig.getInstance().getString(Constants.SESSIONID_STRING);
        if(!TextUtils.isEmpty(sessionId))
            fields.put(Constants.SESSIONID_STRING, sessionId);

        String sha1 = null;
        try {
            sha1 = SHA1.SHA1(fields);
        } catch (DigestException e) {
            e.printStackTrace();
        }
        fields.put("sign", sha1);
    }
}

接下來是關(guān)于自定義的實現(xiàn)Observer的類DefalutObserver豆村,同時也添加了一個加載數(shù)據(jù)時候的progressdialog液兽,可以根據(jù)公司的需求更改成自己的progressdialog,我這只是簡單的調(diào)用普通的。各位客官也可以自己看心情來封裝

**
 * @author Veken
 */
public abstract class DefaultObserver<T extends BaseRespond> implements Observer<T>,ProgressCancelListener {
    private Context context;
    private boolean isAddInStop = false;
    private ProgressDialogHandler mProgressDialogHandler;
    //取消訂閱
    private Disposable mDisposable;

    public DefaultObserver(Context context, boolean isShowLoading) {
        this.context = context;
        mProgressDialogHandler = new ProgressDialogHandler(context,this,true);
        if (isShowLoading) {
            showProgressDialog();
        }
    }

    private void showProgressDialog(){
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
        }
    }

    private void dismissProgressDialog(){
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
            mProgressDialogHandler = null;
        }
    }

    @Override
    public void onSubscribe(Disposable d) {
        mDisposable =d;
    }

    @Override
    public void onNext(T response) {
        dismissProgressDialog();
        if (response.getResCode().equals("200")) {
            onSuccess(response);
        } else {
            onFail(response);
        }
    }


    @Override
    public void onError(Throwable e) {
        LogUtils.e("Retrofit", e.getMessage());
//        dismissProgress();
        dismissProgressDialog();
        if (e instanceof HttpException) {     //   HTTP錯誤
            onException(ExceptionReason.BAD_NETWORK);
        } else if (e instanceof ConnectException
                || e instanceof UnknownHostException) {   //   連接錯誤
            onException(ExceptionReason.CONNECT_ERROR);
        } else if (e instanceof InterruptedIOException) {   //  連接超時
            onException(ExceptionReason.CONNECT_TIMEOUT);
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {   //  解析錯誤
            onException(ExceptionReason.PARSE_ERROR);
        } else {
            onException(ExceptionReason.UNKNOWN_ERROR);
        }
    }

    @Override
    public void onComplete() {
        dismissProgressDialog();
    }

    /**
     * 請求成功
     *
     * @param response 服務(wù)器返回的數(shù)據(jù)
     */
    abstract public void onSuccess(T response);

    /**
     * 服務(wù)器返回數(shù)據(jù)四啰,但響應碼不為200
     *
     * @param response 服務(wù)器返回的數(shù)據(jù)
     */
    public void onFail(T response) {
        String message = response.getResDesc();
        if (TextUtils.isEmpty(message)) {
            ToastUtils.show(R.string.response_return_error);
        } else {
            ToastUtils.show(message);
        }
    }

    /**
     * 請求異常
     *
     * @param reason
     */
    public void onException(ExceptionReason reason) {
        switch (reason) {
            case CONNECT_ERROR:
                ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT);
                break;

            case CONNECT_TIMEOUT:
                ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT);
                break;

            case BAD_NETWORK:
                ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT);
                break;

            case PARSE_ERROR:
                ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT);
                break;

            case UNKNOWN_ERROR:
            default:
                ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT);
                break;
        }
    }

    /**
     * 請求網(wǎng)絡(luò)失敗原因
     */
    public enum ExceptionReason {
        /**
         * 解析數(shù)據(jù)失敗
         */
        PARSE_ERROR,
        /**
         * 網(wǎng)絡(luò)問題
         */
        BAD_NETWORK,
        /**
         * 連接錯誤
         */
        CONNECT_ERROR,
        /**
         * 連接超時
         */
        CONNECT_TIMEOUT,
        /**
         * 未知錯誤
         */
        UNKNOWN_ERROR,
    }

    /**
     * 取消ProgressDialog的時候宁玫,取消對observable的訂閱,同時也取消了http請求
     */
    @Override
    public void onCancelProgress() {
        if (!mDisposable.isDisposed()) {
            mDisposable.dispose();
        }
    }
}

現(xiàn)在就是大家耳熟能詳?shù)腟ervice類柑晒,這就沒什么可說的欧瘪,你要什么請求,就是什么匙赞,比如你要登錄就寫一個login的service佛掖,然后Map中裝你需要請求的參數(shù),以此類推涌庭。

/**
 * @author Veken
 */
public interface Service {


    /**
     * 登錄的service
     * @param fields
     * @return
     */
    @FormUrlEncoded
    @POST(Constants.URLEND)
    Observable<LoginRespond> login(@FieldMap Map<String, Object> fields);

}

現(xiàn)在到了正兒八經(jīng)的封裝了芥被,前面的都是開胃菜,配置都差不多坐榆,大同小異撕彤,到上真貨的時候了

public class UserInfoRequest extends SubScriberHandler {

    private UserInfoBean userInfoBean;
    private Context context;

    public UserInfoRequest(Context context,UserInfoBean userInfoBean) {
        this.context = context;
        this.userInfoBean = userInfoBean;
    }

    public void login() {

        Map<String, Object> fields = new HashMap<>();
        //調(diào)用的接口方法,比如login方法
        fields.put("method", "user.login");
        //加密和傳一些常用參數(shù)
        handleFields(fields);
        //需要傳遞的參數(shù)
        fields.put("phoneNo", userInfoBean.getPhoneNum());
        fields.put("password", userInfoBean.getPwd());
        //申請網(wǎng)絡(luò)
        Observable observable = RetrofitConnect.getApiService().login(fields);
        toSubscribe(observable, new DefaultObserver(context,true) {
            //數(shù)據(jù)返回在onNext
            @Override
            public void onSuccess(BaseRespond response) {
            }

           //數(shù)據(jù)返回
            @Override
            public void onNext(@NonNull BaseRespond response) {
                //將結(jié)果封裝在javabean的onSuccess方法里面
                userInfoBean.onSuccess(response);
            }

            @Override
            public void onError(Throwable e) {
                super.onError(e);
                userInfoBean.onError(e);
            }
        });
    }

}

2 調(diào)用
下面就是具體調(diào)用了猛拴,看一下在MainActivity中怎么簡單的調(diào)用吧
實現(xiàn)UserInfoBean,這樣可以在重寫的方法中傳入我們需要的參數(shù)蚀狰,當然也可以不寫一個bean愉昆,直接用String傳也可以,但是習慣了bean麻蹋,數(shù)據(jù)處理也在bean中跛溉,簡單明了。
初始化你的UserInfoPresent網(wǎng)絡(luò)請求
private UserInfoRequest userInfoRequest;
userInfoRequest= new UserInfoRequest(this,this);
如果沒有實現(xiàn)bean的話扮授,UserInfoPresent()方法中的參數(shù)會報錯芳室,如果不想調(diào)用bean,可以自己簡單的修改一下
剩下的就簡單了刹勃,在自己想要請求網(wǎng)絡(luò)的地方堪侯,調(diào)用 userInfoPresent.login();一句代碼就可以了

public class MainActivity extends BaseActivity implements UserInfoBean {
    private Button btn;
    private UserInfoRequest userInfoRequest;
    private TextView tv;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initData(Bundle savedInstanceState) {
    }

    @Override
    protected void initView() {
        btn = (Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        userInfoRequest= new UserInfoRequest(this,this);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userInfoRequest.login();
            }
        });
    }

    //傳遞的用戶
    @Override
    public String getPhoneNum() {
        //根據(jù)需要,傳遞相應的數(shù)據(jù)
        return "登錄賬號";
    }

    //傳遞的密碼
    @Override
    public String getPwd() {
        //根據(jù)需要荔仁,傳遞相應的數(shù)據(jù)
        return "登錄密碼";
    }

    //數(shù)據(jù)返回
    @Override
    public void onSuccess(Object object) {
        LoginRespond loginRespond = (LoginRespond) object;
        Log.d("登錄信息:", loginRespond.getResDesc());
        tv.setText(loginRespond.getData().getTelphone());
    }

    @Override
    public void onError(Throwable e) {

    }
}

想必有老鐵就問了伍宦,如果一個界面有多處需要請求網(wǎng)絡(luò)的,那該怎么辦呢乏梁?
因為我們是通過實現(xiàn)javabean的方法次洼,java是單繼承,多實現(xiàn)嗎遇骑。這不就清楚了
當然你還可以直接使用

 Map<String, Object> fields = new HashMap<>();
        //調(diào)用的接口方法卖毁,比如login方法
        fields.put("method", "user.login");
        //加密和傳一些常用參數(shù)
        handleFields(fields);
        //傳遞需要傳遞的參數(shù)
        fields.put("phoneNo", userInfoBean.getPhoneNum());
        fields.put("password", userInfoBean.getPwd());
        //申請網(wǎng)絡(luò)
        Observable observable = RetrofitConnect.getApiService().login(fields);
        toSubscribe(observable, new DefaultObserver(context,true) {
            //數(shù)據(jù)返回在onNext
            @Override
            public void onSuccess(BaseRespond response) {
            }

            @Override
            public void onNext(@NonNull BaseRespond response) {
                userInfoBean.onSuccess(response);
            }

            @Override
            public void onError(Throwable e) {
                super.onError(e);
                userInfoBean.onError(e);
            }
        });

初始化UserInfoRequest,就可以了落萎。
還有很多不足的地方亥啦,希望各位老爺批評指正炭剪,大家共同進步,自己項目實戰(zhàn)過的禁悠,不是demo念祭,所以可以直接拿來用,有什么問題碍侦,還煩請指教粱坤、、瓷产、
PS:沒有用到太多的RxJava的操作符站玄,剛接觸,還不是很熟悉濒旦,還不會靈活運用株旷,見諒!6恕晾剖!
在這里要感謝那些為我們分享貢獻的前輩,感謝他們的辛勤耕耘梯嗽,才有我們菜鳥的不斷進步齿尽。
特此感謝:
用水管講解rxjava和retroft使用的:http://www.reibang.com/p/464fa025229e
以及這位前輩提供的demo參考:https://github.com/zhpanvip/Retrofit2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灯节,隨后出現(xiàn)的幾起案子循头,更是在濱河造成了極大的恐慌,老刑警劉巖炎疆,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卡骂,死亡現(xiàn)場離奇詭異,居然都是意外死亡形入,警方通過查閱死者的電腦和手機全跨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亿遂,“玉大人螟蒸,你說我怎么就攤上這事”谰颍” “怎么了七嫌?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長苞慢。 經(jīng)常有香客問我诵原,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任绍赛,我火速辦了婚禮蔓纠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吗蚌。我一直安慰自己腿倚,他們只是感情好,可當我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布蚯妇。 她就那樣靜靜地躺著敷燎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪箩言。 梳的紋絲不亂的頭發(fā)上硬贯,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天,我揣著相機與錄音陨收,去河邊找鬼饭豹。 笑死,一個胖子當著我的面吹牛务漩,可吹牛的內(nèi)容都是我干的拄衰。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼饵骨,長吁一口氣:“原來是場噩夢啊……” “哼翘悉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宏悦,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎包吝,沒想到半個月后饼煞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡诗越,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年砖瞧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嚷狞。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡块促,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出床未,到底是詐尸還是另有隱情竭翠,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布薇搁,位于F島的核電站斋扰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜传货,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一屎鳍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧问裕,春花似錦逮壁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窟勃,卻和暖如春祖乳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秉氧。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工眷昆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汁咏。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓亚斋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親攘滩。 傳聞我的和親對象是個殘疾皇子帅刊,可洞房花燭夜當晚...
    茶點故事閱讀 43,595評論 2 350