Android之通用MVP模式框架
在最近的学习中,我写代码都一直在使用通用的MVP模式框架,在使用的过程中,最让我感触非常深的是,整个代码的层次感非常清晰,耦合度非常低,扩展非常方便,以及能很好的处理Presenter和View直接内存溢出情况。如果你在写代码,我非常建议你使用这种框架下,下面请跟着我的思路走吧。
1、先看以下的构架:
在这里你将看到model层下的BaseModelInter、presenter层下的BasePresenter、view层下的BaseViewInter。下面我们来看看这两个接口,以及一个抽象类的代码。
BaseModelInter.java
public interface BaseModelInter {
}
BaseViewInter.java
public interface BaseViewInter {
}
BasePresenter.java
public abstract class BasePresenter<T extends BaseViewInter, M extends BaseModelInter> {
private WeakReference<T> weakReference;
protected M model;
public void attach(T t) {
weakReference = new WeakReference<>(t);
model = getModel();
}
public void deAttach() {
if (weakReference != null) {
weakReference.clear();
weakReference = null;
}
}
public boolean isViewAttached() {
return weakReference != null && weakReference.get() != null;
}
public T getView() {
if (weakReference != null) {
return weakReference.get();
}
return null;
}
protected abstract M getModel();
}
然后你会发现,我艹,怎么那两个接口中怎么什么都没写,你也把和这个代码贴上来了,是不是找干啊?没错,我就是要贴上来,就是不怕你干我!好了,开个小玩笑,回归正题,其实这样定义两个接口主要是为了扩展子接口,扩展子功能的,因为我们的主题是为了打造通用的框架,那它到底能扩展什么呢?接着往下看你就知道了。然后再看这个抽象类BasePresenter,这个抽象类,最关键的就是解决Presenter和view直接在长时间请求网络数据的情况下,为了防止内存溢出而设计的,我们通过一个虚引用就能很好的解决这个问题了。然后attach方法主要是将需要与具体的presenter类做绑定的view关联起来,并且将对应的model也绑定起来。而这个view类刚好是属于BaseViewInter下的子类。deAttach方法主要是当view向presenter请求数据时,由于网络请求时间过长,而view直接销毁了,而presenter没有即使返回数据,则deAttach就会把对应的view取消关联。isViewAttached方法判断view与presente是否关联,getView是返回一个绑定好的view对象,getModel方法则是让具体的presenter子类去创建具体的model对象。
2.下面看一个具体的实例:
FirstActiviy界面一需要向他对应的presenter层的FirstActivityPresenter请求数据AAAA,然后presenter去找与其具体对应的model层FirstActivityModelImp去拿数据。然后将数据返回给FirstActivity。
框架结构如下:
首先先看看BaseActivity.java和FirstActivity.java的代码:
BaseActivity.java
public abstract class BaseActivity<T extends BasePresenter,V extends BaseViewInter> extends AppCompatActivity {
protected T presenter;//③
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);//④
presenter = getPresenter();//⑤
presenter.attach((V)this);//⑦
}
protected abstract T getPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
presenter.deAttach();
}
}
FirstActivity.java
public class FirstActivity extends BaseActivity<FirstActivityPresenter,FirstActivity> implements FirstActivityViewInter {
private TextView mTv; //①
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);//②
setContentView(R.layout.first_activity);//⑧
mTv = (TextView)findViewById(R.id.mTv);//⑨
presenter.load("我是第一个view,我要获取数据,请presenter为我拿回一个字符串AAAA");//10
}
@Override
public void doSth(String data) {
Log.i("IT_Real", "doSth: 我获取了字符串" + data);
mTv.setText("我拿到的数据是:" + data);
}
@Override
protected FirstActivityPresenter getPresenter() {//⑥
return new FirstActivityPresenter();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
从上面代码看firstActivity继承了BaseActivity类。大家看看上面的执行顺序吧。从上面的10个顺序中,大家应该能理解吧, FirstActivity类首先开始执行,然后第6步将一个具体的FirstActivityPresenter返回给了父类的presenter。然后第7步是将FirstActivity和presenter进行关联。最后执行第10步向具体的FirstActivityPresenter请求数据AAAA。
FirstActivityPresenter.java
public class FirstActivityPresenter extends BasePresenter<FirstActivity,FirstActivityModelImp> {
@Override
protected FirstActivityModelImp getModel() {
return new FirstActivityModelImp();
}
public void load(String request){
model.dealSth(request,new OnSendStrListener() {
@Override
public void sendAAAA(String aaaa) {
getView().doSth(aaaa);
}
});
}
}
load方法主要是向model去拿具体的数据,然后将具体的数据返回给view层。这个类继承了BasePresenter类,可以看到,我们可以通过getView很好的拿到关联的FirstActivity类的具体实例,也能通过model拿到FirstActivityModelImp的具体实例。这样FirstActivityPresenter不持有FirstActivity的引用,就避免了内存溢出的发生。
FirstActivityViewInter.java
public interface FirstActivityViewInter extends BaseViewInter{
void doSth(String data);
}
上面这个接口中提供的就是所有FirstActivity中需要实现的功能,包括以后需要具体扩展FirstActivity中功能等,非常方便。
最后看看Model层中的类
FirstActivityModelInter.java
/**
* 所有FirstModelImp中需要实现的数据操作功能,以后需要操作上面功能,直接在这个接口中添加即可,
* 扩展性好。
*/
public interface FirstActivityModelInter extends BaseModelInter{
void dealSth(String request,OnSendStrListener listener);
}
OnsendStrListener.java
/**
* 具体要处理数据的回调接口,因为View需要一个字符串AAAA,所以就给他提供一个方法发回一个字符串
* 具体的功能自己定义即可,这里不统一,根据实际需求
*/
public interface OnSendStrListener {
void sendAAAA(String aaaa);
}
FIrstActiviyModelImp.java
public class FirstActivityModelImp implements FirstActivityModelInter {
/**
* 将具体的数据返回给FirstActivityPresenter,实现具体的数据操作功能。
*/
@Override
public void dealSth(String request, OnSendStrListener listener) {
if (request.equals("我是第一个view,我要获取数据,请presenter为我拿回一个字符串AAAA")){
Log.i("IT_Real", "dealSth: presenter需要一个AAAA数据,我要返回一个AAAA数据");
//接口回调该方法,就是将AAAA发回给presenter
listener.sendAAAA("AAAA");
}
}
}
通过上面的具体实现,在以后的项目中,我们可以扩展非常方便,这里我只实例了一个界面需要处理的功能,以及请求上面数据等。如果说以后你需要扩展多个界面,就像我一样,在view包下的activity下新建一个SecondActiviy类即可,然后该类继承BaseActivity,创建一个新的SecondViewInter (需要继承BaseViewInter),将所有功能写入到其中,然后让SecondActivity类去实现即可,next创建对应的SecondPresenter,去继承BasePresenter。处理一些事情即可,最后同样写一个SecondModelImp类,去实现SecondModleInter接口(需要继承BaseModelInter)中的具体功能即可。。。这样一来,我们的层次会非常清晰,然后扩展功能也非常方便,耦合也非常低,源代码上传一下,希望大家也可以养成这样的好习惯,建议使用这种模式写代码,对以后的工作会有很大的帮助哦。