日常笔记 - MVP
1 前言
MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。
2 基本实现
在实现MVP模式的过程中,会将Model、View、Presenter三者的行为(方法)抽象成接口,三者之间通过接口来相互调用,可以有效的降低耦合度
(1)Model(数据模型):Model就是数据模型,为View提供填充的数据,我们一般使用容器类来存储从网络或者本地的数据
public class IModel {
// 定义接口方法,提供给 Presenter 来调用
List<String> getData(boolean isNetWork);
}
public class ModelImpl implements IModel {
@Override
public List<String> getData(boolean isNetWork) {
List<String> data = new ArrayList<>();
if (!isNetWork) {
for (int i = 0; i < 10; i++) {
data.add("item" + i);
}
} else {
// 从网络获取数据转换成 List<String> 的数据类型就可以了
}
return data;
}
}
(2)Presenter(逻辑控制器):Presenter其实就是MVC中的控制器,用来将Model与View进行连接的 “房屋中介”,要当好中介就得与View和Model都有联系,即拥有其余两者的引用,来调用两者的方法
public interface IPresenter {
// 定义 Presenter 的接口方法,提供给 View 来调用
void start();
void refresh();
}
public class PresenterImpl implements IPresenter {
private IView iView;
private IModel iModel;
/**
* 我们将 View 从 Presenter 的构造方法中传入,来将 Presenter 所持有的 IView 进行实例化,以便调用 View 中定义的方法
*
* 除此之外,我们还将 IModel 进行实例化,以便调用 ModelImpl 中实现的方法
*
* @param iView
*/
public PresenterImpl(IView iView) {
this.iView = iView;
this.iModel = new ModelImpl();
}
@Override
public void start() {
// 此处调用 View 的初始化方法
iView.init();
}
@Override
public void refresh() {
// 此处调用 View 的刷新方法
iView.refresh(iModel.getData(false));
}
}
(3)View(视图):View就是我们在Android设备中所看到的界面了,但是我们在View中不会处理一点逻辑,所有的逻辑都放在Presenter中进行调用,所以View中必须持有Presenter的对象,来调用PresenterImpl中实现的方法
public interface IView {
// 从 View 中抽象出来的数据方法,提供给 Presenter 来调用
void init();
void refresh(List<String> data);
}
// 我们一般使用 Activity 或者 Fragment 来作为 IView 的实现类
public class MainActivity extends AppCompatActivity implements IView {
private IPresenter presenter;
private ListView lv;
private List<String> data;
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 在此处将 IPresenter 接口的对象实例化为 PresenterImpl 类的对象,以调用 PresenterImpl 中实现的方法
presenter = new PresenterImpl(this);
// 进行初始化
presenter.start();
}
@Override
protected void onResume() {
super.onResume();
// 进行刷新
presenter.refresh();
}
@Override
public void init() {
initView();
loadData();
}
public void initView() {
lv = findViewById(R.id.list);
}
public void loadData() {
data = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
lv.setAdapter(adapter);
}
@Override
public void refresh(List<String> data) {
this.data.addAll(data);
this.adapter.notifyDataSetChanged();
}
}
3 进阶使用
从上面的分析可以得知,MVP将数据与视图进行分离,通过Presenter来进行逻辑控制,这样充分的进行解耦,在进行修改的时候,不会再牵一发而动全身了,优点十分明显,但是还是存在一些缺陷,比如类过多,比如内存泄漏,下面针对问题进行解决
(1)使用MVP我们会发现定义了大量的接口,并不是很好地进行管理,Google爸爸提出了契约类的方式来对定义的接口进行管理
// 具体写法就是将上面定义的接口放在 Contract 中进行统一管理,这样我们就不用再管理大量接口
public interface Contract {
interface IModel {
List<String> getData(boolean isNetWork);
}
interface IPresenter {
void start();
void refresh();
}
interface IView {
void init();
void refresh(List<String> data);
}
}
(2)当我们网络数据的时候,会执行工作线程来获取数据,若是View突然关闭,但是工作线程还未执行完毕,就会导致内存泄漏,针对这种情况,可以使用软引用将View的强引用进行包装使用,除此之外,还可以将Presenter与View的生命周期进行绑定,当View销毁的同时,也将Presenter进行销毁
public interface Contract {
interface IModel {
List<String> getData(boolean isNetWork);
}
interface IPresenter {
// 绑定
void attachView(IView view);
// 解绑
void detachView();
void start();
void refresh();
}
interface IView {
void init();
void refresh(List<String> data);
}
}
public class PresenterImpl implements Contract.IPresenter {
private Contract.IModel iModel;
// 使用软引用确保在进行垃圾回收的时候将内存进行回收
private SoftReference<Contract.IView> softReference;
@Override
public void attachView(Contract.IView view) {
this.iModel = new ModelImpl();
this.softReference = new SoftReference<>(view);
}
@Override
public void detachView() {
if (softReference != null) {
softReference.clear();
}
}
private Contract.IView getView() {
return softReference.get();
}
@Override
public void start() {
getView().init();
}
@Override
public void refresh() {
getView().refresh(iModel.getData(false));
}
}
public class ModelImpl implements Contract.IModel {
@Override
public List<String> getData(boolean isNetWork) {
List<String> data = new ArrayList<>();
if (!isNetWork) {
for (int i = 0; i < 10; i++) {
data.add("item" + i);
}
} else {
// 从网络获取数据转换成 List<String> 的数据类型就可以了
}
return data;
}
}
public class MainActivity extends AppCompatActivity implements Contract.IView {
private ListView lv;
private List<String> data;
private ArrayAdapter<String> adapter;
private Contract.IPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new PresenterImpl();
presenter.attachView(this);
presenter.start();
}
@Override
protected void onResume() {
super.onResume();
presenter.refresh();
}
@Override
public void init() {
initView();
loadData();
}
public void initView() {
lv = findViewById(R.id.list);
}
public void loadData() {
data = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
lv.setAdapter(adapter);
}
@Override
public void refresh(List<String> data) {
this.data.addAll(data);
this.adapter.notifyDataSetChanged();
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachView();
}
}
4 注意事项
在使用MVP模式进行开发的时候需要记得进行内存泄漏处理