Android MVP 模式 架构 参考

Android MVP 模式 架构 参考

 

Model-View-Presenter(MVP)

MVP的概念及优缺点网上一堆,如果大家不了解的可以去百度下,MVP只是个思想,没有固定的铁则,所以不同人对于MVP也有自己的理解,下面是本人对于MVP的理解(偏向于Passive View

  • View仅仅负责实现单纯的、独立的UI操作,尽量不要去维护数据(View层指ActivityFragment这类层级)
  • Model负责处理数据请求、业务逻辑,不涉及UI操作
  • PresenterMVP体系的控制中心,负责给ViewModel安排工作 ,什么时候调用Model处理逻辑,什么时候调用View反应结果,都是Presenter说了算
  • ViewModel均已接口的形式出现在Presenter中,Presenter通过调用 ViewModel的实现接口,来操作 ViewModel;同时Presenter也是以借口的形式出现在View中,这样PresenterView就是通过接口相互依赖了
  • Presenter是主动方,View是被动方,对于绑定到View上的数据,不是View调用Presenter主动拉取数据,而是Presenter主动将数据推给View

其调用顺序如下:

  1. 用户操作了界面
  2. View层响应用户操作,然后向Presenter层发出请求
  3. Presenter层接受了View层的请求,调用Model层处理业务逻辑,然后调用View层将相应的结果反应出来

MVP的基类体系

上面说了那么多,那么如何在项目中构造MVP呢?不急,下面就是了~
首先我们先来构造一下MVP的基类体系,毕竟如果每个项目都重新写一套MVP还是很麻烦的,构造一个MVP的基类体系,再根据项目,基于已有的MVP的基类体系,构造针对项目的MVP才是王道,省时省力,同时基类体系很可以帮我们更清晰的理解MVP

View层的接口基类

这是ActivityFragment需要实现的基类接口,里面只是实现了一个获取Activity的方法,主要用于在Presenter中需要使用Context对象时调用,不直接在Presenter中创建Context对象,最大程度的防止内存泄漏

public interface IBaseXView {

    /**
     * 获取 Activity 对象
     *
     * @return activity
     */
    <T extends Activity> T getSelfActivity();
}

Presenter层的接口基类

Presenter需要实现的接口

public interface IBaseXPresenter {

    /**
     * 判断 presenter 是否与 view 建立联系,防止出现内存泄露状况
     *
     * @return {@code true}: 联系已建立<br>{@code false}: 联系已断开
     */
    boolean isViewAttach();

    /**
     * 断开 presenter 与 view 直接的联系
     */
    void detachView();
}

View层的基类实现

View中通过IBaseXPresenter,来实现ViewPresenter的依赖,同时做了内存泄漏的预防处理。Activity通过getPresenter()来调用Presenter。另外,对于Fragment也可以仿照这样写。

public abstract class BaseXActivity<P extends IBaseXPresenter> extends Activity implements IBaseXView {
    private P mPresenter;
    
    /**
     * 创建 Presenter
     *
     * @return
     */
    public abstract P onBindPresenter();
    
    /**
     * 获取 Presenter 对象,在需要获取时才创建`Presenter`,起到懒加载作用
     */
    public P getPresenter() {
        if (mPresenter == null) {
            mPresenter = onBindPresenter();
        }
        return mPresenter;
    }

    @Override
    public Activity getSelfActivity() {
        return this;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        /**
         * 在生命周期结束时,将 presenter 与 view 之间的联系断开,防止出现内存泄露
         */
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}

Presenter中的基类实现

Presenter中通过IBaseXView,来实现PresenterView的依赖,当每次对View进行调用时,先使用isViewAttach判断一下,PresenterView之间的联系是否还在,防止内存泄漏。Presenter通过View暴露的接口IBaseXView,来控制View

public class BaseXPresenter<V extends IBaseXView> implements IBaseXPresenter {
    // 防止 Activity 不走 onDestory() 方法,所以采用弱引用来防止内存泄漏
    private WeakReference<V> mViewRef;

    public BaseXPresenter(@NonNull V view) {
        attachView(view);
    }

    private void attachView(V view) {
        mViewRef = new WeakReference<V>(view);
    }

    public V getView() {
        return mViewRef.get();
    }

    @Override
    public boolean isViewAttach() {
        return mViewRef != null && mViewRef.get() != null;
    }

    @Override
    public void detachView() {
        if (mViewRef != null) {
            mViewRef.clear();
            mViewRef = null;
        }
    }
}

以上是对ViewPresenter做处理,可以非常明确的看出他们之间的依赖关系,而对于Model,一般涉及到具体逻辑,数据请求在基类的初步构造中不用创建~~

具体项目中的MVP体系

上面是对广大项目的MVP体系进行构造,但是因为项目与项目之间的不同,每个项目都有自己独特的需求,如果直接使用上述的MVP体系,虽说也可以,但是却没有给开发者带来最大程度的便利,仅仅是打了个大众地基,我们还需要为我们要建造的大楼,添上大楼独特的建造需求!

View的接口

没啥好说的,直接继承,再针对实际项目补充一些会常用到的方法

public interface IBaseView extends IBaseXView {

   /**
    * 显示正在加载 view
    */
    void showLoading();
    
    /**
     * 关闭正在加载 view
     */
    void hideLoading();
    
    /**
     * 显示提示
     * @param msg
     */
    void showToast(String msg);
}

View的实现

实现IBaseView中方法,并继承BaseXActivity

public abstract class BaseActivity<P extends IBasePresenter> extends BaseXActivity<P> implements IBaseView {
    // 加载进度框
    private ProgressDialog mProgressDialog;
    
    @Override
    public void showLoading(){
        ......
    }
    
    @Override
    public void hideLoading(){
        ......
    }
    
    @Override
    public void showToast(String msg){
        ......
    }
    
    @Override
    protected void onDestroy() {
        hideLoading();
        super.onDestroy();
    }
}

Presenter的接口

本人在实际项目中对网络请求的取消用的也频繁,所以写上,你根据实际情况自己补充

public interface IBasePresenter extends IBaseXPresenter {

   /**
     * 取消网络请求
     *
     * @param tag 网络请求标记
     */
    void cancel(Object tag);

    /**
     * 取消所有的网络请求
     */
    void cancelAll();
}

Presenter的实现

这里不止实现了IBasePresenter,,还实现了HttpResponseListener,网络请求响应接口

public abstract class BasePresenter<V extends IBaseView, T> extends BaseXPresenter<V> implements IBasePresenter, HttpResponseListener<T>{

    public BasePresenter(@NonNull V view) {
        super(view);
    }
    
      @Override
    public void cancel(@NonNull Object tag) {
        ......
    }

    @Override
    public void cancelAll() {
        ......
    }
    
    /**
     * 来自于 HttpResponseListener
     */
    @Override
    public void onSuccess(Object tag, T t) {
        
    }

    @Override
    public void onFailure(Object tag, HttpFailure failure) {

    }
}

HttpResponseListener

public interface HttpResponseListener<T> {
    /**
     * 网络请求成功
     *
     * @param tag 请求的标记
     * @param t   返回的数据
     */
    void onSuccess(Object tag, T t);

    /**
     * 网络请求失败
     *
     * @param tag     请求的标记
     * @param failure 请求失败时,返回的信息类
     */
    void onFailure(Object tag, HttpFailure failure);
}

Model的实现

在项目中实现常用的发送网络请求的方法,本人在项目中使用的是Retrofit + RxJava

public class BaseModel {

    /**
     * 发送网络请求
     *
     * @param observable
     * @param callback
     * @param <T>
     */
    protected <T> void sendRequest(@NonNull Observable<T> observable, HttpResponseListener<T> callback) {
        ......
    }

    /**
     * 发送网络请求
     *
     * @param tag
     * @param observable
     * @param callback
     * @param <T>
     */
    private <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, HttpResponseListener callback) {
        ......
    }
    
    /**
     * 发送网络请求
     *
     * @param observable 被观察者
     * @param observer   观察者
     * @param <T>
     */
    protected <T> void sendRequest(@NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
       ......
    }

    /**
     * 发送网络请求
     *
     * @param tag        请求标记
     * @param observable 被观察者
     * @param observer   观察者
     * @param <T>
     */
    protected <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
       ......
    }
}

MVP在具体页面中的实战

上面已经建立好了我们的MVP了,下面就是在真实页面中使用了,是时候来一波操作了,let's go!!! 下面就以经典的登录页面来举例。

  • 根据google的建议,在使用MVP时,我们可以建立一个契约类Contacts
public final class LoginContacts {
   /**
     * view 层接口
     */
    public interface LoginUI extends IBaseView {
        /**
         * 登录成功
         */
        void loginSuccess();

        /**
         * 登录失败
         */
        void loginFailure();
    }

    /**
     * presenter 层接口
     */
    public interface LoginPtr extends IBasePresenter{
        void login(String username, String password);
    }

    /**
     * model 层接口
     */
    public interface LoginMdl {
        void login(String username, String password, HttpResponseListener callbak);
    }
}

其实,就是将ViewPresenterModel中的实现接口写在一起,看起来更加规范清晰,方便查找

  • LoginModel
public class LoginMdl extends BaseModel implements LoginContacts.LoginMdl{
    
    /**
     * 登录
     *
     * @param username 用户名
     * @param password 密码
     * @param callbak  网络请求回调
     */
    @Override
    public void login(String username, String password, HttpResponseListener callbak) {
        HashMap<String, String> map = new HashMap<>();
        map.put("username", username);
        map.put("password", Md5Util.encrypt(password));
        RequestBody body = ReqBodyHelper.createJson(map);
        // 发送网络请求
        sendRequest(HttpUtils.getApi().getLoginInfo(body),callbak);
    }
}
  • LoginPtr
public class LoginPtr extends BasePresenter<LoginContacts.LoginUI, LoginBean> implements LoginContacts.LoginPtr, HttpResponseListener<LoginBean> {
    private LoginContacts.LoginMdl mLoginMdl;

    public LoginPtr(@NonNull LoginContacts.LoginUI view) {
        super(view);
        // 实例化 Model 层
        mLoginMdl=new LoginMdl();
    }

    @Override
    public void login(String username, String password) {
        //显示进度条
        showLoading();
        mLoginMdl.login(username,password,this);
    }

    @Override
    public void onSuccess(Object tag, LoginBean t) {
        // 先判断是否已经与 View 建立联系
        if (isViewAttach()) {
            // 隐藏进度条
            hideLoading();
            // 登录成功调用
            getView().loginSuccess();
        }
    }

    @Override
    public void onFailure(Object tag, HttpFailure failure) {
        if (isViewAttach()) {
            // 隐藏进度条
            hideLoading();
            // 登录失败调用
            getView().loginFailure();
        }
    }
}
  • LoginActivity
public class LoginActivity extends BaseActivity<LoginContacts.LoginPtr> implements LoginContacts.LoginUI {
    private Button btn_login;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        btn_login=findViewById(R.id.btn_login);
        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 向 Presenter 层发送登录请求
                getPresenter().login("admin","123456");
            }
        });
    }

    @Override
    public LoginContacts.LoginPtr onBindPresenter() {
        return new LoginPtr(this);
    }

    @Override
    public void loginSuccess() {
        Toast.makeText(this,"登录成功",Toast.LENGTH_LONG).show();
    }

    @Override
    public void loginFailure() {
        Toast.makeText(this,"登录失败",Toast.LENGTH_LONG).show();
    }
}

总结

MVP是一种思想,并不是指某种固定框架,每个人都有自己独特的理解。当前市场上除了MVP,还有MVCMVVM以及他们的变种模式!特别是当MVVM的出现,倍受开发者的推崇,可能会让人觉得MVP已经没落!其实不然,没有哪种模式是没落的,哪怕是MVC,也有许多大公司仍然在坚持使用,而且使用的很好!需要根据实际项目需求以及 个人对这些模式的掌控力来选择项目设计模式,不一定要用最新潮,自己用的顺,用的好,那就是适合你的设计模式!


链接:https://www.jianshu.com/p/19283a3f61de
 

猜你喜欢

转载自blog.csdn.net/MLQ8087/article/details/86231851