Android(安卓)MVP 使用教程
原文地址:
http://engineering.remind.com/android-code-that-scales/
Demo
https://github.com/remind101/android-arch-sample
写一个Hello World程序总是很简单的,它的代码总是很简单、整齐的,SDK完全可以满足我们的需求。但是,如果你在开发过复杂的Android app,你应该清楚,生产环境的代码往往不是这样。你需要不断的在已经很复杂的Activity 的onCreate 方法里面去添加if 语句去解决app在某个设备奔溃的问题,或者添加一些额外的业务逻辑。
我们每两周就会需要添加一些新的需求,为了能够维持产品开发的速度和质量,我们需要让我们的代码简单,可维护,耦合性低,容易测试。使用MVP架构模式可以帮助我们实现这个需求,让我们能够把精力集中到业务逻辑上面。MVP,全称Model-View-Presenter,是一种提升分离交互关系的多功能模式,和其他的模式有些轻微的不同。这篇文章的目标不是其描述MVP与其他模式的区别,而是展示如何把它应用到我们Android应用开发过程中,让我们的app受益。
Android 原有模式
Android 设计的分层是这样的,Model 层是实体类,View层是XML布局,Controller层是Activity,Fragment。理论上,这种模式工作得很好,但是,当你的app变得复杂,那么在你的Controller里面有大量的View代码,因为你需要处理数据绑定,动画,输入检查,事件监听,当然,还包括你的业务逻辑的代码。如果这些复杂的界面放在List或者Grid中,那情况就更坏了,Adapter不仅仅包含着View层和Controller层的代码,而且需要把它们当集合维护,这些模块都是高度耦合的,很难去维护和测试。
MVP模式
MVP 提供了一种方法,可以分离出冗长的控件显示和UI交互的Android代码,放到View层中,把业务逻辑的的代码放到Presenter中。Android中实现MVP方法的是,把Activity和Fragment看做是View层,用一个轻量级的Presenter去控制。最关键的地方是确立每一层的职责,并且使它们的接口规范化。下面是一些分层的规则:
View(Activity or Fragment)层的职责:
- 初始化Presenter,绑定,和解绑Presenter
- 通知Presenter 有关生命周期的方法
- 通知Presenter输入事件
- 初始化View,为View绑定数据
- 动画
- 事件跟踪
- 界面跳转
Presenter职责:
- 加载实体数据
- 持有model 和view 的引用
- 处理数据,并交给View层显示
- 和数据库,网络交互
- 处理UI事件
下面是View层和Presenter层的接口示例:
View:
interface MessageView { // View methods should be directives, as the View is just executing orders from the Presenter. // Methods for updating the view void setMessageBody(String body); void setAuthorName(String name);void showTranslationButton(boolean shouldShow); // Navigation methods void goToUserProfile(User user);}
Presenter:
interface MessagePresenter { // Presenter methods should mostly be callbacks, as the View is reporting events for the// Presenter to evaluate // Lifecycle events methods void onStart();// Input events methods void onAuthorClicked();void onThreeFingersSwipe();}
下面是一些关于这些接口的讨论:
- 更新View的方法必须是简单的,并且作用在一个目标view上面,这比一个setMessage方法去更新所有的view好。因为决定哪个信息放到view上面属于业务逻辑,应该放到Presenter中去处理,例如,当作者就是当前用户时,你想显示”你”代替用户名显示到view上面,着属于业务逻辑。
- Presenter中的生命周期方法没有必要完全和Activity或者Fragment中的生命周期方法去匹配,只实现需要做处理的相关方法。
- View监听到点击事件时先调用Presenter中的onAuthorClicked ,然后Presenter再调用goToAuthorProfile,不能在View中调用goToAuthorProfile()方法,因为决定去profile属于业务逻辑,应该放在Presenter中处理。
在MVP规则中,你的Presenter层的代码包含了Android Framework中的代码,而不是纯Java,那么你一定是某个地方写错了。同样,如果你的View层的代码持有Model层的引用,那么也是错误的。
在测试中,大部分需要测试的代码应该在Presenter中,最大的好处是这些代码不需要Android环境去运行,因为Presenter只持有View的引用,没有Android 相关代码的实现。这意味着我们能模拟一个View的接口,写纯JUnits测试,确保业务逻辑运行正确
List列表
到目前为止,我们猜想View层是Activity和Fragment,其实使用ViewHolder(不管是RecyclerView.ViewHolder还是List的ViewHolder )实现View的接口也可以正确的工作,在Adapter中,你只需要实现一些简单的逻辑,包括绑定和解绑Presenter。假设你的界面上有一个消息列表,一个加载的进度条,和数据为空时的界面,那么应该向下面这样分离:
- list 的Presenter 负责加载消息逻辑,展示消息 list/empty/loading的逻辑
- Fragment或者Activity负责 消息 list/empty/loadingView的显示
- Adapter负责映射Message Presenters 和ViewHolder
- message的 Presenter 负责单条消息的逻辑
- ViewHolder 负责单条消息的显示
所有的组成元素都是低耦合的,非常容易单独测试。
并且,当你有一个界面展现消息列表,一个界面展示消息详情,那么你可以重用相同的Presenter,只要实现两个不同的View接口(一个是VIewHolder,一个是Fragment)。同样的,View的接口可以被自定义view实现,这样可以重用自定义控件。
MVP 和配置改变
如果你开发过Android App,那么你应该在知道支持设备旋转和配置改变是一件痛苦的事情:
- 每次在使用Activity或者Fragment时,就必须考虑,如果设备旋转,应该怎么处理,是否需要保存什么数据。
- 在后台线程处理长时间任务时,最容易犯的错误就是持有Activity或者是Fragment的引用,必须要等到任务结束时才能释放,这样有可能会导致内存泄露。
使用MVP可以正确的处理这一切而你不需要考虑它,因为Presenter 对象没有持有UI强引用,它们都是轻量级的并且可以在方向改变时保留实例。因为Presenter 持有了Model的引用和View的状态,在配置文件改变时,它能重新创建。
下面是假设用户下载时,方向旋转手机屏幕,MVP怎样工作:
- Activity第一次被创建,会创建新的Presenter,绑定到当前Activity。
- 点击下载按钮,长时间任务开始在Presenter中运行。
- 旋转屏幕,Presenter和Activity解绑,第一个Activity实例被回收,Presenter实例仍然保留,下载任务仍然运行。
- 第二个Activity实例被创建,绑定原来的Presenter。
- 下载任务完成,Presenter更新View。
更多相关文章
- 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
- android io写入读取 txt 数据 封装
- eclipse导入android项目后出现错误的解决方法
- 初学Android,短信管理器之发送短信(六十五)
- android 访问WebService
- android 获取正在运行的后台service的代码
- Android友盟推送接入
- Android(安卓)Databinding 与 RecyclerView 完美结合
- Android自学笔记-10-Sqlite的简单使用