Android业务层代码结构扩展 - from MVP to MVPplus

一,背景

Android工程编码,直接沿用了MVC的编码结构,但是由于Activity等组件对View和Mode的高耦合,在编码过程中,经常破坏MVC的风格,使得代码结构和业务模块日益混杂,维护和复用成本越来越高。

二,解决思路

为什么MVP成为主流

业界提出了MVP,MVVM等,结合项目实践,如果项目已经开发成熟,不再适合导入MVVM,且风格变化稍大,对项目重构风险较高;而MVP与MVC较为接近,学习成本和重构成本在可接受范围,因此采用MVP的实践成为主流。

MVC和MVP

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:MVPView并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过Controller。

在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用!

三,一个经典简易的MVP示例

1,代码示例

//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());
    }
}


2,思考升华

由上述示例可见,MVP也存在一些缺点,包括:

1,  MVP的逻辑交互过于强调接口,如有业务增减,逻辑抽取和编码范围较大;

2,  即使简单的数据交互也不得不按原来的编码流程增加逻辑;

3,  View和界面组件维护在一起,对于Activity、Fragment等生命周期重视不够,很多时候形成了Activity组件和Presenter两大主持中心。

 

因此我们在实践中需要进行MVP的剪裁和扩展,形成了自己的MVPplus风格。

 

四,MVPplus实践

示例工程:.myapplication

 

备注:该工程从MVC结构开始编码,模拟实践过程中逐步迁移到MVP,其中迁移过程可以对比MainActivity到MainMVPActivity作为参考。

 

目录结构

 

Mvpbase目录

这里为mvp构建了组件基类,同时清晰的展示了MVPplus的代码结构,主要涉及

l  Presenter -> Activity、Fragment族

l  View -> ActivityView族

l  Model -> controller-logic族

l  Entity -> bean族

 

1,Presenter基类

public abstractclass BaseMvpActivity<V extends BaseActivityView, L extends BaseLogic> 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控制


2,Logic基类

public abstractclass BaseLogic<T> {

    /**
     * 观察者列表
     */
    private List<T> observers = new ArrayList<T>();

    /**
     * 添加观察者
     */
    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  提供泛型由子类定义具体的观察者


3,ActivityView基类

 

public abstractclass BaseActivityView<T> {

    public BaseActivityView() {
    }

    protected ViewcontentView;

    public View getContentView() {
        return contentView;
    }

    protected abstract View onInitView(Context context);

    /**
     * 观察者列表
     */
    private List<T> listeners = new ArrayList<T>();

    /**
     * 添加观察者
     */
    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类中独立维护


4,BaseEntity基类

public class BaseEntity<T>{
    public BaseEntity() {
    }
    public int rCode;
    public String rMsg;
    public T rContent;
}

将具体的content通过泛型供子类定义


5,一个模块的初始化编码示例

示例模块:.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、并添加观察

 

6,一个交互逻辑编码示例

示例,一个图片后台请求、返回前台加载显示

1,MainView中点击事件驱动

 

2, MainMvpActivity中响应事件

l  从监听方法中获取事件,call起Logic接口



3, MainLogic中执行异步任务回调

l  异步任务使用自定义封装的[轻量级]线程调度工具类,图片请求使用了封装的glide,参考项目widget目录。

l  返回的数据,模拟使用ImageEntity封装,使用观察者接口进行回调,回到Presenter中调用view的接口显示图片。


备注:

此处为简便,MainMvpActivity得到回调数据,不再进一步回调到MainView中,实践中可以进一步在下发中使用callback,EventBus等进一步解耦。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值