版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Achilles_Lee/article/details/78314633
MVP是当下比较流行的框架,相对与mvc框架,他可以将View层和model层完全解耦,是代码的阅读和维护更加清晰明朗。
这里主要介绍一种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框架
- TheMvp : https://github.com/kymjs/TheMVP
- Nucleus : https://github.com/konmik/nucleus
- Beam : https://github.com/Jude95/Beam