美文网首页安卓MVP
Android中MVP模式实战版(一)

Android中MVP模式实战版(一)

作者: 码途有道 | 来源:发表于2018-08-17 16:16 被阅读7264次

    从入坑android至今已经有三年了,大小项目都写过,正好最近有闲,整理一下项目中的MVP。在项目开发中,架构就好比地基,地基打不好,建造出来的就是危楼!在小项目中这点可能还不是特别明显,毕竟就那么点代码,一个人就能从开发到维护做的很好!但是如果小项目扩展了呢?如果是中大型项目,多人合作配合呢?这时,无论开发还是维护,就得求稳,清晰明了,否则投入三个人的精力,可能还不如两个人干的好!所以,项目初期,架构设计尤为重要,今天就主要讲一下项目实战中的MVP设计模式。

    本文内容三步走

    1. 构建大众化的MVP基类体系(方便以后在别的项目中的使用,不必在重写)
    2. 基于MVP基类体系,根据实际项目,进行进一步封装,形成该项目型的MVP体系(主要是在基类的基础上,将项目中常用的方法等封装在基类中)
    3. 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,也有许多大公司仍然在坚持使用,而且使用的很好!需要根据实际项目需求以及 个人对这些模式的掌控力来选择项目设计模式,不一定要用最新潮,自己用的顺,用的好,那就是适合你的设计模式!

    相关文章

      网友评论

        本文标题:Android中MVP模式实战版(一)

        本文链接:https://www.haomeiwen.com/subject/fcltiftx.html