MVP+Retrofit2.0+RxJava2.0

最近把MVP+Retrofit2.0+RxJava2.0封裝了一下,在項(xiàng)目里用了之后,發(fā)現(xiàn)代碼確實(shí)簡(jiǎn)潔了很多,看上去簡(jiǎn)直不要太爽!哈哈,大圣寫代碼,自己寫自己夸!
關(guān)于MVP的定義之類的內(nèi)容我就不再贅述了,無非就是Model,View,Presenter。網(wǎng)上千篇文章千篇樣,所以我理解著就是根據(jù)自己的風(fēng)格寫就行了,沒有一個(gè)統(tǒng)一的定義非要怎么樣怎么樣去寫。Retrofit2.0和RxJava2.0的基礎(chǔ)知識(shí)各位看官也就移步別苑自己去看吧。這里就直接說我寫的這個(gè)封裝了。
首先我先定義了一個(gè)Retrofit的管理類:RetrofitManager。

public class RetrofitManager {

    private static int DEFAULT_TIMEOUT = 5;

    private RetrofitManager() {

    }

    public static RetrofitManager getInstance() {

        return RetrofitManagerHolder.INSTANCE;
    }

    private static class RetrofitManagerHolder {

        private static RetrofitManager INSTANCE = new RetrofitManager();
    }

    public <S> S create(Class<S> service, String baseUrl) {
        Retrofit retrofit = new Retrofit.Builder()
                .client(getOkHttpClient())
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        return retrofit.create(service);
    }

    private OkHttpClient getOkHttpClient() {
        return new OkHttpClient.Builder()
                .proxy(Proxy.NO_PROXY)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false)
                .build();
    }
}

這個(gè)類里面定義了Retrofit的獲取方法create(),其中涉及到了OkHttp的同步封裝。單例模式用的是靜態(tài)內(nèi)部類的方式(《設(shè)計(jì)模式》里最推薦的定義單例模式的方式)。
其次是定義了一個(gè)接口:RetrofitService。

public interface RetrofitService {

    @GET(HttpUtil.GET_COUPON_INFO_URL)
    Observable<CouponInfoEntity> getCouponInfo(@QueryMap Map<String, String> params);

    @FormUrlEncoded
    @POST(HttpUtil.CONSUME_COUPON_URL)
    Observable<ConsumeCouponEntity> consumeCoupon(@Query("randomseed") String randomSeed,
                                                  @Query("token") String token,
                                                  @FieldMap Map<String, String> params);
}

這里面放的就是Retrofit請(qǐng)求接口的方式,注意:返回類型為Observable<T>,這是因?yàn)榧闪薘xJava。
然后就是我們的MVP了,首先是Presenter。我這里定義了一個(gè)BasePresenter。

public interface BasePresenter {

    void add(Disposable disposable);

    void clear();
}

兩個(gè)方法:add()和clear(),這兩個(gè)方法就是用來管理RxJava的生命周期的。
其次是View。我同樣定義了一個(gè)BaseView。

public interface BaseView {

    void showResult(String result);
}

這里面就是根據(jù)各位自己的喜好自行定義了。
最后是Model。同樣的,BaseMode。

public class BaseModel {

    public <T> Observable<T> observable(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }
}

這個(gè)方法實(shí)現(xiàn)的是返回一個(gè)切換線程的Observable,這樣就不用在后面每次寫一遍了。
然后我還定義了一個(gè)RxPresenter,用來實(shí)現(xiàn)BasePresenter。

public class RxPresenter implements BasePresenter {

    private CompositeDisposable disposables;

    @Override
    public void add(Disposable disposable) {
        if (null == disposables) {
            disposables = new CompositeDisposable();
        }
        disposables.add(disposable);
    }

    @Override
    public void clear() {
        if (null != disposables && disposables.isDisposed()) {
            disposables.clear();
        }
    }
}

CompositeDisposable是一個(gè)管理Disposable的集合,具體怎么實(shí)現(xiàn)的大家可以去看下源碼,很好理解。
還有一個(gè)BaseObserver,用來簡(jiǎn)潔化請(qǐng)求接口時(shí)的操作。

public abstract class BaseObserver<T> implements Observer<T> {

    private Context context;

    public BaseObserver(Context context) {
        this.context = context;
    }

    @Override
    public void onSubscribe(Disposable d) {
        DialogFactory.showLoadingDialog(context);
        onDisposable(d);
    }

    @Override
    public void onNext(T t) {
        onSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
        LogUtil.i("onError: " + e.getMessage());
        DialogFactory.dismissLoadingDialog();
    }

    @Override
    public void onComplete() {
        DialogFactory.dismissLoadingDialog();
    }

    protected abstract void onSuccess(T t);

    protected abstract void onDisposable(Disposable d);
}

這是一個(gè)抽象類,實(shí)現(xiàn)了Observer,其中我定義了一個(gè)構(gòu)造方法,用于獲取上下文Context來顯示加載對(duì)話框。這個(gè)類定義出來之后,后面實(shí)現(xiàn)請(qǐng)求接口的時(shí)候就只需要執(zhí)行和實(shí)現(xiàn)onSuccess()和onDisposable()方法。
最后是BaseActivity類,此處onDestroy()方法里清除所有的disposable。

public abstract class BaseActivity<P extends RxPresenter> extends Activity {

    protected P presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initContentView());
        ButterKnife.bind(this);
        UIUtil.add(this);
        init();
        presenter = createPresenter();
        initListener();
        showView();
    }

    protected abstract int initContentView();

    protected abstract void init();

    protected abstract P createPresenter();

    protected abstract void initListener();

    protected abstract void showView();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.clear();
        UIUtil.remove(this);
    }
}

最后看一下具體實(shí)現(xiàn):
比如主界面里我要請(qǐng)求兩個(gè)接口,定義兩個(gè)按鈕,具體實(shí)現(xiàn)layout的xml就不貼了。

public class MainActivity extends BaseActivity<HomePresenter> implements HomeView {

    @BindView(R.id.edit_input_coupon_code)
    EditText inputCouponCode;

    private long exitTime;

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

    @Override
    protected void init() {

    }

    @Override
    protected HomePresenter createPresenter() {
        return new HomePresenter(this, this);
    }

    @Override
    protected void initListener() {

    }

    @Override
    protected void showView() {

    }

    @Override
    public String getCouponCode() {
        return StringUtil.getStringValue(inputCouponCode);
    }

    @Override
    public void onConsumeCouponSuccess(ConsumeCouponEntity consumeCouponEntity) {

    }

    @Override
    public void onGetCouponInfoSuccess(CouponInfoEntity couponInfoEntity) {

    }

    @Override
    public void showResult(String result) {
        LogUtil.i(result);
    }

    @OnClick(R.id.img_setting)
    public void onSetting() {
        UIUtil.openUI(this, SettingActivity.class);
    }

    @OnClick(R.id.btn_get_coupon_info)
    public void onGetCouponInfo() {
        presenter.getCouponInfo();
    }

    @OnClick(R.id.btn_consume_coupon)
    public void onConsumeCoupon() {
        presenter.consumeCoupon();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if ((System.currentTimeMillis() - exitTime) > 2000) {
                ToastUtil.toastShort(this, "再按一次退出程序");
                exitTime = System.currentTimeMillis();
            } else {
                UIUtil.exit();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

這里我們用的是HomePresenter和HomeView。然后我們?cè)賮砜匆幌逻@兩個(gè)類是怎么實(shí)現(xiàn)的。

public class HomePresenter extends RxPresenter {

    private Context context;
    private HomeView homeView;
    private HomeModel homeModel;

    public HomePresenter(Context context, HomeView homeView) {
        this.context = context;
        this.homeView = homeView;
        homeModel = new HomeModel();
    }

    /**
     * 獲取優(yōu)惠券信息
     */
    public void getCouponInfo() {
        String randomSeed = String.valueOf(System.currentTimeMillis() / 1000);
        String token = "token";
        homeModel.getCouponInfo(homeView.getCouponCode(), randomSeed, token)
                .subscribe(new BaseObserver<CouponInfoEntity>(context) {
                    @Override
                    protected void onSuccess(CouponInfoEntity couponInfoEntity) {
                        if (couponInfoEntity.Status != 1) {
                            ToastUtil.toastShort(context, couponInfoEntity.Msg);
                            return;
                        }
                        homeView.onGetCouponInfoSuccess(couponInfoEntity);
                        homeView.showResult(couponInfoEntity.toString());
                    }

                    @Override
                    protected void onDisposable(Disposable d) {
                        add(d);
                    }
                });
    }

    /**
     * 核銷優(yōu)惠券
     */
    public void consumeCoupon() {
        String shopId = (String) SPUtil.getInstance().getData(SPUtil.SHOP_ID, "");
        if (null == shopId || shopId.equals("")) {
            ToastUtil.toastShort(context, "請(qǐng)先設(shè)置店鋪Id");
            return;
        }
        String randomSeed = String.valueOf(System.currentTimeMillis() / 1000);
        String token = "token";
        homeModel.consumeCoupon(homeView.getCouponCode(), shopId, randomSeed, token)
                .subscribe(new BaseObserver<ConsumeCouponEntity>(context) {
                    @Override
                    protected void onSuccess(ConsumeCouponEntity consumeCouponEntity) {
                        if (consumeCouponEntity.Status != 1) {
                            ToastUtil.toastShort(context, consumeCouponEntity.Msg);
                            return;
                        }
                        homeView.onConsumeCouponSuccess(consumeCouponEntity);
                        homeView.showResult(consumeCouponEntity.toString());
                    }

                    @Override
                    protected void onDisposable(Disposable d) {
                        add(d);
                    }
                });
    }
}

看,請(qǐng)求接口部分多么簡(jiǎn)潔,數(shù)據(jù)解析已經(jīng)通過RetrofitManager里設(shè)置的.addCallAdapterFactory(RxJava2CallAdapterFactory.create())實(shí)現(xiàn)了,媽媽再也不用擔(dān)心我手動(dòng)解析的麻煩了。在onSuccess()方法里接收就行了。漂亮!
再看HomeView。

public interface HomeView extends BaseView {

    String getCouponCode();

    void onConsumeCouponSuccess(ConsumeCouponEntity consumeCouponEntity);

    void onGetCouponInfoSuccess(CouponInfoEntity couponInfoEntity);
}

其中g(shù)etCouponCode()方法用來獲取界面上輸入框里的值,用于HomePresenter里請(qǐng)求接口傳參,而onConsumeCouponSuccess()和onGetCouponInfoSuccess()方法用來接收返回?cái)?shù)據(jù)的實(shí)體類對(duì)象。
別忘了,還有一個(gè)HomeModel呢,那么它是什么樣的呢?到底長(zhǎng)的是鞋拔子臉還是豬腰子臉呢?

public class HomeModel extends BaseModel {

    public Observable<CouponInfoEntity> getCouponInfo(String couponCode, String randomSeed, String token) {
        Map<String, String> params = new HashMap<>();
        params.put("couponCode", couponCode);
        params.put("randomseed", randomSeed);
        params.put("token", token);
        return observable(RetrofitManager.getInstance().create(RetrofitService.class, HttpUtil.BASE_URL).getCouponInfo(params));
    }

    public Observable<ConsumeCouponEntity> consumeCoupon(String couponCode, String storeId, String randomSeed, String token) {
        Map<String, String> params = new HashMap<>();
        params.put("CouponCode", couponCode);
        params.put("StoreId", storeId);
        return observable(RetrofitManager.getInstance().create(RetrofitService.class, HttpUtil.BASE_URL).consumeCoupon(randomSeed, token, params));
    }
}

哇!原來長(zhǎng)得跟本山大叔一樣,太神奇了。(開個(gè)玩笑,如有侵權(quán),本人概不承認(rèn)?。?br> 這里就是實(shí)現(xiàn)接口請(qǐng)求了,用了BaseMode()里的observable()。
大體就是這樣一個(gè)風(fēng)格,這是我自己適合的風(fēng)格,權(quán)且拋磚引玉,語(yǔ)言描述上有不完善之處,代碼上應(yīng)該也有不盡人意之處,還請(qǐng)各位大神幫忙指點(diǎn)指點(diǎn),本人期待進(jìn)步!
其實(shí)之前寫了一個(gè)Retrofit初接觸,當(dāng)時(shí)還沒用到MVP和RxJava2.0,就已經(jīng)覺得自己很牛13了?,F(xiàn)在看到這個(gè)封裝之后,感覺代碼果然好簡(jiǎn)潔,水平高了很多。再看看那個(gè)Retrofit初接觸,感覺好low啊!哈哈,說明我還是進(jìn)步了。
行了,就這些吧。記在這兒,拋磚引玉,敬請(qǐng)指教。也方便自己以后使用的時(shí)候查看。

最后編輯于
?著作權(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)容