MVP

View

界面

由于MVP被动视图的特性, Presenter会引用View的抽象以及Model的抽象, 同时View会引用Presenter, 那么此时几乎每个界面都会多出很多声明, 初始化的重复语句. 要消除这些重复可以考虑使用依赖注入的框架, 如Dagger, 但是考虑到Dagger本身的复杂性, 以及项目复杂度等原因, CoreLibs采用泛型及模板方法来消除重复.

基本上Android中是由Activity和Fragment来承载视图, 也有可能是View/ViewGroup. 因此在CoreLibs中提供了BaseActivity以及BaseFragment两个模板类, 来处理一些公共的视图逻辑. 至于View/ViewGroup的模板类会在以后加入. 模板类, 不仅可以简化代码, 同时也能起到一定的约束作用, 比如将onCreate方法拆分成多个符合单一职责原则的小方法, 从而避免onCreate过于臃肿.

抽象接口

被动视图中的View是将界面行为抽象出来的接口, 以供Presenter调用. 同Base模板类一样, View也可以抽象出几乎所有界面所共有的行为 -- BaseView. 比如只要有网络请求的界面(项目中几乎都是), 都会有加载框来提示用户当前正在加载网络数据, 加载完成后需要隐藏加载框, 并在适当时候提示用户一些错误消息. 一旦抽象出来BaseView, 就可以在Presenter等类里自动的调用这些公共的行为, 而无需再重复手动写.

Presenter

Presenter的共有逻辑一般就是与界面的绑定, 解绑等.

Model

Model这一层比较复杂, 随着项目的复杂度, Model可能会继续划分层级, 因此暂时没有抽出共有的逻辑.

View与Presenter的结合

View与Presenter之间是通过泛型来约束类型, 这里View以BaseActivity为例, BaseFragment与其类似.

BaseActivity声明

public abstract class BaseActivity<V extends BaseView, T extends BasePresenter<V>>

BasePresenter声明

public abstract class BasePresenter<T extends BaseView>

BaseView声明

public interface BaseView

可以看到, BaseActivity需要两个泛型, 第一个是继承自BaseView的接口, 第二个是继承自BasePresenter的Presenter, 同时Presenter里的泛型必须是V类型, 也就是第一个泛型类型.

BasePresenter则只需要一个继承自BaseView的接口.

这样做是为了约束View与Presenter的类型, 可以强制开发使用MVP模式, 同时可以将一些通用逻辑被抽象到BaseActivity与BasePresenter中:

BaseActivity部分通用逻辑

    protected T presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());

        presenter = createPresenter();
        if (presenter != null) presenter.attachView((V) this);

        init(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) presenter.detachView();
        presenter = null;
    }

    /**
     * 指定Activity需加载的布局ID, {@link BaseActivity}BaseActivity
     * 会通过{@link #setContentView}方法来加载布局
     *
     * @return 需加载的布局ID
     */
    protected abstract int getLayoutId();

    /**
     * 初始化方法, 类似OnCreate, 仅在此方法中做初始化操作, findView与事件绑定请使用ButterKnife
     */
    protected abstract void init(Bundle savedInstanceState);

    /**
     * 创建Presenter, 然后通过调用{@link #getPresenter()}来使用生成的Presenter
     * @return Presenter
     */
    protected abstract T createPresenter();

上述代码声明了T类型的Presenter, 根据BaseActivity的声明可以得知T类型就是我们需要的Presenter的实际类型.

在onCreate方法中, 首先通过getLayoutId()抽象方法来获取Activity的布局id, 然后通过setContentView()设置到content中. 这样我们在BaseActivity的子类中就无需再复写onCreate方法. 然后通过createPresenter()抽象方法来实例化声明的presenter. 由于presenter是protected, 因此子类可以直接使用presenter成员变量. 接着调用presenter的attachView()方法将View与Presenter绑定起来, attachView()的实现会在下面贴出来. 最后调用了抽象方法init(Bundle savedInstanceState), 来替代onCreate方法.

在onDestroy中, 首先通过presenter的detachView()将View与Presenter解绑, 同时将presenter置空, 很大程度上避免了内存泄漏的可能.

可以看到, BaseActivity为子类做了如下逻辑, 子类无需再做:

  • 设置布局视图

  • 声明以及初始化Presenter

  • 将Presenter与View绑定

  • 初始化Activity

  • 将Presenter与View解除绑定

子类需要实现三个抽象方法, 具体含义请参考注释:

  • int getLayoutId()

  • T createPresenter()

  • void init(Bundle savedInstanceState)

BasePresenter部分通用逻辑

    protected T view;

    protected void onViewAttached() {}

    /**
     * 将Presenter与MVPView绑定起来.
     * @param view MVPView
     */
    public void attachView(T view) {
        this.view = view;
        onViewAttached();
    }

    /**
     * 将Presenter与MVPView解除.
     */
    public void detachView() {
        view = null;
    }

BasePresenter的逻辑比较简单, 就不做过多解释了, 同样的BasePresenter的子类可以直接使用view变量, 当然这里的view只是一个接口, 具体还需要BaseActivity子类去实现. 如果不去实现则会报类型转换错误. onViewAttached()方法通过名字也可以知道作用, 一些需要延迟初始化的变量或代码可以在覆写的onViewAttached()方法里写.

BaseView部分代码

    /**
     * 加载时显示加载框
     */
    void showLoading();

    /**
     * 加载完成时隐藏加载框
     */
    void hideLoading();

    /**
     * 显示提示消息
     */
    void showToastMessage(String message);

    /**
     * 获取Context对象
     */
    Context getViewContext();

BaseView接口中抽象了几乎所有页面都会使用的方法, 有了BaseView, 我们就可以利用BaseView为开发者做一些通用的逻辑, 比如自动隐藏加载框, 自动提示服务器返回的错误消息或者是网络错误消息等. 但是这段代码不在Presenter或View中, 以后会提及.

BaseActivty默认实现了BaseView, 因此BaseActivty的子类去实现BaseView的子类时, 只需要实现BaseView中没有的方法即可. 也可以通过覆写已实现的方法来达到细化控制的目的.

BaseActivity的完整声明

public abstract class BaseActivity<V extends BaseView, T extends BasePresenter<V>>

extends FragmentActivity implements BaseView

以上就是通过泛型来抽象通用逻辑的过程, 当然这三个Base类中还可以加入更多的逻辑, 这里只是其中一部分, 比如BaseActivity中还加入了ButterKnife的bind()方法, 以及加载框等.

Last updated