美文网首页
Android应用模板之MVP, MVVM模式

Android应用模板之MVP, MVVM模式

作者: 唐洪峰 | 来源:发表于2019-07-30 11:15 被阅读0次

    应用模板代码地址:https://github.com/thfhongfeng/AndroidAppTemplate

    其实所谓架构模式,无非是一个通过接口化实现代码解耦分层的过程。而接口的实质就是一个输入输出的规范。这个规范成为各个分层代码的沟通桥梁,使得各个分层只关心自己要做的事,即告诉我你需要我提供什么(输出),并且给我相关的证明(输入),至于你要用来做什么,我不关心。

    MVP模式

    先上通用图

    MVP
    View发送指令给Presenter,Presenter获取指令后,调用响应的Model进行业务逻辑处理,然后返回数据给Presenter,Presenter根据Model返回的数据再来调用相应的View。

    View:我的服务对象是用户,用户告诉我要给它提供哪些显示和响应服务,我给出相应的服务;
    Presenter:我的服务对象是View,View告诉我哪些UI界面需要显示和响应,我根据它提供的内容适时的给它提供这些View所需的数据内容;
    Model:我的服务对象是Presenter,Presenter告诉我需要哪些业务数据,我根据它提供的内容适时给它提供业务数据。


    MVP基础代码目录结构
    上图是mvp架构的各个基类,业务模块通过继承这些基类来实现mvp架构。


    IContract接口集:规范了Ui(View层)接口和Presenter(Presenter层)接口,这里没有做model层的接口化,实际上这里的接口定义并不是说一定每层都必须要,而应该是根据自己的项目来灵活处理。所谓的架构其实只是给了一个分层的规范,好比一个公司的员工组织架构:管理做规划,技术人员实现产品,客户经理推广产品,而助理就负责这些岗位的沟通工作。但有时候公司规模小或者公司性质原因,可能就不需要助理而是采用直接沟通的方式。所以这里接口化的处理是根据自己项目的性质来决定的,选择适合自己项目的接口化程度能减少项目的开发成本。
    public interface IContract {
        interface Ui {
            Activity getContextActivity();
    
            void setLoadingUiVisibility(boolean visibility);
        }
    
        interface Presenter {
    
        }
    }
    

    IContract只是定义了基本的通用的一些接口,具体到业务模块,通过继承添加自己的业务接口。


    IModelAsyncResponse数据响应接口:用于Presenter层响应Model层处理结果。即告诉Model层,我需要T对象。

    public interface IModelAsyncResponse<T> {
        void onResponse(T t);
    
        boolean onFail(Exception e);
    
        void onCancel();
    }
    
    



    Presenter基类:规范与UI(View层)的绑定与解绑,定义了一些通用的方法。业务模块中通过继承该类实现Presenter层。

    public abstract class Presenter<V extends IContract.Ui> {
        protected final String TAG = LogUtils.makeLogTag(this.getClass());
        private UiState mUiState = UiState.UI_STATE_UNDEFINE;
    
        protected boolean mIsLoadProcessing;
    
        /**
         * UI的弱引用
         */
        private WeakReference<V> mUiRef;
    
        /**
         * 关联UI
         *
         * @param ui
         */
        @CallSuper
        public void attachUi(V ui) {
            mUiRef = new WeakReference<V>(ui);
        }
    
        /**
         * 解除关联
         */
        @CallSuper
        public void detachUi() {
            if (mUiRef != null) {
                onUiState(UiState.UI_STATE_ON_DETACH);
                mUiRef.clear();
            }
        }
    
        /**
         * 得到UI
         *
         * @return
         */
        public V getUi() {
            return mUiRef.get();
        }
    
        /**
         * 得到Application
         *
         * @return
         */
        public Application getApplication() {
            return AppUtils.getApplication();
        }
    
        /**
         * 得到Activity
         *
         * @return
         */
        public Activity getActivity() {
            return getUi().getContextActivity();
        }
    
        /**
         * 得到Context
         *
         * @return
         */
        public Context getContext() {
            return getUi().getContextActivity();
        }
    
        /**
         * 得到Intent
         *
         * @return
         */
        public Intent getIntent() {
            if (mUiRef.get() != null) {
                if (mUiRef.get() instanceof Activity) {
                    return ((Activity) mUiRef.get()).getIntent();
                } else if (mUiRef.get() instanceof Fragment) {
                    return ((Fragment) mUiRef.get()).getActivity().getIntent();
                } else if (mUiRef.get() instanceof android.support.v4.app.Fragment) {
                    return ((android.support.v4.app.Fragment) mUiRef.get()).getActivity().getIntent();
                }
            }
            return new Intent();
        }
    
        public final boolean isUiAlive() {
            if (mUiRef.get() == null) {
                return false;
            }
            if (mUiRef.get() instanceof Activity) {
                return !((Activity) mUiRef.get()).isFinishing();
            }
            return true;
        }
    
        public final void finishUi() {
            if (mUiRef.get() != null)
                if (mUiRef.get() instanceof Activity) {
                    ((Activity) mUiRef.get()).finish();
                } else if (mUiRef.get() instanceof Fragment) {
                    ((Fragment) mUiRef.get()).getActivity().finish();
                } else if (mUiRef.get() instanceof android.support.v4.app.Fragment) {
                    ((android.support.v4.app.Fragment) mUiRef.get()).getActivity().finish();
                }
        }
    
        public void setUiLoading(boolean uiLoading) {
            mIsLoadProcessing = uiLoading;
            if (!isUiAlive()) {
                return;
            }
            getUi().setLoadingUiVisibility(uiLoading);
        }
    
        /**
         * 用于分析传入参数是否非法
         *
         * @return true表示非法, false表示合法
         */
        public boolean parseIntentData(@NonNull Bundle bundle) {
            return false;
        }
    
        /**
         * 用于分析传入参数是否非法,在View init之后调用
         *
         * @return
         */
        public void afterViewInit() {
    
        }
    
        /**
         * UI状态回调
         *
         * @param state UI_STATE_ON_CREATE,UI_STATE_ON_START,UI_STATE_ON_RESUME,UI_STATE_ON_PAUSE,
         *              UI_STATE_ON_STOP,UI_STATE_ON_DETACH
         */
        @CallSuper
        public void onUiState(UiState state) {
            mUiState = state;
        }
    
        public UiState getUiState() {
            return mUiState;
        }
    
        public void showShortToast(String message) {
            if (isUiAlive()) {
                Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
            }
        }
    
        public void showShortToast(@StringRes int resId) {
            if (isUiAlive()) {
                Toast.makeText(getContext(), resId, Toast.LENGTH_SHORT).show();
            }
        }
    
        public void showLongToast(String message) {
            if (isUiAlive()) {
                Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
            }
        }
    
        public void showLongToast(@StringRes int resId) {
            if (isUiAlive()) {
                Toast.makeText(getContext(), resId, Toast.LENGTH_LONG).show();
            }
        }
    
        public enum UiState {
            UI_STATE_UNDEFINE,
            UI_STATE_ON_CREATE,
            UI_STATE_ON_START,
            UI_STATE_ON_RESUME,
            UI_STATE_ON_PAUSE,
            UI_STATE_ON_STOP,
            UI_STATE_ON_DETACH
        }
    }
    



    MvpActivity基类:规范与Presenter(Presenter层)的绑定与解绑,定义了一些通用的方法。业务模块中通过继承该类实现View层。
    注意:该类继承自笔者自定义的Activity类,而非原生Activity类。自定义的Activity类实现的内容是与架构无关的,所以无需担心。如果直接继承原生Activity类,只需将beforeInitOnCreate, parseIntentData,afterInit三个方法的内容直接按顺序移到原生的Activity的onCreate方法中即可。

    public abstract class MvpActivity<V extends IContract.Ui, P extends Presenter<V>>
            extends Activity implements IContract.Ui {
        protected P mPresenter;
    
        @CallSuper
        @Override
        protected void beforeInitOnCreate(@Nullable Bundle savedInstanceState) {
            // 创建并绑定presenter
            mPresenter = createPresenter();
            if (mPresenter == null) {
                Class presenterClazz;
                Type type = getClass().getGenericSuperclass();
                if (type instanceof ParameterizedType) {
                    presenterClazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
                    try {
                        mPresenter = (P) presenterClazz.newInstance();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (mPresenter != null) {
                mPresenter.attachUi((V) this);
            } else {
                throw new RuntimeException("must initialize a presenter!");
            }
        }
    
        protected P createPresenter() {
            return null;
        }
    
        @Override
        protected final boolean parseIntentData() {
            if (mPresenter != null) {
                return mPresenter.parseIntentData(getIntent().getExtras() == null ?
                        new Bundle() : getIntent().getExtras());
            }
            return false;
        }
    
        @CallSuper
        @Override
        protected void afterInit() {
            if (mPresenter != null) {
                mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_CREATE);
                mPresenter.afterViewInit();
            }
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (mPresenter != null) {
                mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_START);
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (mPresenter != null) {
                mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_RESUME);
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (mPresenter != null) {
                mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_PAUSE);
            }
        }
    
        @Override
        protected void onStop() {
            if (mPresenter != null) {
                mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_STOP);
            }
            super.onStop();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //解除绑定
            if (mPresenter != null) {
                mPresenter.detachUi();
            }
        }
    
        @Override
        public android.app.Activity getContextActivity() {
            return this;
        }
    }
    

    MVVM模式

    先上通用图


    MVVM

    MVVM是Model-View-ViewModel的简写,即模型-视图-视图模型。【模型】指的是后端传递的数据,【视图】指的是所看到的页面,【视图模型】mvvm模式的核心,它是连接view和model的桥梁。


    MVVM模式相对于MVP模式,其实就是将接口化沟通变成了实体化(模型化)沟通。
    简单来说就是视图和业务逻辑的沟通方式不再是命令响应(接口化)的方式,而是具体化的实体模型。即VM提供给View的不再是响应型的数据,而是显示整个View的实体模型。View响应的是实体模型的改变,而不再是接口的回调。这样不仅简化原先MVP模式下Presenter层与View层的沟通成本,还将View的显示更加组件化,VM与View的绑定可以形成更微小的显示窗口,以显示窗口的聚合重用来复用代码。
    其实从命名就可以看出了,VM即View-model,也就是View的model。

    实现MVVM的两个重要的技术:

    1. DataBinding:完成视图实体模型与View绑定的利器。
    2. LiveData:完成视图实体模型的状态化,让业务响应与视图实体模型的状态关联起来。



    IModelAsyncResponse数据响应接口:用于VM层响应Model层处理结果。即告诉Model层,我需要T对象。
    这部分与MVP类似


    ViewModel视图模型:定义了一些基本的显示模型实体。业务模块中通过继承该类实现VM层。

    public abstract class ViewModel extends android.arch.lifecycle.ViewModel {
        protected final String TAG = LogUtils.makeLogTag(this.getClass());
        private UiState mUiState = UiState.UI_STATE_UNDEFINE;
    
        /**
         * UI状态回调
         *
         * @param state UI_STATE_ON_INIT,UI_STATE_ON_RESUME,UI_STATE_ON_PAUSE,
         *              UI_STATE_ON_STOP,UI_STATE_ON_DETACH
         */
        @CallSuper
        public void onUiState(UiState state) {
            mUiState = state;
        }
    
        public UiState getUiState() {
            return mUiState;
        }
    
        /**
         * 用于分析传入参数是否非法,在View init之前调用
         *
         * @return true表示非法, false表示合法
         */
        public boolean parseIntentData(@NonNull Bundle bundle) {
            return false;
        }
    
        /**
         * 在View init之后调用
         *
         * @return
         */
        public void afterViewInit() {
    
        }
    
        public void onCleared() {
    
        }
    
        MutableLiveData<Integer> observeSyncLiveData = new MutableLiveData<>();
    
        public MutableLiveData<Integer> getObserveSyncLiveDataData() {
            return observeSyncLiveData;
        }
        
        /**
         * 用于LiveData是其它功能操作返回(不是在VM中初始化赋值)的情况,
         * 在LiveData返回时通过调用setSyncLiveDataTag来告诉UI开始绑定Observer,
         * UI中的必须实现observeSyncLiveData,同时所有其它功能操作返回的LiveData只能在此方法中进行绑定Observer
         *
         * @param liveDataObjTag 用来标识对应的LiveData(由调用者自己确定)
         */
        public void setSyncLiveDataTag(int liveDataObjTag) {
            observeSyncLiveData.setValue(liveDataObjTag);
        }
    
        // 重置UI
        MutableLiveData<Boolean> resetUiData = new MutableLiveData<>();
    
        public MutableLiveData<Boolean> getResetUiData() {
            return resetUiData;
        }
    
        public void resetUi() {
            resetUiData.setValue(true);
        }
    
        // 结束UI
        MutableLiveData<Boolean> finishData = new MutableLiveData<>();
    
        public MutableLiveData<Boolean> getFinishData() {
            return finishData;
        }
    
        public void finishUi() {
            finishData.setValue(true);
        }
    
        // 加载中ui显示状态
        MutableLiveData<Boolean> uiLoadingData = new MutableLiveData<>();
    
        public MutableLiveData<Boolean> getUiLoadingData() {
            return uiLoadingData;
        }
    
        public boolean isUiLoading() {
            return uiLoadingData.getValue();
        }
    
        public void setUiLoading(boolean isLoading) {
            uiLoadingData.setValue(isLoading);
        }
    
        // Toast ui显示
        MutableLiveData<String> toastMsgData = new MutableLiveData<>();
    
        public MutableLiveData<String> getToastMsgData() {
            return toastMsgData;
        }
    
        public void setToastMsg(String msg) {
            toastMsgData.setValue(msg);
        }
    
        MutableLiveData<Integer> toastResIdData = new MutableLiveData<>();
    
        public MutableLiveData<Integer> getToastResIdData() {
            return toastResIdData;
        }
    
        public void setToastResId(@StringRes Integer id) {
            toastResIdData.setValue(id);
        }
    }
    



    MvvmActivity基类:规范与VM的绑定与解绑,定义了一些通用的方法。业务模块中通过继承该类实现View层。
    注意:该类继承自笔者自定义的Activity类,而非原生Activity类。自定义的Activity类实现的内容是与架构无关的,所以无需担心。如果直接继承原生Activity类,只需将beforeInitOnCreate,setContentView, findViewOnCreate,parseIntentData,afterInit五个方法的内容直接按顺序移到原生的Activity的onCreate方法中即可。

    public abstract class MvvmActivity<T extends ViewDataBinding, VM extends ViewModel> extends Activity {
        protected T mBinding;
        protected VM mViewModel;
    
        private Observer<Integer> mSyncLiveDataObserver = new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer tag) {
                observeSyncLiveData(tag);
            }
        };
    
        private Observer<Boolean> mResetUiDataObserver = new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {
                if (aBoolean) {
                    finish();
                    startActivity(getIntent());
                }
            }
        };
    
        private Observer<Boolean> mFinishDataObserver = new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {
                if (aBoolean) {
                    finish();
                }
            }
        };
    
        private Observer<Boolean> mUiLoadingDataObserver = new Observer<Boolean>() {
            @Override
            public void onChanged(@Nullable Boolean aBoolean) {
                setLoadingUiVisibility(aBoolean);
            }
        };
    
        private Observer<String> mToastMsgDataObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable String msg) {
                showShortToast(msg);
            }
        };
    
        private Observer<Integer> mToastResIdDataObserver = new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable Integer resId) {
                showShortToast(resId);
            }
        };
    
        @CallSuper
        @Override
        protected void beforeInitOnCreate(@Nullable Bundle savedInstanceState) {
            // 创建ViewModel{
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                Class presenterClazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
                mViewModel = (VM) ViewModelProviders.of(this).get(presenterClazz);
                mViewModel.getUiLoadingData().setValue(false);
            }
            mViewModel.getObserveSyncLiveDataData().observe(this, mSyncLiveDataObserver);
            mViewModel.getResetUiData().observe(this, mResetUiDataObserver);
            mViewModel.getFinishData().observe(this, mFinishDataObserver);
            mViewModel.getUiLoadingData().observe(this, mUiLoadingDataObserver);
            mViewModel.getToastMsgData().observe(this, mToastMsgDataObserver);
            mViewModel.getToastResIdData().observe(this, mToastResIdDataObserver);
            observeInitLiveData();
        }
    
        /**
         * 用于在VM中初始化赋值的LiveData的进行监听观察
         * 此方法在Activity onCreate的时候自动调用
         * (注意区别于observeSyncLiveData)
         * observeInitLiveData:用于在VM中初始化的LiveData的进行监听观察。
         * observeSyncLiveData :用于对不是在VM中初始化赋值的LiveData的进行监听观察,需要在VM中主动调用setSyncLiveDataTag。
         */
        public abstract void observeInitLiveData();
    
        protected void setContentView(Bundle savedInstanceState) {
            mBinding = DataBindingUtil.setContentView(this, getActivityLayoutResId());
        }
    
        @Override
        protected final void findViewOnCreate() {
    
        }
    
        @CallSuper
        @Override
        protected final boolean parseIntentData() {
            if (mViewModel != null) {
                return mViewModel.parseIntentData(getIntent().getExtras() == null ?
                        new Bundle() : getIntent().getExtras());
            }
            return false;
        }
    
        @CallSuper
        @Override
        protected void afterInit() {
            if (mViewModel != null) {
                mViewModel.onUiState(UiState.UI_STATE_ON_INIT);
            }
            if (mViewModel != null) {
                mViewModel.afterViewInit();
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (mViewModel != null) {
                mViewModel.onUiState(UiState.UI_STATE_ON_RESUME);
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (mViewModel != null) {
                mViewModel.onUiState(UiState.UI_STATE_ON_PAUSE);
            }
        }
    
        @Override
        protected void onStop() {
            if (mViewModel != null) {
                mViewModel.onUiState(UiState.UI_STATE_ON_STOP);
            }
            super.onStop();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //解除绑定
            if (mViewModel != null) {
                mViewModel.onUiState(UiState.UI_STATE_ON_DETACH);
            }
        }
    
        public void setLoadingUiVisibility(boolean visibility) {
    
        }
    
        /**
         * 对不是在VM中初始化赋值的LiveData的进行监听观察(通过其它功能返回的LiveData)。
         * 此方法的调用需要在VM获取到LiveData后中主动调用setSyncLiveDataTag方法。
         * (注意区别于observeInitLiveData)
         * observeInitLiveData:用于在VM中初始化的LiveData的进行监听观察。
         * observeSyncLiveData :用于对不是在VM中初始化赋值的LiveData的进行监听观察,需要在VM中主动调用setSyncLiveDataTag。
         *
         * @param liveDataObjTag 用来标识对应的LiveData(由调用者自己标识)
         */
        public abstract void observeSyncLiveData(int liveDataObjTag);
    }
    



    各种模式只是规范化编码方式的一种总结,千万不要为了模式而模式,要根据自己的项目特性和规模,选择合适自己的模式。一般来说模式的开发是针对业务模块的,不同的业务模块根据自己的需求可以使用不同的模式来开发。
    比如:
    业务重用较多的模块,可以使用MVP模式。
    视图重用较多或者业务较复杂的模块,可以使用MVVM模式。
    简单的过渡模块,直接Android的默认开发模式就可以了。
    不过,一般的模块开发,还是建议使用MVVM模式。

    相关文章

      网友评论

          本文标题:Android应用模板之MVP, MVVM模式

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