MVP+Retrofit2.0+RxJava2.0

作者: 花果山来的猴子 | 来源:发表于2018-12-19 10:36 被阅读604次

    最近把MVP+Retrofit2.0+RxJava2.0封装了一下,在项目里用了之后,发现代码确实简洁了很多,看上去简直不要太爽!哈哈,大圣写代码,自己写自己夸!
    关于MVP的定义之类的内容我就不再赘述了,无非就是Model,View,Presenter。网上千篇文章千篇样,所以我理解着就是根据自己的风格写就行了,没有一个统一的定义非要怎么样怎么样去写。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的同步封装。单例模式用的是静态内部类的方式(《设计模式》里最推荐的定义单例模式的方式)。
    其次是定义了一个接口: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来显示加载对话框。这个类定义出来之后,后面实现请求接口的时候就只需要执行和实现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();
        }
    
        /**
         * 获取优惠券信息
         */
        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);
                        }
                    });
        }
    
        /**
         * 核销优惠券
         */
        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));
        }
    }
    

    哇!原来长得跟本山大叔一样,太神奇了。(开个玩笑,如有侵权,本人概不承认!)
    这里就是实现接口请求了,用了BaseMode()里的observable()。
    大体就是这样一个风格,这是我自己适合的风格,权且抛砖引玉,语言描述上有不完善之处,代码上应该也有不尽人意之处,还请各位大神帮忙指点指点,本人期待进步!
    其实之前写了一个Retrofit初接触,当时还没用到MVP和RxJava2.0,就已经觉得自己很牛13了。现在看到这个封装之后,感觉代码果然好简洁,水平高了很多。再看看那个Retrofit初接触,感觉好low啊!哈哈,说明我还是进步了。
    行了,就这些吧。记在这儿,抛砖引玉,敬请指教。也方便自己以后使用的时候查看。

    相关文章

      网友评论

        本文标题:MVP+Retrofit2.0+RxJava2.0

        本文链接:https://www.haomeiwen.com/subject/ioydkqtx.html