内容简介:Presenter作为中间人,负责在View和数据Model之间处理业务逻辑。其实这里隐藏了Presenter带来的问题,Presenter的作用不单纯他需要一边与View打交道,一边与数据打交道。只能剥离一些Activity/Fragment的代码。下面已Google architecture sample里MVP的代码作为例子:这是一个加载一个todo list的界面例子
MVP vs MVVM
MVP
MVP代表Model–view–presenter,用图来表示MVP的思想如下:
图片来之wikipedia
Presenter作为中间人,负责在View和数据Model之间处理业务逻辑。其实这里隐藏了Presenter带来的问题,Presenter的作用不单纯他需要一边与View打交道,一边与数据打交道。只能剥离一些Activity/Fragment的代码。
下面已Google architecture sample里MVP的代码作为例子:
这是一个加载一个todo list的界面例子
MVP会先定义接口作为协议
interface TasksContract { interface View : BaseView<Presenter> { var isActive: Boolean fun setLoadingIndicator(active: Boolean) fun showTasks(tasks: List<Task>) fun showNoTasks() // 省略 } interface Presenter : BasePresenter { fun loadTasks(forceUpdate: Boolean) fun result(requestCode: Int, resultCode: Int) // 省略 } }
TasksPresenter代码:
class TasksPresenter(val tasksRepository: TasksRepository, val tasksView: TasksContract.View) : TasksContract.Presenter { private fun loadTasks(forceUpdate: Boolean, showLoadingUI: Boolean) { // 这里需要知道view的状态来判断是否需要显示loading,showLoading由view传递过来。这里已经开始让逻辑不单一了 if (showLoadingUI) { tasksView.setLoadingIndicator(true) } // 省略部分代码... // 加载数据 tasksRepository.getTasks(object : TasksDataSource.LoadTasksCallback { override fun onTasksLoaded(tasks: List<Task>) { val tasksToShow = ArrayList<Task>() // 当View不是active的时候直接返回,这里有要考虑View被销毁,回调还在的问题,代码持续ugly if (!tasksView.isActive) { return } if (showLoadingUI) { tasksView.setLoadingIndicator(false) } processTasks(tasksToShow) } override fun onDataNotAvailable() { // The view may not be able to handle UI updates anymore if (!tasksView.isActive) { return } tasksView.showLoadingTasksError() } }) } }
承担View职责的是一个Fragment
class TasksFragment : Fragment(), TasksContract.View { internal var itemListener: TaskItemListener = object : TaskItemListener { override fun onTaskClick(clickedTask: Task) { presenter.openTaskDetails(clickedTask) } override fun onCompleteTaskClick(completedTask: Task) { presenter.completeTask(completedTask) } override fun onActivateTaskClick(activatedTask: Task) { presenter.activateTask(activatedTask) } } // 省略代码... setOnRefreshListener { presenter.loadTasks(false) } }
可以从上述代码了解到,MVP模式面临的问题是
- View和Presenter之间需要相互持有
- Presenter职责不单一,作为中间人同时要管理view和数据,让代码看起来丑陋。而我们期望Presenter的作用应该是只对数据进行逻辑操作而不需要关系View的状态。
MVVM
图片来之wikipedia MVVM
MVVM是通过数据绑定,让原来的View和Presenter的职责更单一的改进。Model提供数据,ViewModel来做业务逻辑处理和管理状态,View只更具ViewModel的数据自己变化显示。
这样的想法很好,如何在Android里实现呢,答案是ViewModel和LiveData
ViewModel + LiveData实现MVVM
Android里去年引入的ViewModel类,被设计用来保存和View显示逻辑相关的数据,它是忽略configuration change的一个类,就算因为configuration change数据依然会被保留。
LiveData可以简单理解为一个放数据的容器,并且知晓UI的生命周期,它是一个数据的观察者它可以知晓数据的变化,所以叫它LiveData活数据嘛。
同样是前面MVP展示的逻辑,一个todo list应用需要加todo列表
public class TasksViewModel extends AndroidViewModel { // 数据列表 public final ObservableList<Task> items = new ObservableArrayList<>(); // loading状态 public final ObservableBoolean dataLoading = new ObservableBoolean(false); // 省略代码... private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) { // 只用设置loading的状态,不再需要持有View if (showLoadingUI) { dataLoading.set(true); } mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { // 单纯的更新数据,而不再用操心View的状态 List<Task> tasksToShow = new ArrayList<>(); if (showLoadingUI) { dataLoading.set(false); } mIsDataLoadingError.set(false); items.clear(); items.addAll(tasksToShow); empty.set(items.isEmpty()); } @Override public void onDataNotAvailable() { mIsDataLoadingError.set(true); } }); } }
View的逻辑,这里用了Android的DataBinding,loading的显示更具ViewModel里的值来判断
<SwipeRefreshLayout android:id="@+id/refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:onRefresh="@{viewmodel}" app:refreshing="@{viewmodel.dataLoading}"/>
总结,MVVM可以让View的职责更加单一只是根据数据来显示。ViewModel可以单纯的处理数据而不用关心View的状态
ViewModel的其他好处
-
Fragment间传递数据
Fragment之前直接传递数据是如此的痛苦,小数据放bundle put也就忍了,数据太大还只能通过直接持有Fragment的引用传入数据引用。现在有了ViewModel可以很方便的传递数据,因为在同一个Activity里可以获得到同一个ViewModel,数据可以直接拿来用。
这样的好处显而易见,
- Activity不需要了解Fragment直接通信的过程
- Fragment直接也不需要互相了解,只需要从ViewModel里拿数据就好了
- 数据会因为是通过引用传递过去的,其中一个Fragment因为生命周期而影响数据
-
测试方便
ViewModel不再持有具体的Android类,View啊Activity,Fragment啥的,给测试单独ViewModel提供了可能。
-
代替onSaveInstanceState保存状态
Fragment之前保存数据和恢复状态一直很疼,需要自己onSaveInstanceState,并且因为onSaveInstanceState用的bundle并不能放入大量数据,只能简单的保存一些简单的状态。但是现在有ViewModel它不会随着configuration change而变化,数据就静静的呆在那所以Fragment被回收后从新创建也不用担心数据丢失了。
ViewModel的秘密
ViewModel的神奇的一点是它到底如何逃离configuration change导致的生命周期变化的呢?
其实非常简单,是利用了Fragment的
Fragment.setRetainInstance(true)
。
看源码:
ViewModelStores是保存ViewModel的地方
ViewModel是重HolderFragment里取出。
HolderFragment
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 为什么你应该从学习数据可视化和操作开始
- 顶会论文应不应该提交代码?应该,但不能强制
- 单元测试 – 我应该对不应该在函数中传递的数据(无效输入)进行单元测试吗?
- 智能合约事件应该这么用
- 研发职位到底应该怎么设置?
- 我们应该如何给需求排序?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JSON 在线解析
在线 JSON 格式化工具
RGB HSV 转换
RGB HSV 互转工具