一个简单的MVP Demo

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Achilles_Lee/article/details/78314633
  • MVP是当下比较流行的框架,相对与mvc框架,他可以将View层和model层完全解耦,是代码的阅读和维护更加清晰明朗。

  • 这里主要介绍一种mvp的实现方式,主要的类图如下。

MVP主要类图

这个框架的好处是在与将Persenter层对于View(Activity)层的引用,使用弱引用。避免了Model层在做耗时操作时,如果用户点击了返回,退出当前页面时,可以将view层与persenter的引用断开,避免activity层的内存泄漏。
代码地址:https://github.com/Lilee902/Mvp

Base层代码部分

主要的设计部分上base层的baseView和BaseMVPActivtiy和BasePersenter。

  • 先从最简单的BaseView开始,这个view将会被Activity实现,并传递给persenter持有,用于p层和view层通信。
public interface BaseView {}
  • 然后是BasePersenter层。这个类的声明需要指定一个泛型,具体好处再往下面阅读。
public abstract class BasePersenter<T extends BaseView> {}
  • 声明BaseMvpActivity ,这个类的声明,主要是泛型的,其中V表示BaseView的继承类,P表示BasePersenter的继承类。
public abstract class BaseMVPActivity<V extends BaseView, P extends BasePersenter<V>>
            extends AppCompatActivity implements BaseView {}
  • MVP的设计在于,让view层和model层的引用隔离开,不再相互持有。而view层将要做的事情传给中间层P层,P层再传给model层去做处理。我们现在要做的是P层与View层的持有做成弱引用,方便gc回收。
public abstract class BaseMVPActivity<V extends BaseView, P extends BasePersenter<V>>
        extends AppCompatActivity implements BaseView {

    // Persenter类的实例。
    public P mActPersenter;

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActPersenter = createPersenter();
        if (mActPersenter != null) {
            // BasePersenter类的方法。主要用于将View用弱引用赋值给P层的View对象
            mActPersenter.attach((V) this);
        }
    }

    // 子类实现,具体类型创建具体P层对象。
    protected abstract P createPersenter();

    @Override
    protected void onDestroy() {
        if (mActPersenter != null) {
            // BasePersenter类的方法。主要用于将View的引用清除。
            mActPersenter.detach();
        }
        super.onDestroy();
    }
}
  • 从上面的代码可以看到,在activity的oncreate和ondestory方法中,调用了Persenter的attach和detach方法。这两个方法的作用就是将View对象传给p层持有。而具体的P对象,则是通过抽象方法,让子类具体去创建。
  • 那么P层的attach和detach又是如何实现的呢?我们来具体看一下:
public abstract class BasePersenter<T extends BaseView> {

    // 弱引用的View
    public WeakReference<T> mActView;

    public BasePersenter() {
        super();
    }

    // 获取View中view引用。
    protected T getView() {
        return mActView.get();
    }

    // 判断view的引用是否仍持有,没有被GC回收。
    public boolean isViewAttached() {
        return mActView != null && mActView.get() != null;
    }

    // 将View对象以若引用的形式给mActView持有。
    public void attach(T view) {
        if (mActView == null) {
            mActView = new WeakReference<T>(view);
        }
    }

    // 将View在Activity调用onDestory时,释放。以便于Activiy销毁掉之后,内存可以被正常回收。
    public void detach() {
        if (mActView != null) {
            mActView.clear();
            mActView = null;
        }
    }
}
  • 上面是BasePersenter类的所有代码。可以看到attach 就是将View用一个弱引用保存起来(这里也有用软引用的,具体可以再在项目中实测决定,弱引用和软引用gc的回收的条件有差异)。而detach则是将View层和p层的引用断开,方便view层被回收。
  • isViewAttached方法,是用来判断当前的引用是否已经被gc回收,如果回收则表明View层已经走了ondestory方法,销毁掉了,就没有必要调用刷新UI线程的方法了。
  • getView方法是返回View对象。

具体使用

使用起来还是比较简单的。下面我简单贴一下几个主要的类,也可以进入github看具体代码。
代码地址:https://github.com/BravoLee/Mvp

  • Activity类
public class MainActivity extends BaseMVPActivity<MainView, MainPersenter> implements View.OnClickListener, MainView {

    private static final String TAG = MainActivity.class.getSimpleName();
    private ActivityMainBinding mainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        initEvent();
    }

    private void initEvent() {
        mainBinding.btnChange.setOnClickListener(this);
    }

    @Override
    protected MainPersenter createPersenter() {
        return new MainPersenter();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_change:
                mActPersenter.doAdd();
                break;
        }
    }

    @Override
    public int getTvNumber() {
        String intStr = mainBinding.tvNumber.getText().toString().trim();
        return Integer.parseInt(intStr);
    }

    @Override
    public void setTvNumber(String s) {
        mainBinding.tvNumber.setText(s);
    }
}
  • Persenter层
import com.bravo.mvp.base.BasePersenter;

/**
 * Created by Administrator on 2017/8/7.
 */

public class MainPersenter extends BasePersenter<MainView> implements ModuleListener {

    private final MainModule mainModule;

    public MainPersenter() {
        mainModule = new MainModuleImp(this);
    }

    public void doAdd() {
        int number = getNowNumber();
        mainModule.doAdd(number);
    }

    private int getNowNumber() {
        if (isViewAttached()) {
            return mActView.get().getTvNumber();
        }
        return -1;
    }

    @Override
    public void addFinish(int i) {
        if (isViewAttached()){
            mActView.get().setTvNumber(String.valueOf(i));
        }
    }

    @Override
    public void onAddError(Exception e) {
        if (isViewAttached()){
            mActView.get().onRespondError(e.getMessage());
            mActView.get().setTvNumber("0");
        }
    }
}
  • model层 模拟网络访问,有成功和失败两种情况。

public class MainModuleImp implements MainModule {
    private ModuleListener listener;

    public MainModuleImp(ModuleListener listener) {
        this.listener = listener;
    }

    @Override
    public void doAdd(int number) {
        if (number >= 0 && number < 10) {
            listener.addFinish(++number);
        } else {
            listener.onAddError(new Exception("Number can not be a minus or bigger than 10 !"));
        }
    }
}

缺点

  • mvp架构会让类和接口增多
  • 上面介绍的这种方法的话,每个activity对应一个view接口用于刷新Activity的UI,这样的话,违反了接口单一原则。

优点

  • 可以将具体的业务实现放在Persenter层和model层,activity层只负责UI的展示,要做什么事情传给Persenter和model层去做,这样后面在代码阅读和维护上会清晰很多。还是很值得引入的。

Github上面优秀的mvp框架

猜你喜欢

转载自blog.csdn.net/Achilles_Lee/article/details/78314633