Android中的MVP
RobynTooth
10年前
<h2>前言</h2> <p>MVP作为一种MVC的演化版本在Android开发中受到了越来越多的关注,但在项目开发中选择一种这样的软件设计模式需保持慎重心态,一旦确定使用MVP作为你App的开发模式那么你就最好坚持做下去,如果在使用MVP模式开发过程中发现问题而且坑越来越大,这时你想用MVC等来重新设计的话基本上就等于推倒重来了。要知道在Android上MVP在现在为止并没有统一的标准或者框架,不像SSH这三个成熟稳重强而有力的三剑客支持推动着Java EE的开发,所以在运用MVP时一定要做好自己的理解,并且尽量预知自己App各模块的需求(客户说改改改,我们就改改改 :-( )以便提前做好充分的设计工作。当然MVP既然能出现那么必然有它的优点的,不然谁会理会这个冒出来的东西,下面就对Android中MVP做一些阐述。</p> <h2>MVP简介</h2> <p>相信大家对MVC都是比较熟悉了:<code>M-Model-模型</code>、<code>V-View-视图</code>、<code>C-Controller-控制器</code>,MVP作为MVC的演化版本,也是作为用户界面(用户层)的实现模式,那么类似的MVP所对应的意义:<code>M-Model-模型</code>、<code>V-View-视图</code>、<code>P-Presenter-表示器</code>。从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。而MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控View与Model之间的间接交互,MVP的结构图如下所示,对于这个图理解即可而不必限于其中的条条框框,毕竟在不同的场景下多少会有些出入的。在Android中很重要的一点就是对UI的操作基本上需要异步进行也就是在MainThread中才能操作UI,所以对View与Model的切断分离是合理的。此外Presenter与View、Model的交互使用接口定义交互操作可以进一步达到松耦合也可以通过接口更加方便地进行单元测试。<br> <a href="https://simg.open-open.com/show/a1541f3107731818c9f37916a8390850.png" rel="article0" title="MVP结构图"><img alt="Android中的MVP" src="https://simg.open-open.com/show/a1541f3107731818c9f37916a8390850.png" width="435" height="347"></a>MVP结构图</p> <h2>MVP之Model</h2> <p>Model 是用户界面需要显示数据的抽象,也可以理解为从业务数据(结果)那里到用户界面的抽象(Business rule, data access, model classes)。本文 Demo 为了简单处理就直接把业务放到了对应的 Model 之中。</p> <h2>MVP之View</h2> <p>视图这一层体现的很轻薄,负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment体现在了这一层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。例如,Activity上滚动列表时隐藏或者显示Acionbar(Toolbar),这样的UI逻辑时也应该在这一层。另外在View上输入的数据做一些判断时,例如,EditText的输入数据,假如是简单的非空判断则可以作为View层的逻辑,而当需要对EditText的数据进行更复杂的比较时,如从数据库获取本地数据进行判断时明显需要经过Model层才能返回了,所以这些细节需要自己掂量。</p> <h2>MVP之Presenter</h2> <p>Presenter这一层处理着程序各种逻辑的分发,收到View层UI上的反馈命令、定时命令、系统命令等指令后分发处理逻辑交由业务层做具体的业务操作,然后将得到的 Model 给 View 显示。</p> <h2>演示demo</h2> <p>动手写起代码来才有更好的感觉。demo很简单,还是上个图更直观,输入城市的代号,点击按钮获取城市的天气信息然后显示出来,网络操作使用Volley框架,解析用Gson,其它的就手写了。整个项目的包设计如下:<br> <a href="https://simg.open-open.com/show/3cdbf080eb5530507461a10e87fcee68.png" rel="article0" title="包结构"><img alt="Android中的MVP" src="https://simg.open-open.com/show/8d4240329b03f08ca2dba7eccfc67359.png" width="446" height="552"></a>包结构<br> <a href="https://simg.open-open.com/show/9e5a7ea0793a10e75f4527aa0c8e3f68.png" rel="article0" title="项目效果预览"><img alt="Android中的MVP" src="https://simg.open-open.com/show/feb658777702b0d5b7a3354f58fde589.png" width="387" height="309"></a>项目效果预览<br> 包图中明显的三层:Model包、Presenter包、UI包,其中,三者都实现各自的结构,Model为WeatherModel、Presenter为WeatherPresenter、View为Weather,那么具体实现类就是impl包里的了,View层的即为Activity。此外的app和util包无关紧要可以不看。可以看到采用MVP设计后项目明显多了很多东西,这也是不可避免的,使用原始方法可以使项目开起来简单些但是以后还有维护呢、测试呢、加功能呢、。。。<br> entity里的实体属性基本上对应<a href="/misc/goto?guid=4959671466749512149" rel="external">json里的这些属性了</a>,代码不贴了,View里面的接口:</p> <table> <tbody> <tr> <td> <pre> <code class="language-java">1 2 3 4 5 6 </code></pre> </td> <td> <pre> <code class="language-java">public interface WeatherView { void showLoading(); void hideLoading(); void showError(); void setWeatherInfo(Weather weather); } </code></pre> </td> </tr> </tbody> </table> <p> </p> <p>WeatherPresenter的接口:</p> <table> <tbody> <tr> <td> <pre> <code class="language-java">1 2 3 4 5 6 </code></pre> </td> <td> <pre> <code class="language-java">public interface WeatherPresenter { /** * 获取天气的逻辑 */ void getWeather(String cityNO); } </code></pre> </td> </tr> </tbody> </table> <p> </p> <p>WeatherModel接口:</p> <table> <tbody> <tr> <td> <pre> <code class="language-java">1 2 3 </code></pre> </td> <td> <pre> <code class="language-java">public interface WeatherModel { void loadWeather(String cityNO, OnWeatherListener listener); } </code></pre> </td> </tr> </tbody> </table> <p> </p> <p>prestener里面还有个OnWeatherListener,其在Presenter层实现,给Model层回调,更改View层的状态,确保Model层不直接操作View层。如果没有这一接口在WeatherPresenterImpl实现的话,WeatherPresenterImpl只有View和Model的引用那么Model怎么把结果告诉View呢?当然这只是一种解决方案,在实际项目中可以使用Dagger、EventBus、Otto等第三方框架结合进来达到更加松耦合的设计。</p> <table> <tbody> <tr> <td> <pre> <code class="language-java">1 2 3 4 5 6 7 8 9 10 11 12 </code></pre> </td> <td> <pre> <code class="language-java">public interface OnWeatherListener { /** * 成功时回调 * * @param weather */ void onSuccess(Weather weather); /** * 失败时回调,简单处理,没做什么 */ void onError(); } </code></pre> </td> </tr> </tbody> </table> <p> </p> <p>所以demo的代码流程:Activity做了一些UI初始化的东西并需要实例化对应WeatherPresenter的引用和实现WeatherView的接口,监听界面动作,Go按钮按下后即接收到查询天气的事件,在onClick里接收到即通过WeatherPresenter的引用把它交给WeatherPresenter处理。WeatherPresenter接收到了查询天气的逻辑就知道要查询天气了,然后把查询天气的具体业务实现交给WeatherModel去实现同时把WeatherListener即WeatherPresenter自己传给WeatherModel。WeatherModel进行查询天气业务后即把结果通过WeatherListener回调通知WeatherPresenter,WeatherPresenter再把结果返回给View层的Activity,最后Activity显示结果。就这样,拍砖之处请拍。</p> <h2>End</h2> <p>采用哪种软件设计模式都是为了达到如下目的,找到合适的加以运用就是最好的:</p> <ul> <li>易于维护</li> <li>易于测试</li> <li>松耦合度</li> <li>复用性高</li> <li>健壮稳定</li> </ul> <p>本文demo<br> <a href="/misc/goto?guid=4959669961549511772" rel="external">Rocko’s MVP demo</a><br> MVP相关demo<br> <a href="/misc/goto?guid=4959644067374070709" rel="external">androidmvp</a><br> <a href="/misc/goto?guid=4959671466906345108" rel="external">ActivityFragmentMVP</a><br> <a href="/misc/goto?guid=4959644031033899749" rel="external">EffectiveAndroidUI</a><br> <a href="/misc/goto?guid=4959671467016803490" rel="external">MvpCleanArchitecture</a><br> <a href="/misc/goto?guid=4959671467098477045" rel="external">Material-Movies</a></p> <p>相关参考文章<br> <a href="/misc/goto?guid=4958863056099423485" rel="external">50个Android开发技巧(20 使用MVP模式)</a><br> <a href="/misc/goto?guid=4958961538098926038" rel="external">MVP for Android: how to organize the presentation layer(英文原版)</a><br> <a href="/misc/goto?guid=4958835641839039298" rel="external">MVP for Android: how to organize the presentation layer(中文译文)</a><br> <a href="/misc/goto?guid=4959671467267348061" rel="external">从三层架构到MVC,MVP</a><br> <a href="/misc/goto?guid=4959671467345938700" rel="external">Architecting Android…The clean way?(中文译文)</a><br> <a href="/misc/goto?guid=4959641038390201197" rel="external">TED MOSBY - SOFTWARE ARCHITECT</a></p>