写点什么

王者荣耀 MVP- 不不不, 一个小例子彻底搞懂 Android 的 -MVP 到底是什么

用户头像
Android架构
关注
发布于: 2021 年 11 月 06 日

MVP 的封装

很显然,MVP 的实现套路是大致相同的,如果在一个应用中,存在大量的 Activity 和 Fragment,并且都使用 MVP 的架构,那么难免会有很多重复工作,所以封装就很有必要性了。


在说 MVP 的封装之前,需要强调一点,MVP 更多的是一种思想,而不是一种模式,每个开发者都可以按照自己的思路来实现具有个性化的 MVP,所以不同的人写出的 MVP 可能会有一些差别,笔者在此仅提供一种实现思路,供读者参考。


首先 Model、View 和 Presenter 都可能会有一些通用性的操作,所以可以分别定义三个对应的底层接口。


interface?BaseModel?{}


interface?BaseView?{void?showError(String?msg);}


public?abstract?class?BasePresenter<V?extends?BaseView,?M?extends?BaseModel>?{protected?V?view;protected?M?model;


public?BasePresenter()?{model?=?createModel();}


void?attachView(V?view)?{this.view?=?view;}


void?detachView()?{this.view?=?null;}


abstract?M?createModel();}


这里的 View 层添加了一个通用的方法,显示错误信息,写在接口层,可以在实现处按照需求来显示,比如有的地方可能会是弹出一个 Toast,或者有的地方需要将错误信息显示在 TextView 中,Model 层也可以根据需要添加通用的方法,重点来看一下 Presenter 层。


这里的 BasePresenter 采用了泛型,为什么要这么做呢?主要是因为 Presenter 必须同时持有 View 和 Model 的引用,但是在底层接口中无法确定他们的类型,只能确定他们是 BaseView 和 BaseModel 的子类,所以采用泛型的方式来引用,就巧妙的解决了这个问题,在 BasePresenter 的子类中只要定义好 View 和 Model 的类型,就会自动引用他们的对象了。Presenter 中的通用的方法主要就是 attachView 和 detachView,分别用于创建 View 对象和把 View 的对象置位空,前面已经说过,置空是为了防止内存泄漏,Model 的对象可以在 Presenter 的构造方法中创建。另外,这里的 Presenter 也可以写成接口的形式,读者可以按照自己的喜好来选择。


然后看一下在业务代码中该如何使用 MVP 的封装,代码如下


interface?TestContract?{


interface?Model?extends?BaseModel?{void?getData1(Callback1?callback1);void?getData2(Callback2?callback2);void?getData3(Callback3?callback3);}


interface?View?extends?BaseView?{void?updateUI1();void?updateUI2();void?updateUI3();}


abstract?class?Presenter?extends?BasePresenter<View,?Model>?{abstract?void?request1();abstract?void?request2();void?request3()?{model.getData3(new?Callback3()?{@Overridepublic?void?onResult(String?text)?{view.updateUI3();}});}}}


首先定义一个 Contract 契约接口,然后把 Model、View、和 Presenter 的子类分别放入 Contract 的内部,这里的一个 Contract 就对应一个页面(一个 Activity 或者一个 Fragment),放在 Contract 内部是为了让同一个页面的逻辑方法都放在一起,方便查看和修改。Presenter 中的 request3 方法演示了如何通过 Presenter 来进行 View 和 Model 的交互。


接下来要做的就是实现这三个模块的逻辑方法了,在 Activity 或 Fragment 中实现 TextContract.View 的接口,再分别创建两个类用来实现 TextContract.Model 和 TextContract.Presenter,复写里面的抽象方法就好了。

扩展:用 RxJava 简化代码

上面的代码中,Model 层中的每个方法都传入了一个回调接口,这是因为获取数据往往是异步的,在获取的数据时需要用回调接口通知 Presenter 来更新 View。


如果想要避免回调接口,可以采用 RxJava 的方式来 Model 获取的数据直接返回一个 Observable,接下来用 RxJava 的方式来改造前面的例子


public?class?HttpModel?{public?Observable<String>?request()?{return?Observable.create(new?ObservableOnSubscribe<String>()?{@Overridepublic?void?subscribe(ObservableEmitter<String>?emitter)?throws?Exception?{Thread.sleep(2000);emitter.onNext("从网络获取到的数据");emitter.onComplete();}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());}}


public?class?Presenter?{private?MVPView?view;private?HttpModel?model;


public?Presenter(MVPView?view)?{this.view?=?view;model?=?new?HttpModel();}


private?Disposable?disposable;


public?void?request()?{disposable?=?model.request().subscribe(new?Consumer<String>()?{@Overridepublic?void?accept(String?s)?throws?Exception?{view.updateTv(s);}},?new?Consumer<Throwable>()?{@Overridepublic?void?accept(Throwable?throwable)?throws?Exception?{


}});}


public?void?detachView()?{view?=?null;if?(disposable?!=?null?&&?!disposable.isDisposed())?{disposable.dispose();}}}


Model 的 request 方法直接返回一个 Observable,然后在 Presenter 中调用 subscribe 方法来通知 View 更新,这样就避免了使用回调接口。


开源库推荐

最后,推荐一个 MVP 架构的开源库,正如笔者所说,MVP 更多的是一种思想,所以 github 上关于 MVP 的开源库并不多,大多是在完整的 APP 内部自己封装的 MVP。如果想要比较简单的集成 MVP 的架构,笔者推荐这个库:https://github.com/sockeqwe/mosby 它的使用方法比较简单,可以直接参考官方的 demo,接下来简单的分析一下作者的封装思想。


首先 View 层和 Presenter 层分别有一个基础的接口


public?interf


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ace?MvpView?{}


public?interface?MvpPresenter<V?extends?MvpView>?{


/***?Set?or?attach?the?view?to?this?presenter*/@UiThreadvoid?attachView(V?view);


/***?Will?be?called?if?the?view?has?been?destroyed.?Typically?this?method?will?be?invoked?from*?<code>Activity.detachView()</code>?or?<code>Fragment.onDestroyView()</code>*/@UiThreadvoid?detachView(boolean?retainInstance);}


这里加 @UIThread 注解是为了确保 attachView 和 detachView 都运行在主线程中。然后业务代码的 Activity 需要继承 MvpActivity


public?abstract?class?MvpActivity<V?extends?MvpView,?P?extends?MvpPresenter<V>>extends?AppCompatActivity?implements?MvpView,com.hannesdorfmann.mosby3.mvp.delegate.MvpDelegateCallback<V,P>?{


protected?ActivityMvpDelegate?mvpDelegate;protected?P?presenter;protected?boolean?retainInstance;


@Override?protected?void?onCreate(Bundle?savedInstanceState)?{super.onCreate(savedInstanceState);getMvpDelegate().onCreate(savedInstanceState);}


@Override?protected?void?onDestroy()?{super.onDestroy();getMvpDelegate().onDestroy();}


@Override?protected?void?onSaveInstanceState(Bundle?outState)?{super.onSaveInstanceState(outState);getMvpDelegate().onSaveInstanceState(outState);}


@Override?protected?void?onPause()?{super.onPause();getMvpDelegate().onPause();}


@Override?protected?void?onResume()?{super.onResume();getMvpDelegate().onResume();}


@Override?protected?void?onStart()?{super.onStart();getMvpDelegate().onStart();}


@Override?protected?void?onStop()?{super.onStop();getMvpDelegate().onStop();}


@Override?protected?void?onRestart()?{super.onRestart();getMvpDelegate().onRestart();}


@Override?public?void?onContentChanged()?{super.onContentChanged();getMvpDelegate().onContentChanged();}


@Override?protected?void?onPostCreate(Bundle?savedInstanceState)?{super.onPostCreate(savedInstanceState);getMvpDelegate().onPostCreate(savedInstanceState);}


/***?Instantiate?a?presenter?instance**?@return?The?{@link?MvpPresenter}?for?this?view*/@NonNull?public?abstract?P?createPresenter();


/***?Get?the?mvp?delegate.?This?is?internally?used?for?creating?presenter,?attaching?and?detaching*?view?from?presenter.**?<p><b>Please?note?that?only?one?instance?of?mvp?delegate?should?be?used?per?Activity*?instance</b>.*?</p>**?<p>*?Only?override?this?method?if?you?really?know?what?you?are?doing.*?</p>**?@return?{@link?ActivityMvpDelegateImpl}*/@NonNull?protected?ActivityMvpDelegate<V,?P>?getMvpDelegate()?{if?(mvpDelegate?==?null)?{mvpDelegate?=?new?ActivityMvpDelegateImpl(this,?this,?true);}


return?mvpDelegate;}


@NonNull?@Override?public?P?getPresenter()?{return?presenter;}


@Override?public?void?setPresenter(@NonNull?P?presenter)?{this.presenter?=?presenter;}


@NonNull?@Override?public?V?getMvpView()?{return?(V)?this;}}


MvpActivity 中持有一个 ActivityMvpDelegate 对象,它的实现类是 ActivityMvpDelegateImpl,并且需要传入 MvpDelegateCallback 接口,ActivityMvpDelegateImpl 的代码如下


public?class?ActivityMvpDelegateImpl<V?extends?MvpView,?P?extends?MvpPresenter<V>>implements?ActivityMvpDelegate?{


protected?static?final?String?KEY_MOSBY_VIEW_ID?=?"com.hannesdorfmann.mosby3.activity.mvp.id";


public?static?boolean?DEBUG?=?false;private?static?final?String?DEBUG_TAG?=?"ActivityMvpDelegateImpl";


private?MvpDelegateCallback<V,?P>?delegateCallback;protected?boolean?keepPresenterInstance;protected?Activity?activity;protected?String?mosbyViewId?=?null;


/***?@param?activity?The?Activity*?@param?delegateCallback?The?callback*?@param?keepPresenterInstance?true,?if?the?presenter?instance?should?be?kept?across?screen*?orientation?changes.?Otherwise?false.*/public?ActivityMvpDelegateImpl(@NonNull?Activity?activity,@NonNull?MvpDelegateCallback<V,?P>?delegateCallback,?boolean?keepPresenterInstance)?{


if?(activity?==?null)?{throw?new?NullPointerException("Activity?is?null!");}


if?(delegateCallback?==?null)?{throw?new?NullPointerException("MvpDelegateCallback?is?null!");}this.delegateCallback?=?delegateCallback;this.activity?=?activity;this.keepPresenterInstance?=?keepPresenterInstance;}


/***?Determines?whether?or?not?a?Presenter?Instance?should?be?kept**?@param?keepPresenterInstance?true,?if?the?delegate?has?enabled?keep*/static?boolean?retainPresenterInstance(boolean?keepPresenterInstance,?Activity?activity)?{return?keepPresenterInstance?&&?(activity.isChangingConfigurations()||?!activity.isFinishing());}


/***?Generates?the?unique?(mosby?internal)?view?id?and?calls?{@link*?MvpDelegateCallback#createPresenter()}*?to?create?a?new?presenter?instance**?@return?The?new?created?presenter?instance*/private?P?createViewIdAndCreatePresenter()?{


P?presenter?=?delegateCallback.createPresenter();if?(presenter?==?null)?{throw?new?NullPointerException("Presenter?returned?from?createPresenter()?is?null.?Activity?is?"?+?activity);}if?(keepPresenterInstance)?{mosbyViewId?=?UUID.randomUUID().toString();PresenterManager.putPresenter(activity,?mosbyViewId,?presenter);}return?presenter;}


@Override?public?void?onCreate(Bundle?bundle)?{


P?presenter?=?null;


if?(bundle?!=?null?&&?keepPresenterInstance)?{


mosbyViewId?=?bundle.getString(KEY_MOSBY_VIEW_ID);


if?(DEBUG)?{Log.d(DEBUG_TAG,"MosbyView?ID?=?"?+?mosbyViewId?+?"?for?MvpView:?"?+?delegateCallback.getMvpView());}


if?(mosbyViewId?!=?null&&?(presenter?=?PresenterManager.getPresenter(activity,?mosbyViewId))?!=?null)?{////?Presenter?restored?from?cache//if?(DEBUG)?{Log.d(DEBUG_TAG,"Reused?presenter?"?+?presenter?+?"?for?view?"?+?delegateCallback.getMvpView());}}?else?{////?No?presenter?found?in?cache,?most?likely?caused?by?process?death//presenter?=?createViewIdAndCreatePresenter();if?(DEBUG)?{Log.d(DEBUG_TAG,?"No?presenter?found?although?view?Id?was?here:?"+?mosbyViewId+?".?Most?likely?this?was?caused?by?a?process?death.?New?Presenter?created"+?presenter+?"?for?view?"+?getMvpView());}}}?else?{////?Activity?starting?first?time,?so?create?a?new?presenter//presenter?=?createViewIdAndCreatePresenter();if?(DEBUG)?{Log.d(DEBUG_TAG,?"New?presenter?"?+?presenter?+?"?for?view?"?+?getMvpView());}}


if?(presenter?==?null)?{throw?new?IllegalStateException("Oops,?Presenter?is?null.?This?seems?to?be?a?Mosby?internal?bug.?Please?report?this?issue?here:?https://github.com/sockeqwe/mosby/issues");}


delegateCallback.setPresenter(presenter);getPresenter().attachView(getMvpView());


if?(DEBUG)?{Log.d(DEBUG_TAG,?"View"?+?getMvpView()?+?"?attached?to?Presenter?"?+?presenter);}}


private?P?getPresenter()?{P?presenter?=?delegateCallback.getPresenter();if?(presenter?==?null)?{throw?new?NullPointerException("Presenter?returned?from?getPresenter()?is?null");}return?presenter;}


private?V?getMvpView()?{V?view?=?delegateCallback.getMvpView();if?(view?==?null)?{throw?new?NullPointerException("View?returned?from?getMvpView()?is?null");}return?view;}


@Override?public?void?onDestroy()?{boolean?retainPresenterInstance?=?retainPresenterInstance(keepPresenterInstance,?activity);getPresenter().detachView(retainPresenterInstance);if?(!retainPresenterInstance?&&?mosbyViewId?!=?null)?{PresenterManager.remove(activity,?mosbyViewId);}


if?(DEBUG)?{if?(retainPresenterInstance)?{Log.d(DEBUG_TAG,?"View"+?getMvpView()+?"?destroyed?temporarily.?View?detached?from?presenter?"+?getPresenter());}?else?{Log.d(DEBUG_TAG,?"View"

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
王者荣耀MVP-不不不,一个小例子彻底搞懂Android的-MVP到底是什么