Android-实用的MVP
前言
在MVP模式刚出来的时候,也看了很多关于这方面的介绍。说实话,看完之后只是有了大概的了解,让我自己去写一个还真弄不出来。因为项目是灵活多变且复杂的,要想熟练使用MVP模式还得自己去尝试多练,渐渐改进和优化。
开始
接下来的内容是我对自己项目的一种总结。因为水平有限,如果有理解有误的地方,还请指正,以免误导他人。
① View
我为什么会先说View,因为View是可见的,可以与用户交互的,会给我们一种最直观的感受。
MvpView:
这是我对View的一种简单封装,里面包含了进入页面时的loading、在当前页面可能出现的错误或提示信息、还有就是对键盘可见性的操作。这里大家根据自己的业务需求进行修改和添加。
public interface MvpView {
void showLoading();
void hideLoading();
void onError(@StringRes int resId);
void onError(String message);
void showMessage(@StringRes int resId);
void showMessage(String message);
void hideKeyboard();
}
② Presenter
这里我的Presenter关联了Activity/Fragment的重要生命周期方法。还对页面中出现的错误进行了统一的管理。
public interface MvpPresenter<V extends MvpView> {
void onAttach(V mvpView);
void onDetach();
boolean handleApiError(String error);
void handleException(Throwable throwable);
void setUserLoggedOut();
}
下面咱们对该Presenter进行实现。
public class BasePresenter<V extends MvpView> implements MvpPresenter<V> {
private Reference<V> mViewRef;
private final SchedulerProvider mSchedulerProvider;
private CompositeDisposable mCompositeDisposable;
public BasePresenter() {
//获取线程调度器实例
mSchedulerProvider = AppSchedulerProvider.getInstance();
}
//实例化CompositeDisposable,用来统一管理请求
protected void addDisposable(Disposable disposable) {
if (mCompositeDisposable == null) {
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(disposable);
}
/**
* 取消请求
*/
private void dispose(){
if (mCompositeDisposable != null) {
mCompositeDisposable.dispose();
}
}
@Override
public void onAttach(V mvpView) {
mViewRef = new WeakReference(mvpView);
}
//清除请求、置空view引用
@Override
public void onDetach() {
dispose();
ReferenceClear();
}
/**
* 清除View引用
*/
private void ReferenceClear(){
if (mViewRef != null) {
mViewRef.clear();
mViewRef = null;
}
}
public boolean isViewAttached() {
return mViewRef != null;
}
public V getMvpView() {
return mViewRef.get();
}
public SchedulerProvider getSchedulerProvider(){
return mSchedulerProvider;
}
public CompositeDisposable getCompositeDisposable(){
return mCompositeDisposable;
}
public void checkViewAttached() {
if (!isViewAttached())
throw new MvpViewNotAttachedException();
}
//这里可以对错误进行分类处理
@Override
public boolean handleApiError(String content) {
boolean error = ApiEmptyError.isError(content);
boolean isDialogShow = ApiEmptyError.getIsDialogShow();
boolean needAgainLog = ApiEmptyError.getIsNeedAgainLog();
String msg = JsonParse.getSuccessStatusMsg(content, "msg");
if (error && isDialogShow) {//以弹框形式提示
return true;
}else if(error && needAgainLog){//需要重新登录
setUserAsLoggedOut();
return true;
}else if(error){//普通提示,直接toast
getMvpView().onError(msg);
return true;
}
return false;
}
/**
* 处理Throwable类型,如网络异常,解析异常等
* @param throwable
*/
@Override
public void handleException(Throwable throwable) {
String throwstr = ApiException.setThrowable(throwable);
LogUtil.debug("handleException----->"+throwstr);
}
/**
* 清除个人相关信息
*/
@Override
public void setUserLoggedOut() {
//TODO 清除用户数据
}
public static class MvpViewNotAttachedException extends RuntimeException {
public MvpViewNotAttachedException() {
super("在Presenter请求数据之前,请先调用Presenter.onAttach(MvpView)");
}
}
}
接下来,我们该实现接口里的方法了。
public abstract class MvpBaseFragment<V extends MvpView, P extends MvpPresenter> extends Fragment implements MvpView {
protected P mPresenter;
protected List<P> mListPresenter;
protected ProgressDialog mProgressDialog;
//创建Presenter
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mListPresenter = new ArrayList<>();
if (mPresenter == null) {
mPresenter = createPresenter();
}
if (mListPresenter == null) {
mListPresenter = createListPresenter();
}
}
//销毁Presenter
@Override
public void onDestroyView() {
super.onDestroyView();
if (mPresenter != null) {
mPresenter.onDetach();
}
if (mListPresenter != null && mListPresenter.size() > 0) {
for (P p : mListPresenter) {
p.onDetach();
}
mListPresenter.clear();
}
}
//显示Loading
@Override
public void showLoading() {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(getActivity());
}
mProgressDialog.show();
}
//隐藏Loading
@Override
public void hideLoading() {
if (mProgressDialog != null) {
mProgressDialog.cancel();
}
}
//显示resId错误信息
@Override
public void onError(int resId) {
}
//显示string错误信息
@Override
public void onError(String message) {
showShortToast(message);
}
//显示resId提示信息
@Override
public void showMessage(int resId) {
}
//显示string提示信息
@Override
public void showMessage(String message) {
showMessage(message);
}
//隐藏软键盘
@Override
public void hideKeyboard() {
}
//创建Presenter集合
protected List<P> createListPresenter(){
return mListPresenter;
}
//单个Presenter
protected P createPresenter(){
return mPresenter;
}
}
上面我使用了ArrayLIst来存储多个Presenter。因为我在实际使用的时候,如果页面的内容很多的时候,我用一个Presenter时,所有的东西都添加到这一个Presenter里面,还是会显得很臃肿。所以我就想着拆开成多个Presenter来处理,让多个Presenter来分担业务。
好了,大体的封装咱们完成了,下面就是具体的使用了。
public class HomeFragment extends MvpBaseFragment implements HomeMvpView {
private HomePresenter<HomeMvpView> mPresenter;
@Override
public void beforeViewData() {
}
@Override
public View initLayout(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return LayoutInflater.from(getActivity()).inflate(R.layout.fragment_home_layout, container,false);
}
@Override
public void initView(View view, Bundle savedInstanceState) {
initPresenter();
}
//实例化Presenter
private void initPresenter(){
mPresenter = new HomePresenter<>();
mPresenter.onAttach(this);
}
//展示请求的结果
@Override
public void showBanner(List<BannerImageInfo> adInfos) {
}
//开始执行下载操作
@Override
public void requestNetData() {
mPresenter.downloadBanner();
}
@Override
public void onClick(View view) {
}
//返回Presenter实例,用于清理操作
@Override
protected MvpPresenter createPresenter() {
return mPresenter;
}
}
HomeMvpView.java
//具体View实现
public interface HomeMvpView extends MvpView{
void showBanner(List<BannerImageInfo> adInfos);
}
HomeMvpPresenter.java
//具体业务需求
public interface HomeMvpPresenter<V extends HomeMvpView & MvpView> extends MvpPresenter<V> {
/***
* 下载Banner
*/
void downloadBanner();
}
HomePresenter.java
public class HomePresenter<V extends HomeMvpView & MvpView> extends BasePresenter<V> implements HomeMvpPresenter<V> {
@Override
public void downloadBanner() {
addDisposable(HttpMethods.getInstance().getApiHepler()
.downloadBanner(HttpMethods.BASE_URL + "/v1/default/slider")
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(new DefaultConsumerAccept(){
@Override
public void onSuccess(String response) {
super.onSuccess(response);
BannerImageModel model = JsonParse.parseJson(response, BannerImageModel.class);
ArrayList<BannerImageInfo> list = model.getData().getList();
getMvpView().showBanner(list);
}
@Override
public void showErrorDialog() {
super.showErrorDialog();
}
@Override
public void setUserLoggedOut() {
super.setUserLoggedOut();
}
@Override
public void showErrorMsg(String msg) {
super.showErrorMsg(msg);
}
}));
}
在MvpPresenter那块儿我把异常处理封装在里面,不过,后来我发现了一种更好的方式去处理异常和错误(这里大家根据自己的习惯去处理就好)
。就是去封装一下Consumer。
下面是我的简单封装。
public class DefaultConsumerAccept<T> implements Consumer<T> {
@Override
public void accept(T t) throws Exception {
if(handleApiError((String) t)){
return;
}
onSuccess((String) t);
}
public boolean handleApiError(String content) {
//ApiEmptyError是根据项目需求封装的错误分类
boolean error = ApiEmptyError.isError(content);
boolean isDialogShow = ApiEmptyError.getIsDialogShow();
boolean needAgainLog = ApiEmptyError.getIsNeedAgainLog();
String msg = JsonParse.getSuccessStatusMsg(content, "msg");
if (error && isDialogShow) {//以弹框形式提示
showErrorDialog();
return true;
}else if(error && needAgainLog){//需要重新登录
setUserLoggedOut();
return true;
}else if(error){//普通提示,直接toast
showErrorMsg(msg);
return true;
}
return false;
}
public void onSuccess(String response){
}
public void showErrorDialog(){
}
public void setUserLoggedOut(){
}
public void showErrorMsg(String msg){
}
}
大概就是这样了,我们还可以对Consumer<Throwable>进行封装,这样,我们就能去更好的处理请求时出现的异常和错误。
例如:
public class DefaultConsumerThrowable<T> implements Consumer<Throwable>{
@Override
public void accept(Throwable throwable) throws Exception {
handleException(throwable);
}
/**
* 处理Throwable类型,如网络异常,解析异常等
* @param throwable
*/
public void handleException(Throwable throwable) {
//ApiException是自己封装异常处理类
String throwstr = ApiException.setThrowable(throwable);
showErrorMsg(throwstr);
}
private void showErrorMsg(String error){
}
}
整个MVP使用流程就这么多了。