当前位置:   article > 正文

Android MVP模式介绍和讲解_android mvp model里面写什么

android mvp model里面写什么

作者:谭东

先给个MVP的出处链接:https://github.com/googlesamples/android-architecture/,出处就是google在github上的一个架构的开源例子,里面有个todo-mvp的sample项目,大概的展示了下它们的架构设计,也就是这个todo-mvp例子项目的架构:https://github.com/googlesamples/android-architecture/tree/todo-mvp/

这只是他们认为比较好的架构方式,被传开了了,也就出现了MVP模式。但是并不一定这个模式就非常好,只不过有它的好处的初衷:想让结构更加的清晰,逻辑耦合度降低。但相对来说也使开发变得逻辑复杂了些,一般大型的项目推荐使用,看你的需求和权衡了。

之前也看了一些MVP的文章,没有太认真看,也是云里雾里。后来索性抽空一上午的时间,直接看官方的todo-mvp的例子源码,一点点分析整理google的mvp的设计的思路和流程。这个Demo演示了谷歌github例子的Model-View-Presenter(MVP)的架构设计,不过说实话,官方的demo写的有点乱,类命名、方法命名等很不清晰。总体来说还好,下午的时间就按照这个mvp的架构规范重新写了一个版本的todo-mvp。原版的todo-mvp用的是sqlite,涉及到了RoomDatabase。让注重mvp阅读人看起来很乱,容易分散重点。所以我这里使用sharedpreferences替换了sqlite的数据增删改查。 整体说来,官方的例子选择的还算容易理解,实现的功能就是列表展示本地存储的任务Task数据。有增加一条、删除一条、获取一条、获取全部任务的功能。

先看原版的实现设计图:


淡绿色部分就是View和Presenter部分的双向交互,左侧白底部分的就是Model部分的逻辑,可以看出整个MVP就是Model和Presenter的交互及View和Presenter的双向交互,而View和Model之间并没有直接的交互,这也就避免了耦合吧。

下面这个是我改过重写的demo的实现,把SQLite换成了SharedPreference,去除了本地和网络的任务的判断。





我这里以todo-mvp的我精心改版整理后的项目进行分析讲解,MVP的整体架构分析:


官方的demo基本上是按照功能分类的,把很多类都混在一起了,我这里的结构整理,相对清晰一些。

先说MVP的Model:

Model里先有实体,这里是Task:

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * MVP中某个M的基础实体类
  4. */
  5. public class Task {
  6. private String id;
  7. private String date;
  8. private String title;
  9. private String content;
  10. private boolean completed;
  11. public String getId() {
  12. return id;
  13. }
  14. public void setId(String id) {
  15. this.id = id;
  16. }
  17. public String getDate() {
  18. return date;
  19. }
  20. public void setDate(String date) {
  21. this.date = date;
  22. }
  23. public String getTitle() {
  24. return title;
  25. }
  26. public void setTitle(String title) {
  27. this.title = title;
  28. }
  29. public String getContent() {
  30. return content;
  31. }
  32. public void setContent(String content) {
  33. this.content = content;
  34. }
  35. public boolean isCompleted() {
  36. return completed;
  37. }
  38. public void setCompleted(boolean completed) {
  39. this.completed = completed;
  40. }
  41. @Override
  42. public boolean equals(Object o) {
  43. if (this == o) return true;
  44. if (o == null || getClass() != o.getClass()) return false;
  45. Task task = (Task) o;
  46. return id.equals(task.id);
  47. }
  48. }

以及操作Task实体的定义的方法,TaskDao,它的作用就是预先定义对外暴漏操作Task的接口方法:

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * 对应的Task的Model的主要方法接口定义类
  4. */
  5. public interface TaskDao {
  6. void getTasks(@NonNull LoadTasksCallback callback);
  7. void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
  8. void saveTask(@NonNull Task task);
  9. void refreshTasks();
  10. void deleteAllTasks();
  11. void deleteTask(@NonNull Task task);
  12. interface LoadTasksCallback {
  13. void onTasksLoaded(List<Task> tasks);
  14. void onDataNotAvailable();
  15. }
  16. interface GetTaskCallback {
  17. void onTaskLoaded(Task task);
  18. void onDataNotAvailable();
  19. }
  20. }

之后,再实现TaskDao里的定义的接口方法,也就是要有TaskImpl实现类:

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * Task的Model的接口方法的具体实现,采用单例模式
  4. */
  5. public class TaskDaoImpl implements TaskDao {
  6. private static TaskDaoImpl INSTANCE = null;
  7. private static final int SERVICE_LATENCY_IN_MILLIS = 500;
  8. private static List<Task> TASKS_SERVICE_DATA = null;
  9. // 防止直接实例化
  10. private TaskDaoImpl() {
  11. }
  12. /**
  13. * 单例模式创建TaskDaoImpl
  14. *
  15. * @return
  16. */
  17. public static TaskDaoImpl getInstance() {
  18. if (INSTANCE == null) {
  19. INSTANCE = new TaskDaoImpl();
  20. }
  21. return INSTANCE;
  22. }
  23. /**
  24. * 销毁回收实例对象
  25. */
  26. public static void destroyInstance() {
  27. INSTANCE = null;
  28. }
  29. @Override
  30. public void getTasks(@NonNull final LoadTasksCallback callback) {
  31. //逻辑的具体实现,这里获取数据
  32. Handler handler = new Handler();
  33. handler.postDelayed(new Runnable() {
  34. @Override
  35. public void run() {
  36. TASKS_SERVICE_DATA = Utils.getTasksFromSp();
  37. callback.onTasksLoaded(TASKS_SERVICE_DATA);
  38. }
  39. }, SERVICE_LATENCY_IN_MILLIS);
  40. }
  41. @Override
  42. public void getTask(@NonNull String taskId, @NonNull final GetTaskCallback callback) {
  43. int position = 0;
  44. TASKS_SERVICE_DATA = Utils.getTasksFromSp();
  45. for (int i = 0; i < TASKS_SERVICE_DATA.size(); i++) {
  46. if (TASKS_SERVICE_DATA.get(i).getId().equals(taskId)) {
  47. position = i;
  48. }
  49. }
  50. //逻辑的具体实现
  51. final Task task = TASKS_SERVICE_DATA.get(position);
  52. Handler handler = new Handler();
  53. handler.postDelayed(new Runnable() {
  54. @Override
  55. public void run() {
  56. callback.onTaskLoaded(task);
  57. }
  58. }, SERVICE_LATENCY_IN_MILLIS);
  59. }
  60. @Override
  61. public void saveTask(@NonNull Task task) {
  62. //逻辑的具体实现
  63. TASKS_SERVICE_DATA.add(task);
  64. Utils.addTaskToSp(task);
  65. }
  66. @Override
  67. public void refreshTasks() {
  68. //逻辑的具体实现
  69. TASKS_SERVICE_DATA = Utils.getTasksFromSp();
  70. }
  71. @Override
  72. public void deleteAllTasks() {
  73. //逻辑的具体实现
  74. TASKS_SERVICE_DATA.clear();
  75. Utils.deleteAllTaskToSp();
  76. }
  77. @Override
  78. public void deleteTask(@NonNull Task task) {
  79. //逻辑的具体实现
  80. Utils.deleteTaskToSp(task);
  81. }
  82. }

那么可以看出,Model里一般有3个类,以这个todo-mvp例子来说,有实体类Task,接口方法类TaskDao,实现类TaskImpl。

接着定义我们的View:

根据情况,我们可以定义一个父类的View叫BaseView,当然直接写一个View也没问题。

这个View的类里我们定义好我们的Activity或者Fragment界面需要交互操作的一些方法。

  1. interface View extends BaseView<Presenter> {
  2. /**
  3. * 这里定义操作View界面相关的回调方法
  4. */
  5. void setLoadingIndicator(boolean show);
  6. void showTasks(List<Task> tasks);
  7. void showAddTaskOk(Task task);
  8. void showTaskDetailsUi(String taskId);
  9. void showLoadingTasksError();
  10. void showNoTasks();
  11. boolean isActive();
  12. void showClearedAllTaskOk();
  13. void showClearedTaskOk(Task task);
  14. }

然后定义Presenter,Presenter的中文意思是主持人,顾名思义也就是整个Model和View的调度和指挥者。Presenter就是把Model的TaskImpl类和View的View类关联结合起来。里面定义的方法也是对应View和Model相连接调用的中间件接口方法,类似于一个中间转接接口。

  1. interface Presenter extends BasePresenter {
  2. /**
  3. * 这里定义操作加载处理数据的相关回调方法
  4. */
  5. void loadTasks(boolean showLoading);
  6. void addNewTask(@NonNull Task task);
  7. void openTaskDetails(@NonNull Task task);
  8. void clearAllTasks();
  9. void clearTask(@NonNull Task task);
  10. }

google官方的todo-mvp的例子中间又加了一层,Contract类,就是一个连接关联类Contract,把View和Presenter关联起来。

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * 一个连接关联类Contract,把View和Presenter关联起来
  4. */
  5. public interface TasksContract {
  6. interface View extends BaseView<Presenter> {
  7. /**
  8. * 这里定义操作View界面相关的回调方法
  9. */
  10. void setLoadingIndicator(boolean show);
  11. void showTasks(List<Task> tasks);
  12. void showAddTaskOk(Task task);
  13. void showTaskDetailsUi(String taskId);
  14. void showLoadingTasksError();
  15. void showNoTasks();
  16. boolean isActive();
  17. void showClearedAllTaskOk();
  18. void showClearedTaskOk(Task task);
  19. }
  20. interface Presenter extends BasePresenter {
  21. /**
  22. * 这里定义操作加载处理数据的相关回调方法
  23. */
  24. void loadTasks(boolean showLoading);
  25. void addNewTask(@NonNull Task task);
  26. void openTaskDetails(@NonNull Task task);
  27. void clearAllTasks();
  28. void clearTask(@NonNull Task task);
  29. }
  30. }

这一层我觉的可以调整,可有可无。把这个写成一个View就可以。

然后定义编写我们的真正的Presenter实现类,TasksPresenter类。

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * Presenter的业务真正实现,监听用户UI操作,回调和更新数据给UI
  4. */
  5. public class TasksPresenter implements TasksContract.Presenter {
  6. private final TaskDaoImpl taskDaoImpl;
  7. private final TasksContract.View tasksView;
  8. public TasksPresenter(@NonNull TaskDaoImpl taskDaoImpl, @NonNull TasksContract.View tasksView) {
  9. this.taskDaoImpl = Utils.checkNotNull(taskDaoImpl, "tasksRepository cannot be null");
  10. this.tasksView = Utils.checkNotNull(tasksView, "tasksView cannot be null!");
  11. this.tasksView.setPresenter(this);
  12. }
  13. @Override
  14. public void start() {
  15. loadTasks(true);
  16. }
  17. @Override
  18. public void loadTasks(boolean showLoading) {
  19. toLoadTasks(showLoading);
  20. }
  21. /**
  22. * 添加数据操作
  23. */
  24. @Override
  25. public void addNewTask(@NonNull Task task) {
  26. //操作Model
  27. taskDaoImpl.saveTask(task);
  28. //操作View
  29. tasksView.showAddTaskOk(task);
  30. }
  31. @Override
  32. public void openTaskDetails(@NonNull Task requestedTask) {
  33. Utils.checkNotNull(requestedTask, "requestedTask cannot be null!");
  34. tasksView.showTaskDetailsUi(requestedTask.getId());
  35. }
  36. /**
  37. * 删除全部数据操作
  38. */
  39. @Override
  40. public void clearAllTasks() {
  41. //操作Model
  42. taskDaoImpl.deleteAllTasks();
  43. //操作View
  44. tasksView.showClearedAllTaskOk();
  45. }
  46. /**
  47. * 删除单条数据操作
  48. *
  49. * @param task
  50. */
  51. @Override
  52. public void clearTask(@NonNull Task task) {
  53. //操作Model
  54. taskDaoImpl.deleteTask(task);
  55. //操作View
  56. tasksView.showClearedTaskOk(task);
  57. }
  58. private void toLoadTasks(final boolean showLoadingUI) {
  59. if (showLoadingUI) {//显示加载中进度条布局
  60. tasksView.setLoadingIndicator(true);
  61. }
  62. taskDaoImpl.getTasks(new TaskDao.LoadTasksCallback() {
  63. @Override
  64. public void onTasksLoaded(List<Task> tasks) {
  65. // The view may not be able to handle UI updates anymore
  66. if (!tasksView.isActive()) {
  67. return;
  68. }
  69. if (showLoadingUI) {//隐藏加载中进度条
  70. tasksView.setLoadingIndicator(false);
  71. }
  72. processTasks(tasks);
  73. }
  74. @Override
  75. public void onDataNotAvailable() {
  76. // The view may not be able to handle UI updates anymore
  77. if (!tasksView.isActive()) {
  78. return;
  79. }
  80. tasksView.showLoadingTasksError();
  81. }
  82. });
  83. }
  84. private void processTasks(List<Task> tasks) {
  85. if (tasks.isEmpty()) {
  86. // Show a message indicating there are no tasks for that filter type.
  87. processEmptyTasks();
  88. } else {
  89. //加载完毕后回调给View进行显示逻辑处理
  90. tasksView.showTasks(tasks);
  91. }
  92. }
  93. private void processEmptyTasks() {
  94. tasksView.showNoTasks();
  95. }
  96. }

可以看出,这个Presenter实现类,实现了之前定义的Presenter接口里的方法,监听用户UI操作,回调和更新数据给UI。里面含有两个大类:TaskDaoImpl这个Model实现类和TasksContract.View这个View类,也就是把Model和View进行了关联和调度。从每个方法里也可以看出,每个方法基本上都有Model方法的调用和View的相应的操作调用。

好了,我们的Model、View、Presenter都写好了。接下来就是Activity或Fragment界面的调用了。

todo-mvp的demo里是Activity里调用了TaskFragment进行View的操作。那看下TaskFragment里的具体逻辑,TaskFragment先实现之前View里调用的接口TasksContract.View:

  1. /**
  2. * Created by Tandong on 2018/4/2.
  3. * Fragment实现View的接口方法回调
  4. */
  5. public class TaskFragment extends BaseFragment implements TasksContract.View, OnItemCallBack {
  6. @Bind(R.id.rv)
  7. RecyclerView recyclerView;
  8. @Bind(R.id.tv_tips)
  9. TextView tv_tips;
  10. private TaskAdapter taskAdapter;
  11. private LinearLayoutManager linearLayoutManager;
  12. private TasksContract.Presenter presenter;
  13. public TaskFragment() {
  14. // Requires empty public constructor
  15. }
  16. public static TaskFragment newInstance() {
  17. return new TaskFragment();
  18. }
  19. @Override
  20. public void onCreate(@Nullable Bundle savedInstanceState) {
  21. super.onCreate(savedInstanceState);
  22. }
  23. @Nullable
  24. @Override
  25. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  26. View root = inflater.inflate(R.layout.fragment_tasks, container, false);
  27. ButterKnife.bind(this, root);
  28. return root;
  29. }
  30. @Override
  31. public void onActivityCreated(@Nullable Bundle savedInstanceState) {
  32. super.onActivityCreated(savedInstanceState);
  33. linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
  34. taskAdapter = new TaskAdapter(getActivity(), Utils.getTasksFromSp());
  35. taskAdapter.setOnItemCallBack(this);
  36. recyclerView.setLayoutManager(linearLayoutManager);
  37. recyclerView.setAdapter(taskAdapter);
  38. }
  39. @Override
  40. public void onResume() {
  41. super.onResume();
  42. presenter.start();
  43. }
  44. @Override
  45. public void setPresenter(TasksContract.Presenter presenter) {
  46. this.presenter = Utils.checkNotNull(presenter);
  47. }
  48. @Override
  49. public void setLoadingIndicator(boolean show) {
  50. if (show) {
  51. tv_tips.setVisibility(View.VISIBLE);
  52. tv_tips.setText("加载中...");
  53. } else {
  54. tv_tips.setVisibility(View.GONE);
  55. }
  56. }
  57. @Override
  58. public void showTasks(List<Task> tasks) {
  59. //view的操作具体逻辑实现
  60. taskAdapter.setDatas(tasks);
  61. }
  62. @Override
  63. public void showAddTaskOk(Task task) {
  64. //view的操作具体逻辑实现
  65. showToast("添加任务成功~");
  66. taskAdapter.addData(task);
  67. }
  68. @Override
  69. public void showTaskDetailsUi(String taskId) {
  70. //view的操作具体逻辑实现
  71. }
  72. @Override
  73. public void showLoadingTasksError() {
  74. //view的操作具体逻辑实现
  75. tv_tips.setVisibility(View.VISIBLE);
  76. tv_tips.setText("加载失败~");
  77. }
  78. @Override
  79. public void showNoTasks() {
  80. //view的操作具体逻辑实现
  81. tv_tips.setVisibility(View.VISIBLE);
  82. tv_tips.setText("没有数据~");
  83. }
  84. @Override
  85. public boolean isActive() {
  86. //view的操作具体逻辑实现
  87. return isAdded();
  88. }
  89. @Override
  90. public void showClearedAllTaskOk() {
  91. //view的操作具体逻辑实现
  92. tv_tips.setVisibility(View.VISIBLE);
  93. tv_tips.setText("没有数据~");
  94. }
  95. @Override
  96. public void showClearedTaskOk(Task task) {
  97. taskAdapter.removeData(task);
  98. }
  99. @Override
  100. public void onItemLongClick(View v, int posiotion) {
  101. showToast("长按移除 " + posiotion);
  102. presenter.clearTask(taskAdapter.getTasks().get(posiotion));
  103. }
  104. @Override
  105. public void onItemClick(View v, int position) {
  106. Task task = taskAdapter.getTasks().get(position);
  107. showToast(position + " " + task.getTitle() + " " + task.getContent());
  108. }
  109. }

然后在实现的回调方法里,把返回的数据或者状态进行相应的操作和UI上的响应。也可以操作presenter里的一些方法。

那么整合MVP的todo-mvp的官方例子基本上这样,官方的todo-mvp里的功能多一点,有展示、添加、详情等分类。不过流程和操作都类似,MVP的官方例子就给大家讲解这么多。

整理后的Demo的github地址:https://github.com/jaychou2012/MVPDemo-todo-mvp


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/150199
推荐阅读
相关标签
  

闽ICP备14008679号