Android应用模板之MVP, MVVM模式
应用模板代码地址:https://github.com/thfhongfeng/AndroidAppTemplate
其实所谓架构模式,无非是一个通过接口化实现代码解耦分层的过程。而接口的实质就是一个输入输出的规范。这个规范成为各个分层代码的沟通桥梁,使得各个分层只关心自己要做的事,即告诉我你需要我提供什么(输出),并且给我相关的证明(输入),至于你要用来做什么,我不关心。
MVP模式
先上通用图
View发送指令给Presenter,Presenter获取指令后,调用响应的Model进行业务逻辑处理,然后返回数据给Presenter,Presenter根据Model返回的数据再来调用相应的View。
View:我的服务对象是用户,用户告诉我要给它提供哪些显示和响应服务,我给出相应的服务;
Presenter:我的服务对象是View,View告诉我哪些UI界面需要显示和响应,我根据它提供的内容适时的给它提供这些View所需的数据内容;
Model:我的服务对象是Presenter,Presenter告诉我需要哪些业务数据,我根据它提供的内容适时给它提供业务数据。
上图是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的两个重要的技术:
- DataBinding:完成视图实体模型与View绑定的利器。
- 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模式。