MVP是在开发中常用的框架,要了解其原理,先要从了解MVC开始,这里就对MVP框架做一个简单的介绍
MVC
MVC为Model,View与Controllor的缩写
Model:业务逻辑和实体模型
View:对应于布局文件,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller
Controllor:对应于Activity
MVP
MVC为Model,View与Presenter的缩写
Model:业务逻辑和实体模型
View:对应于Activity,负责View的绘制以及与用户交互
Presenter:负责完成View于Model间的交互
MVP对比MVC
减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低
Activity 代码变得更加简洁:使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多
方便进行单元测试:一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧
避免 Activity 的内存泄露:
- 发生OOM异常的原因:现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak));Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收;Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM
- MVC产生内存泄漏异常分析:采用传统的MVC模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM
- MVC模式如何比面内存泄漏:只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak
MVP角色
View::负责绘制UI元素、与用户进行交互(在Android中体现为Activity)
Activity interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试
Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑
开源MVP框架
TheMVP
原理:TheMVP使用Activity作为Presenter层来处理代码逻辑,通过让Activity包含一个ViewDelegate对象来间接操作View层对外提供的方法,从而做到完全解耦视图层
链接:https://github.com/kymjs/TheMVPMVPro
原理:都是将Activity和Fragment作为Presenter。Presenter即我们的Activity或者Fragment, View呢?说白了就是我们从Activity和Fragment中提取出来的和View操作相关的代码
链接:https://github.com/qibin0506/MVProNucleus框架(MVP)
链接:https://github.com/konmik/nucleusBeam框架
链接:https://github.com/Jude95/Beam
理解实例
这里使用一个ListView的设计来说明MVP在实际项目开发过程中运用
首先准备一个java bean类,用来作为ListView的显示部分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
35public class Fruit {
private int icon;
private String name;
private String describe;
public Fruit(int icon, String name, String describe) {
this.icon = icon;
this.name = name;
this.describe = describe;
}
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
}
另外还需要一个数据类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
49public class FruitListAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Fruit> data;
public FruitListAdapter(Context context, List<Fruit> data) {
this.inflater = LayoutInflater.from(context);
this.data = data;
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return data.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item, null);
holder = new ViewHolder();
holder.iv_icon = convertView.findViewById(R.id.iv_icon);
holder.tv_name = convertView.findViewById(R.id.tv_name);
holder.tv_describe = convertView.findViewById(R.id.tv_describe);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.iv_icon.setImageResource(data.get(position).getIcon());
holder.tv_name.setText(data.get(position).getName());
holder.tv_describe.setText(data.get(position).getDescribe());
return convertView;
}
private class ViewHolder {
ImageView iv_icon;
TextView tv_name;
TextView tv_describe;
}
}
model层1
2
3
4
5
6
7
8public interface IFruitModel {
void loadFruit(FruitOnLoadListener fruitOnLoadListener);
//监听数据返回
interface FruitOnLoadListener {
void onComplete(List<Fruit> fruits);
}
}
1 | public class FruitModel implements IFruitModel { |
View层1
2
3
4
5
6public interface IFruitView {
//UI业务逻辑,加载进度条
void showLoading();
//回调给Present
void showFruit(List<Fruit> fruits);
}
Present层1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class FruitPresent {
//持有视图层引用
private IFruitView fruitView;
//持有模型层引用
private IFruitModel fruitModel = new FruitModel();
public FruitPresent(IFruitView fruitView) {
this.fruitView = fruitView;
}
public void fectch() {
fruitView.showLoading();
if (fruitModel != null) {
//回调监听
fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
public void onComplete(List<Fruit> fruits) {
fruitView.showFruit(fruits);
}
});
}
}
}
MainActivity调用,这里的MainActivity其实就是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//View层
public class MainActivity extends AppCompatActivity implements IFruitView {
private ListView listView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
//实例化Present并得到数据
new FruitPresent(this).fectch();
}
public void showLoading() {
}
public void showFruit(List<Fruit> fruits) {
listView.setAdapter(new FruitListAdapter(this,fruits));
}
}
这里还应该对其优化,防止内存泄漏
增加BasePresent类,使其添加绑定和解绑操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public abstract class BasePresent<T> {
//持有UI接口的弱引用
protected WeakReference<T> viewRef;
//获取数据方法
public abstract void fectch();
//弱引用绑定
public void attachView(T view) {
viewRef = new WeakReference<T>(view);
}
//解绑
public void detachView() {
if (viewRef != null) {
viewRef.clear();
viewRef = null;
}
}
}
让其子类继承1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class FruitPresent<T> extends BasePresent<IFruitView> {
//持有视图层引用
private IFruitView fruitView;
//持有模型层引用
private IFruitModel fruitModel = new FruitModel();
public FruitPresent(IFruitView fruitView) {
this.fruitView = fruitView;
}
public void fectch() {
fruitView.showLoading();
if (fruitModel != null) {
//回调监听
fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
public void onComplete(List<Fruit> fruits) {
fruitView.showFruit(fruits);
}
});
}
}
}
添加BaseActivity,完成绑定和解绑操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public abstract class BaseActivity<V, T extends BasePresent<V>> extends Activity {
protected T present;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
present = createPresent();
present.attachView((V) this);
}
protected void onDestroy() {
super.onDestroy();
present.detachView();
}
//子类实现具体构建过程
protected abstract T createPresent();
}
让其子类继承完成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//View层
public class MainActivity extends BaseActivity<IFruitView, FruitPresent<IFruitView>> implements IFruitView {
private ListView listView;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_v1);
listView = findViewById(R.id.listview);
present.fectch();
}
protected FruitPresent<IFruitView> createPresent() {
return new FruitPresent<>(this);
}
public void showLoading() {
Toast.makeText(this, "loading", Toast.LENGTH_SHORT).show();
}
public void showFruit(List<Fruit> fruits) {
listView.setAdapter(new FruitListAdapter(this, fruits));
}
}