MVP+Retrofit2.0+RxJava2.0

最近把MVP+Retrofit2.0+RxJava2.0封裝了一下,在項目里用了之后几缭,發(fā)現代碼確實簡潔了很多,看上去簡直不要太爽沃呢!哈哈年栓,大圣寫代碼,自己寫自己夸樟插!
關于MVP的定義之類的內容我就不再贅述了韵洋,無非就是Model竿刁,View黄锤,Presenter。網上千篇文章千篇樣食拜,所以我理解著就是根據自己的風格寫就行了鸵熟,沒有一個統(tǒng)一的定義非要怎么樣怎么樣去寫。Retrofit2.0和RxJava2.0的基礎知識各位看官也就移步別苑自己去看吧负甸。這里就直接說我寫的這個封裝了流强。
首先我先定義了一個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();
    }
}

這個類里面定義了Retrofit的獲取方法create()呻待,其中涉及到了OkHttp的同步封裝打月。單例模式用的是靜態(tài)內部類的方式(《設計模式》里最推薦的定義單例模式的方式)。
其次是定義了一個接口: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請求接口的方式奏篙,注意:返回類型為Observable<T>,這是因為集成了RxJava。
然后就是我們的MVP了秘通,首先是Presenter为严。我這里定義了一個BasePresenter。

public interface BasePresenter {

    void add(Disposable disposable);

    void clear();
}

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

public interface BaseView {

    void showResult(String result);
}

這里面就是根據各位自己的喜好自行定義了夕吻。
最后是Model。同樣的稿静,BaseMode梭冠。

public class BaseModel {

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

這個方法實現的是返回一個切換線程的Observable,這樣就不用在后面每次寫一遍了改备。
然后我還定義了一個RxPresenter控漠,用來實現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是一個管理Disposable的集合悬钳,具體怎么實現的大家可以去看下源碼盐捷,很好理解。
還有一個BaseObserver默勾,用來簡潔化請求接口時的操作碉渡。

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);
}

這是一個抽象類,實現了Observer母剥,其中我定義了一個構造方法滞诺,用于獲取上下文Context來顯示加載對話框。這個類定義出來之后环疼,后面實現請求接口的時候就只需要執(zhí)行和實現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);
    }
}

最后看一下具體實現:
比如主界面里我要請求兩個接口淋叶,定義兩個按鈕,具體實現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煞檩。然后我們再來看一下這兩個類是怎么實現的。

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, "請先設置店鋪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);
                    }
                });
    }
}

看栅贴,請求接口部分多么簡潔斟湃,數據解析已經通過RetrofitManager里設置的.addCallAdapterFactory(RxJava2CallAdapterFactory.create())實現了,媽媽再也不用擔心我手動解析的麻煩了檐薯。在onSuccess()方法里接收就行了凝赛。漂亮!
再看HomeView。

public interface HomeView extends BaseView {

    String getCouponCode();

    void onConsumeCouponSuccess(ConsumeCouponEntity consumeCouponEntity);

    void onGetCouponInfoSuccess(CouponInfoEntity couponInfoEntity);
}

其中getCouponCode()方法用來獲取界面上輸入框里的值哄酝,用于HomePresenter里請求接口傳參友存,而onConsumeCouponSuccess()和onGetCouponInfoSuccess()方法用來接收返回數據的實體類對象。
別忘了陶衅,還有一個HomeModel呢屡立,那么它是什么樣的呢?到底長的是鞋拔子臉還是豬腰子臉呢搀军?

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));
    }
}

哇膨俐!原來長得跟本山大叔一樣,太神奇了罩句。(開個玩笑焚刺,如有侵權,本人概不承認C爬谩)
這里就是實現接口請求了乳愉,用了BaseMode()里的observable()。
大體就是這樣一個風格屯远,這是我自己適合的風格蔓姚,權且拋磚引玉,語言描述上有不完善之處慨丐,代碼上應該也有不盡人意之處坡脐,還請各位大神幫忙指點指點,本人期待進步房揭!
其實之前寫了一個Retrofit初接觸备闲,當時還沒用到MVP和RxJava2.0,就已經覺得自己很牛13了⊥北現在看到這個封裝之后恬砂,感覺代碼果然好簡潔,水平高了很多伶唯。再看看那個Retrofit初接觸觉既,感覺好low熬屙铩乳幸!哈哈,說明我還是進步了钧椰。
行了粹断,就這些吧。記在這兒嫡霞,拋磚引玉瓶埋,敬請指教。也方便自己以后使用的時候查看。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末养筒,一起剝皮案震驚了整個濱河市曾撤,隨后出現的幾起案子,更是在濱河造成了極大的恐慌晕粪,老刑警劉巖挤悉,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異巫湘,居然都是意外死亡装悲,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門尚氛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诀诊,“玉大人,你說我怎么就攤上這事阅嘶∈舭辏” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵讯柔,是天一觀的道長奠涌。 經常有香客問我,道長磷杏,這世上最難降的妖魔是什么溜畅? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮极祸,結果婚禮上慈格,老公的妹妹穿的比我還像新娘。我一直安慰自己遥金,他們只是感情好浴捆,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稿械,像睡著了一般选泻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上美莫,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天页眯,我揣著相機與錄音,去河邊找鬼厢呵。 笑死窝撵,一個胖子當著我的面吹牛,可吹牛的內容都是我干的襟铭。 我是一名探鬼主播碌奉,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼短曾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赐劣?” 一聲冷哼從身側響起嫉拐,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎魁兼,沒想到半個月后椭岩,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡璃赡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年判哥,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碉考。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡塌计,死狀恐怖,靈堂內的尸體忽然破棺而出侯谁,到底是詐尸還是另有隱情锌仅,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布墙贱,位于F島的核電站热芹,受9級特大地震影響,放射性物質發(fā)生泄漏惨撇。R本人自食惡果不足惜伊脓,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魁衙。 院中可真熱鬧报腔,春花似錦、人聲如沸剖淀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纵隔。三九已至翻诉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捌刮,已是汗流浹背碰煌。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留糊啡,地道東北人拄查。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓吁津,卻偏偏與公主長得像棚蓄,于是被迫代替她去往敵國和親堕扶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容