Android工程编码,直接沿用了MVC的编码结构,但是由于Activity等组件对View和Mode的高耦合,在编码过程中,经常破坏MVC的风格,使得代码结构和业务模块日益混杂,维护和复用成本越来越高。
业界提出了MVP,MVVM等,结合项目实践,如果项目已经开发成熟,不再适合导入MVVM,且风格变化稍大,对项目重构风险较高;而MVP与MVC较为接近,学习成本和重构成本在可接受范围,因此采用MVP的实践成为主流。
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过Controller。
在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用!
//1、建立bean
public class UserBean {
private StringmFirstName;
private String mLastName;
public UserBean(StringfirstName, String lastName) {
this. mFirstName = firstName;
this. mLastName= lastName;
}
public StringgetFirstName() {
return mFirstName;
}
public StringgetLastName() {
return mLastName;
}
}
//2、建立model接口(处理业务逻辑,这里指数据读写)
public interface IUserModel {
void setID(int id);
void setFirstName(String firstName);
void setLastName(StringlastName);
int getID();
UserBean load(int id);//通过id读取user信息,返回一个UserBean
}
//3、建立view接口(更新ui中的view状态),这里列出需要操作当前view的方法
public interface IUserView {
int getID();
String getFristName();
String getLastName();
void setFirstName(String firstName);
void setLastName(StringlastName);
}
//4、 建立presenter(主导器,通过iView和iModel接口操作model和view),activity可以把所有逻辑给presenter处理,这样java逻辑就从手机的activity中分离出来
public class UserPresenter {
private IUserViewmUserView;
private IUserModel mUserModel;
public UserPresenter(IUserView view) {
mUserView =view;
mUserModel = new UserModel();
}
public void saveUser( int id, String firstName, StringlastName) {
mUserModel.setID(id);
mUserModel.setFirstName(firstName);
mUserModel.setLastName(lastName);
}
public void loadUser( int id) {
UserBean user = mUserModel.load(id);
mUserView.setFirstName(user.getFirstName()); //通过调用IUserView的方法来更新显示
mUserView.setLastName(user.getLastName());
}
}
由上述示例可见,MVP也存在一些缺点,包括:
1, MVP的逻辑交互过于强调接口,如有业务增减,逻辑抽取和编码范围较大;
2, 即使简单的数据交互也不得不按原来的编码流程增加逻辑;
3, View和界面组件维护在一起,对于Activity、Fragment等生命周期重视不够,很多时候形成了Activity组件和Presenter两大主持中心。
因此我们在实践中需要进行MVP的剪裁和扩展,形成了自己的MVPplus风格。
示例工程:.myapplication
备注:该工程从MVC结构开始编码,模拟实践过程中逐步迁移到MVP,其中迁移过程可以对比MainActivity到MainMVPActivity作为参考。
这里为mvp构建了组件基类,同时清晰的展示了MVPplus的代码结构,主要涉及
l Presenter -> Activity、Fragment族
l View -> ActivityView族
l Model -> controller-logic族
l Entity -> bean族
public abstractclass BaseMvpActivity extends AppCompatActivity{
protected V mActivityView;
protected L mLogic;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
mActivityView = onCreateActivityView();
if (null!= mActivityView) {
mActivityView.contentView = mActivityView.onInitView(this);
}
setContentView(mActivityView.contentView);
mLogic =onCreateBaseLogic();
}
protected abstract V onCreateActivityView();
protected abstract L onCreateBaseLogic();
}
核心作用
l Presenter扩展界面组件
l 在presenter中将定义了两个泛型,分别为view基类和Logic基类
l 完成view和logic的定义接口,抽象给子类具体实现子view和子logic
l 完成view和Activity实现contentView的IOC控制
public abstractclass BaseLogic {
/**
* 观察者列表
*/
private List observers = new ArrayList();
/**
* 添加观察者
*/
public synchronized void addObserver(T observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
/**
* 移除观察者
*/
public synchronized void removeObserver(T observer) {
if (null != observer && observers.contains(observer)){
observers.remove(observer);
}
}
核心作用
l 提供观察者模式,维护了presenters对logic的观察添加和移除
l 提供泛型由子类定义具体的观察者
public abstractclass BaseActivityView {
public BaseActivityView() {
}
protected ViewcontentView;
public View getContentView() {
return contentView;
}
protected abstract View onInitView(Context context);
/**
* 观察者列表
*/
private List listeners = new ArrayList();
/**
* 添加观察者
*/
public synchronized void addListener(T observer) {
if (!listeners.contains(observer)) {
listeners.add(observer);
}
}
/**
* 移除观察者
*/
public synchronized void removeListener(T observer) {
if (null != observer && listeners.contains(observer)){
listeners.remove(observer);
}
}
核心作用
l 提供观察者模式,维护了presenters对view的监听添加和移除
l 提供泛型由子类定义具体的监听者
l 提供初始化方法,由子类实现一个具体的layout,一方面通过IOC供给presenter的setContentView方法,另一方面将layout抽取出Activity,在ActivityView类中独立维护
public class BaseEntity{
public BaseEntity() {
}
public int rCode;
public String rMsg;
public T rContent;
}
将具体的content通过泛型供子类定义
示例模块:.myapplication.presenter.MainMvpActivity
1, 创建MainView和MainListener
l 在onInitView()中创建具体view并返回为presenter提供具体contentView
l 提供一个实例化方法,返回给presenter控制,这里采用单例工具类实现
l MainListener提供了主要的view业务接口,供view中的事件驱动
备注:
由此,presenter对view的控制即可通过直接call成员方式,也可以通过callback方式,甚至通过eventbus消息方式,但是逻辑模块依然清晰明了。
当然,这也对Presenter的资源释放提出要求,需要做同生命周期的销毁工作,这也是良好的性能编码规范要求,详情参考presenter-Activity的onDestroy方法实现。
2,创建MainLogic和MainObserver
l 提供一个实例化方法,返回给presenter控制,这里采用单例工具类实现
l 提供了主要的logic业务接口,供presenter调用
l MainObserver提供了主要数据回调接口,供logic业务的回调
3,创建MainMvpActivity
l 实现View监听接口、实现Logic观察接口
l 实例化MainView、并添加监听
l 实例化MainLogic、并添加观察
示例,一个图片后台请求、返回前台加载显示
1,MainView中点击事件驱动
2, MainMvpActivity中响应事件
l 从监听方法中获取事件,call起Logic接口
3, MainLogic中执行异步任务回调
l 异步任务使用自定义封装的[轻量级]线程调度工具类,图片请求使用了封装的glide,参考项目widget目录。
l 返回的数据,模拟使用ImageEntity封装,使用观察者接口进行回调,回到Presenter中调用view的接口显示图片。
备注:
此处为简便,MainMvpActivity得到回调数据,不再进一步回调到MainView中,实践中可以进一步在下发中使用callback,EventBus等进一步解耦。