Android 架构:MVP实践
概念
MVP分三层
M:Model包含具体的数据请求数据源,主要负责
1 :从网络,数据库,文件,传感器,等第三放数据源读取数据
2 :对外部数据类型进行解析,转换为app内部数据
3 : 对数据的临时储存,管理,协调上层数据请求
V :View主要负责处理UI,不包含页面逻辑,对应Activity和Fragment
1 :提供UI交互
2 :在presenter控制下修改UI
3 :将业务事件交由presenter处理
注意View层不储存数据,不与Model层进行交互
P :Persenter 业务处理层,既能调用UI逻辑,又能请求数据,理论上,这层为纯java类,不包含Android Api
三层调用顺序为 View→Presenter→Model 为安全着想不能反向调用,不能跨级调用
那么三层该如何交互呢?
从上图可以看出,下层会通过CallBack和View接口对上层进行反馈,上图的View和Callback,是以接口的形式存在的,View是经典MVP定义的,Callback是自己加的
View中定义了,Activity的具体操作,主要是将请求的数据进行界面更新
Callback 定义了数据请的各种状态,成功,失败,异常等
下面看一个简单的例子
View接口
public interface MvpView {
/**
* 定义ui处理的接口,为Presenter提供操作ui的方法
*/
void showLoading();
void dissMissLoading();
void showData(String msg);
}
Callback接口
public interface MvpCallBack {
/**
* 定义请求数据的状态
* @param data
*/
void onSuccess(String data);
void onFailure();
}
Persenter
public class MvpPresenter {
private final MvpView mView;
public MvpPresenter(MvpView view) {
//和View的交互
this.mView = view;
}
public void getDtata() {
mView.showLoading();
//和Model的交互
MvpModel.getData(new MvpCallBack() {
@Override
public void onSuccess(String data) {
mView.dissMissLoading();
mView.showData(data);
}
@Override
public void onFailure() {
}
});
}
}
Model
public class MvpModel {
/**
* 模拟数据请求
* @param callBack
*/
public static void getData(final MvpCallBack callBack) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
callBack.onSuccess("请求成功");
}
}, 2000);
}
}
Activity
public class MVP1Activity extends AppCompatActivity implements MvpView {
private TextView mTextView;
private Button mButton;
private ProgressDialog mProgressDialog;
private MvpPresenter mMvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp1);
initView();
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("正在加载数据");
mProgressDialog.setCancelable(false);
mMvpPresenter = new MvpPresenter(this);
}
private void initView() {
mButton = (Button) findViewById(R.id.button);
mTextView = (TextView) findViewById(R.id.text_view);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mMvpPresenter.getDtata();
}
});
}
@Override
public void showLoading() {
mProgressDialog.show();
}
@Override
public void dissMissLoading() {
mProgressDialog.dismiss();
}
@Override
public void showData(String msg) {
mTextView.setText(msg);
}
}
优化MVP
优化Persenter
1 当每次请求数据完成回调View接口的时候,可能Activity销毁,导致异常,为了避免这种情况我,我们需要把Persenter和Activity的生命周期绑定,例如:
2 抽取Persenter基类,把通用的方法抽取出来,利用泛型强制传进来的是BaseView类型
public class BasePresenter<V extends BaseView> {
private V mView;
public void attchView(V view) {
this.mView = view;
}
public void detachView() {
mView = null;
}
public V getView() {
return mView;
}
public boolean isViewAttch() {
return mView != null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp2);
initView();
mMvpPersenter = new MvpPersenter();
//绑定
mMvpPersenter.attchView(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//销毁
mMvpPersenter.detachView();
}
优化View层
1 抽取View的基类,因为很多方法在每个Activity都会用到,抽取出来,例如:显示Loading和隐藏Loading,每个具体的View接口需要继承基类并且扩展自己的方法
2 抽取BaseActivity 实现BaseView,因为这些方法是通用方法所以直接抽取出来实现,具体的Activity直接继承BaseActivity,就不用重复实现就可以直接调用,例如:直接调用显示Loading
public interface BaseView {
void showLoading();
void dissMissLoading();
}
public interface MvpView extends BaseView {
void showData(String msg);
}
public class Base1Activity extends AppCompatActivity implements BaseView {
private ProgressDialog mProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base1);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setCancelable(false);
mProgressDialog.setMessage("正在加载数据");
}
@Override
public void showLoading() {
mProgressDialog.show();
}
@Override
public void dissMissLoading() {
mProgressDialog.dismiss();
}
}
public class Mvp2Activity extends Base1Activity implements MvpView {
private Button mButton;
private TextView mTextView;
private MvpPersenter mMvpPersenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp2);
initView();
mMvpPersenter = new MvpPersenter();
mMvpPersenter.attchView(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mMvpPersenter.detachView();
}
private void initView() {
mButton = (Button) findViewById(R.id.button2);
mTextView = (TextView) findViewById(R.id.text_view2);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mMvpPersenter.getData();
}
});
}
@Override
public void showData(String msg) {
mTextView.setText(msg);
}
}
优化Callback
在上面的简单版中,Callback的onSuccess(String data) 方法中,需要根据返回的不同数据类型设置不同的参数,所以每当有新的数据类型就要新建一个新的Callback ,为了解决这个问题我们引入泛型解决问题,让调用者去定义想要接受的数据类型
public interface Callback<T> {
void onSuccess(T data);
void onFailure();
}
优化Model层
优化后的model我们希望他能解决下面几个问题
1 当项目越来越大,model也会越来也多,不容易统计
2 无法对所有的model进行统一管理
3 每个model获取数据的方式不一样,上层没有定义统一规范
代码实现
我们需要增加这几个类
1 ModelManager :数据层*入口,项目所有的数据都由该类流入和流出,负责分发管理所有的Model
2 Token:数据请求标识类,定义项目中所有的Model,方便统计
3 BaseModel: 所有Model的*父类,负责对外提供数据请求规范,对内为所有的Modle提供底层支持
ModelManager
利用工厂模式,对外提供需要的对象
public class ModleManager {
public static <T extends BaseModel> T request(Class<T> clazz) {
T t = null;
try {
t = (T) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return t;
}
}
Token
public class Token {
public static final Class sMvpModleClass= MvpModle.class;
}
BaseModel
public abstract class BaseModel<T> {
//数据参数
public String[] params;
public BaseModel params(String... arg) {
params = arg;
return this;
}
//添加callback并执行数据请求
//具体数据请求由子类实现
public abstract void excute(Callback<T> callback);
}
具体的model
public class MvpModle extends BaseModel<String> {
//模仿数据请求
@Override
public void excute(final Callback<String> callback) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
callback.onSuccess(params[0]);
}
}, 2000);
}
}
外部调用的形式
public class MvpPersenter extends BasePresenter<MvpView> {
public void getData() {
getView().showLoading();
ModleManager
//设置请求标识Token
.request(Token.sMvpModleClass)
//设置参数
.params("更改之后")
//执行
.excute(new Callback<String>() {
@Override
public void onSuccess(String data) {
if (isViewAttch()) {
getView().dissMissLoading();
MvpView view = getView();
view.showData(data);
}
}
@Override
public void onFailure() {
}
});
}
}
添加一个新Model
1 继承BaseModel ,并实现具体数据请求
2 把Model添加到Token中
MVP的优缺点
优点
1 降低耦合度,实现了View和Model的完全分离,可以修改View而不影响Model
2 模块职责划分明显,层次清晰
3 Presenter 可以复用,一个Presenter可以用于多个View
4 View可以进行组件化
缺点
1 Presenter中除了应用逻辑外,还有大量的View→Model 和Model→View,的手动同步逻辑,造成Presenter比较笨重,维护起来比较困难
2 Persenter和View交互太过于频繁,以至于View一改变Presenter也需要改变
3 增加大量的类和接口
代码已经上传到GitHubgithub
参考 https://www.jianshu.com/p/9a6845b26856
http://www.jcodecraeer.com/a/anzhuokaifa/2017/1024/8636.html