前面我已经写过一篇关于Google的RxJava版MVP解读的文章,本篇就是我在此基础上进行了修改和封装,稍微变化一下,见上图。目的是让这个框架更加实用,同时简化操作,少写代码。果然程序员都是喜欢偷懒的- -
前言
首先,阅读本篇文章前,建议你先去了解一下MVP这个设计模式。当然,也可以先看看我前面的文章^_^ 传送门
另外,还用到了RxJava、Retrofit、Okhttp。如果你已经了解了他们的基本用法,请直接忽略这段,接着往下看~
不想看长长的文章的,可以直接看代码。代码地址已经上传到Github,传送门。
工程结构
按照惯例,先看看工程的主要结构:
简单说一下几个主要包下的功能。首先是api包,这是存放对Retrofit进行包装的类。Base包当然是放各种Base类啦~ mvp包是将契约类Contract、Model的实现类和Presenter的实现类放一起,方便管理。其实你也可以按功能分包,个人喜好吧。ui包放一些界面的类,如Activity和Fragment。
下面正式开始~
契约类
同样也是从Contract契约类开始说起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public interface MainContract { interface View extends BaseView { void showDialog(); void onSucceed(Gank data); void onFail(String err); void hideDialog(); } interface Model extends BaseModel { Observable<Gank> getGank(); } abstract class Presenter extends BasePresenter<View, Model> { public abstract void getGank(); } } |
我们可以看到,整体上和Google的Demo差不多,都是把View和Presenter放到Contract类里面统一管理,我这里多加了个Model接口,我不推荐在Presenter进行Model操作,本来很优雅的一件事,在Presenter进行Model操作的话,感觉就差了很多,要做一个优雅的程序员。不同的地方是Model和View接口继承了BaseModel接口和BaseView接口,Presenter变成了一个抽象类,继承于BasePresenter抽象类,传入两个泛型View、Model。为啥呢?我们接着看Base包下的三个Base类。
Base类
BaseModel:
1 2 | public interface BaseModel { } |
BaseView:
1 2 | public interface BaseView { } |
BasePresenter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class BasePresenter<V extends BaseView, M extends BaseModel> { protected V mView; protected M mModel; private CompositeSubscription mCompositeSubscription; protected void addSubscribe(Subscription subscription) { if (mCompositeSubscription == null) { mCompositeSubscription = new CompositeSubscription(); } mCompositeSubscription.add(subscription); } public void unSubscribe() { if (mView != null) { mView = null; } if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) { mCompositeSubscription.clear(); } } } |
BaseModel和BaseView接口里面是空的,在这里我只是为了在BasePresenter中提供一个约束。当然,如果你有其它全局的需求,可以在里面添加一些方法。重点是BasePresenter这个抽象类,传入一个View和Model,并将其用protected关键字修饰,这样,在它的子类中就可以直接对其赋值和使用了。加入CompositeSubscription变量,是为了对RxJava进行管理。unSubscribe方法对View进行null赋值和清除Rx的Subscription(订阅),防止内存泄漏。
Presnter桥梁
接下来看看这个很重要的类,作为连接Model和View的桥梁,这里又是怎么做的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class MainPresenter extends MainContract.Presenter { public MainPresenter(MainContract.View view) { mView = view; mModel = new MainModel(); } @Override public void getGank() { Subscription subscribe = mModel.getGank() .subscribe(new Subscriber<Gank>() { @Override public void onStart() { mView.showDialog(); } @Override public void onCompleted() { mView.hideDialog(); } @Override public void onError(Throwable e) { mView.onFail(e.getMessage()); onCompleted(); } @Override public void onNext(Gank gank) { mView.onSucceed(gank); } }); addSubscribe(subscribe); } } |
构造方法传进一个View,并且new了一个Model对象,直接赋值给父类中的View和Model。然后下面复写的方法中调用Model中的方法,再将结果通过View中的方法传出去,这是很原始的MVP方式。最后addSubscribe添加到订阅队列中。
Model处理数据
Model分出来,而不在Presenter处理,其实也是为了简洁,当你要处理很多数据的时候,Presenter就会变得很乱了。
1 2 3 4 5 6 7 8 9 | public class MainModel implements MainContract.Model { @Override public Observable<Gank> getGank() { return ApiEngine.getInstance().getApiService() .getGank("1") .compose(RxSchedulers.<Gank>switchThread()); } } |
我这里很简单,就获取ApiService对象,然后调用API。最后compose传进我自己定义的线程切换器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class RxSchedulers { public static <T> Observable.Transformer<T, T> switchThread() { return new Observable.Transformer<T, T>() { @Override public Observable<T> call(Observable<T> tObservable) { return tObservable .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } } |
将网络请求的IO线程切换回Android的UI主线程,才能继续进行在Presenter中的操作。
BaseActivity中的封装
在前面我们可以看到BasePresenter中有两个方法,一个是添加订阅addSubscribe,另一个是unSubscribe解除订阅。我们只看到了在Presenter中使用了addSubscribe,而没有看到unSubscribe在哪使用了。因为要防止内存泄漏,所以当然要在和生命周期相关的地方进行释放资源,这个地方只有我们所说的View了,也就是Activity和Fragment中。我们先开看一下相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity { protected P mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (onCreatePresenter() != null) { mPresenter = onCreatePresenter(); } } @Override protected void onDestroy() { super.onDestroy(); if (mPresenter!=null){ mPresenter.unSubscribe(); } } protected abstract P onCreatePresenter(); } |
通过泛型确定一个Presenter的类型,然后使用抽象方法onCreatePresenter对其进行赋值,最后在onDestroy方法中进行资源的释放。继承这个BaseActivity类的Activity,就不用每次都在onDestroy进行同样的操作啦~达到简洁的目的。同理,Fragment中也是同样的,只是在生命周期的onResume和onPause中分别进行Presenter的赋值和资源的释放。这里我就不贴代码,可以上我的Github看。
Retrofit引擎封装
说到这了,就顺便说一下Retrofit吧。上面的MainModel类中用到了ApiEngine类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public class ApiEngine { private volatile static ApiEngine apiEngine; private Retrofit retrofit; private ApiEngine() { //日志拦截器 HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); //缓存 int size = 1024 * 1024 * 100; File cacheFile = new File(App.getContext().getCacheDir(), "OkHttpCache"); Cache cache = new Cache(cacheFile, size); OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(12, TimeUnit.SECONDS) .writeTimeout(12, TimeUnit.SECONDS) .writeTimeout(12, TimeUnit.SECONDS) .addNetworkInterceptor(new NetworkInterceptor()) .addInterceptor(loggingInterceptor) .cache(cache) .build(); retrofit = new Retrofit.Builder() .baseUrl(ApiService.BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } public static ApiEngine getInstance() { if (apiEngine == null) { synchronized (ApiEngine.class) { if (apiEngine == null) { apiEngine = new ApiEngine(); } } } return apiEngine; } public ApiService getApiService() { return retrofit.create(ApiService.class); } } |
用了单例模式,在构造方法中只初始化一次Retrofit和Okhttp。双重锁的方式获取单例,然后再根据需要获取ApiService,如果你有很多个不同源的API,那就可以创建多个getXXXXApiService。
结语
其实还有一个问题,就是关于错误类型的统一处理。刚开始的时候我是打算添加进去的,但是后来想想,每个项目的API都有不同的需求,有不同的错误,所以,还是留给需要的时候再封装一层错误处理吧。如果要统一处理错误,自定义一个Subscriber,在里面的onError方法处理即可。