赞
踩
作者:谭东
先给个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:
- /**
- * Created by Tandong on 2018/4/2.
- * MVP中某个M的基础实体类
- */
-
- public class Task {
- private String id;
- private String date;
- private String title;
- private String content;
- private boolean completed;
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getDate() {
- return date;
- }
-
- public void setDate(String date) {
- this.date = date;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
-
- public boolean isCompleted() {
- return completed;
- }
-
- public void setCompleted(boolean completed) {
- this.completed = completed;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Task task = (Task) o;
- return id.equals(task.id);
- }
- }
以及操作Task实体的定义的方法,TaskDao,它的作用就是预先定义对外暴漏操作Task的接口方法:
- /**
- * Created by Tandong on 2018/4/2.
- * 对应的Task的Model的主要方法接口定义类
- */
-
- public interface TaskDao {
- void getTasks(@NonNull LoadTasksCallback callback);
-
- void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
-
- void saveTask(@NonNull Task task);
-
- void refreshTasks();
-
- void deleteAllTasks();
-
- void deleteTask(@NonNull Task task);
-
- interface LoadTasksCallback {
-
- void onTasksLoaded(List<Task> tasks);
-
- void onDataNotAvailable();
- }
-
- interface GetTaskCallback {
-
- void onTaskLoaded(Task task);
-
- void onDataNotAvailable();
- }
- }
之后,再实现TaskDao里的定义的接口方法,也就是要有TaskImpl实现类:
- /**
- * Created by Tandong on 2018/4/2.
- * Task的Model的接口方法的具体实现,采用单例模式
- */
-
- public class TaskDaoImpl implements TaskDao {
- private static TaskDaoImpl INSTANCE = null;
- private static final int SERVICE_LATENCY_IN_MILLIS = 500;
- private static List<Task> TASKS_SERVICE_DATA = null;
-
- // 防止直接实例化
- private TaskDaoImpl() {
- }
-
- /**
- * 单例模式创建TaskDaoImpl
- *
- * @return
- */
- public static TaskDaoImpl getInstance() {
- if (INSTANCE == null) {
- INSTANCE = new TaskDaoImpl();
- }
- return INSTANCE;
- }
-
- /**
- * 销毁回收实例对象
- */
- public static void destroyInstance() {
- INSTANCE = null;
- }
-
- @Override
- public void getTasks(@NonNull final LoadTasksCallback callback) {
- //逻辑的具体实现,这里获取数据
- Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- TASKS_SERVICE_DATA = Utils.getTasksFromSp();
- callback.onTasksLoaded(TASKS_SERVICE_DATA);
- }
- }, SERVICE_LATENCY_IN_MILLIS);
- }
-
- @Override
- public void getTask(@NonNull String taskId, @NonNull final GetTaskCallback callback) {
- int position = 0;
- TASKS_SERVICE_DATA = Utils.getTasksFromSp();
- for (int i = 0; i < TASKS_SERVICE_DATA.size(); i++) {
- if (TASKS_SERVICE_DATA.get(i).getId().equals(taskId)) {
- position = i;
- }
- }
- //逻辑的具体实现
- final Task task = TASKS_SERVICE_DATA.get(position);
- Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- callback.onTaskLoaded(task);
- }
- }, SERVICE_LATENCY_IN_MILLIS);
- }
-
- @Override
- public void saveTask(@NonNull Task task) {
- //逻辑的具体实现
- TASKS_SERVICE_DATA.add(task);
- Utils.addTaskToSp(task);
- }
-
- @Override
- public void refreshTasks() {
- //逻辑的具体实现
- TASKS_SERVICE_DATA = Utils.getTasksFromSp();
- }
-
- @Override
- public void deleteAllTasks() {
- //逻辑的具体实现
- TASKS_SERVICE_DATA.clear();
- Utils.deleteAllTaskToSp();
- }
-
- @Override
- public void deleteTask(@NonNull Task task) {
- //逻辑的具体实现
- Utils.deleteTaskToSp(task);
- }
- }
那么可以看出,Model里一般有3个类,以这个todo-mvp例子来说,有实体类Task,接口方法类TaskDao,实现类TaskImpl。
接着定义我们的View:
根据情况,我们可以定义一个父类的View叫BaseView,当然直接写一个View也没问题。
这个View的类里我们定义好我们的Activity或者Fragment界面需要交互操作的一些方法。
- interface View extends BaseView<Presenter> {
- /**
- * 这里定义操作View界面相关的回调方法
- */
- void setLoadingIndicator(boolean show);
-
- void showTasks(List<Task> tasks);
-
- void showAddTaskOk(Task task);
-
- void showTaskDetailsUi(String taskId);
-
- void showLoadingTasksError();
-
- void showNoTasks();
-
- boolean isActive();
-
- void showClearedAllTaskOk();
-
- void showClearedTaskOk(Task task);
- }
然后定义Presenter,Presenter的中文意思是主持人,顾名思义也就是整个Model和View的调度和指挥者。Presenter就是把Model的TaskImpl类和View的View类关联结合起来。里面定义的方法也是对应View和Model相连接调用的中间件接口方法,类似于一个中间转接接口。
- interface Presenter extends BasePresenter {
- /**
- * 这里定义操作加载处理数据的相关回调方法
- */
-
- void loadTasks(boolean showLoading);
-
- void addNewTask(@NonNull Task task);
-
- void openTaskDetails(@NonNull Task task);
-
- void clearAllTasks();
-
- void clearTask(@NonNull Task task);
- }
google官方的todo-mvp的例子中间又加了一层,Contract类,就是一个连接关联类Contract,把View和Presenter关联起来。
- /**
- * Created by Tandong on 2018/4/2.
- * 一个连接关联类Contract,把View和Presenter关联起来
- */
-
- public interface TasksContract {
-
- interface View extends BaseView<Presenter> {
- /**
- * 这里定义操作View界面相关的回调方法
- */
- void setLoadingIndicator(boolean show);
-
- void showTasks(List<Task> tasks);
-
- void showAddTaskOk(Task task);
-
- void showTaskDetailsUi(String taskId);
-
- void showLoadingTasksError();
-
- void showNoTasks();
-
- boolean isActive();
-
- void showClearedAllTaskOk();
-
- void showClearedTaskOk(Task task);
- }
-
- interface Presenter extends BasePresenter {
- /**
- * 这里定义操作加载处理数据的相关回调方法
- */
-
- void loadTasks(boolean showLoading);
-
- void addNewTask(@NonNull Task task);
-
- void openTaskDetails(@NonNull Task task);
-
- void clearAllTasks();
-
- void clearTask(@NonNull Task task);
- }
-
- }
这一层我觉的可以调整,可有可无。把这个写成一个View就可以。
然后定义编写我们的真正的Presenter实现类,TasksPresenter类。
- /**
- * Created by Tandong on 2018/4/2.
- * Presenter的业务真正实现,监听用户UI操作,回调和更新数据给UI
- */
-
- public class TasksPresenter implements TasksContract.Presenter {
- private final TaskDaoImpl taskDaoImpl;
- private final TasksContract.View tasksView;
-
- public TasksPresenter(@NonNull TaskDaoImpl taskDaoImpl, @NonNull TasksContract.View tasksView) {
- this.taskDaoImpl = Utils.checkNotNull(taskDaoImpl, "tasksRepository cannot be null");
- this.tasksView = Utils.checkNotNull(tasksView, "tasksView cannot be null!");
-
- this.tasksView.setPresenter(this);
- }
-
- @Override
- public void start() {
- loadTasks(true);
- }
-
- @Override
- public void loadTasks(boolean showLoading) {
- toLoadTasks(showLoading);
- }
-
- /**
- * 添加数据操作
- */
- @Override
- public void addNewTask(@NonNull Task task) {
- //操作Model
- taskDaoImpl.saveTask(task);
- //操作View
- tasksView.showAddTaskOk(task);
- }
-
- @Override
- public void openTaskDetails(@NonNull Task requestedTask) {
- Utils.checkNotNull(requestedTask, "requestedTask cannot be null!");
- tasksView.showTaskDetailsUi(requestedTask.getId());
- }
-
- /**
- * 删除全部数据操作
- */
- @Override
- public void clearAllTasks() {
- //操作Model
- taskDaoImpl.deleteAllTasks();
- //操作View
- tasksView.showClearedAllTaskOk();
- }
-
- /**
- * 删除单条数据操作
- *
- * @param task
- */
- @Override
- public void clearTask(@NonNull Task task) {
- //操作Model
- taskDaoImpl.deleteTask(task);
- //操作View
- tasksView.showClearedTaskOk(task);
- }
-
- private void toLoadTasks(final boolean showLoadingUI) {
- if (showLoadingUI) {//显示加载中进度条布局
- tasksView.setLoadingIndicator(true);
- }
- taskDaoImpl.getTasks(new TaskDao.LoadTasksCallback() {
- @Override
- public void onTasksLoaded(List<Task> tasks) {
- // The view may not be able to handle UI updates anymore
- if (!tasksView.isActive()) {
- return;
- }
- if (showLoadingUI) {//隐藏加载中进度条
- tasksView.setLoadingIndicator(false);
- }
- processTasks(tasks);
- }
-
- @Override
- public void onDataNotAvailable() {
- // The view may not be able to handle UI updates anymore
- if (!tasksView.isActive()) {
- return;
- }
- tasksView.showLoadingTasksError();
- }
- });
- }
-
- private void processTasks(List<Task> tasks) {
- if (tasks.isEmpty()) {
- // Show a message indicating there are no tasks for that filter type.
- processEmptyTasks();
- } else {
- //加载完毕后回调给View进行显示逻辑处理
- tasksView.showTasks(tasks);
- }
- }
-
- private void processEmptyTasks() {
- tasksView.showNoTasks();
- }
-
- }
可以看出,这个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:
- /**
- * Created by Tandong on 2018/4/2.
- * Fragment实现View的接口方法回调
- */
-
- public class TaskFragment extends BaseFragment implements TasksContract.View, OnItemCallBack {
- @Bind(R.id.rv)
- RecyclerView recyclerView;
- @Bind(R.id.tv_tips)
- TextView tv_tips;
- private TaskAdapter taskAdapter;
- private LinearLayoutManager linearLayoutManager;
- private TasksContract.Presenter presenter;
-
- public TaskFragment() {
- // Requires empty public constructor
- }
-
- public static TaskFragment newInstance() {
- return new TaskFragment();
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Nullable
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.fragment_tasks, container, false);
- ButterKnife.bind(this, root);
- return root;
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
- taskAdapter = new TaskAdapter(getActivity(), Utils.getTasksFromSp());
- taskAdapter.setOnItemCallBack(this);
- recyclerView.setLayoutManager(linearLayoutManager);
- recyclerView.setAdapter(taskAdapter);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- presenter.start();
- }
-
- @Override
- public void setPresenter(TasksContract.Presenter presenter) {
- this.presenter = Utils.checkNotNull(presenter);
- }
-
- @Override
- public void setLoadingIndicator(boolean show) {
- if (show) {
- tv_tips.setVisibility(View.VISIBLE);
- tv_tips.setText("加载中...");
- } else {
- tv_tips.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void showTasks(List<Task> tasks) {
- //view的操作具体逻辑实现
- taskAdapter.setDatas(tasks);
- }
-
- @Override
- public void showAddTaskOk(Task task) {
- //view的操作具体逻辑实现
- showToast("添加任务成功~");
- taskAdapter.addData(task);
- }
-
- @Override
- public void showTaskDetailsUi(String taskId) {
- //view的操作具体逻辑实现
- }
-
- @Override
- public void showLoadingTasksError() {
- //view的操作具体逻辑实现
- tv_tips.setVisibility(View.VISIBLE);
- tv_tips.setText("加载失败~");
- }
-
- @Override
- public void showNoTasks() {
- //view的操作具体逻辑实现
- tv_tips.setVisibility(View.VISIBLE);
- tv_tips.setText("没有数据~");
- }
-
- @Override
- public boolean isActive() {
- //view的操作具体逻辑实现
- return isAdded();
- }
-
- @Override
- public void showClearedAllTaskOk() {
- //view的操作具体逻辑实现
- tv_tips.setVisibility(View.VISIBLE);
- tv_tips.setText("没有数据~");
- }
-
- @Override
- public void showClearedTaskOk(Task task) {
- taskAdapter.removeData(task);
- }
-
- @Override
- public void onItemLongClick(View v, int posiotion) {
- showToast("长按移除 " + posiotion);
- presenter.clearTask(taskAdapter.getTasks().get(posiotion));
- }
-
- @Override
- public void onItemClick(View v, int position) {
- Task task = taskAdapter.getTasks().get(position);
- showToast(position + " " + task.getTitle() + " " + task.getContent());
- }
- }
然后在实现的回调方法里,把返回的数据或者状态进行相应的操作和UI上的响应。也可以操作presenter里的一些方法。
那么整合MVP的todo-mvp的官方例子基本上这样,官方的todo-mvp里的功能多一点,有展示、添加、详情等分类。不过流程和操作都类似,MVP的官方例子就给大家讲解这么多。
整理后的Demo的github地址:https://github.com/jaychou2012/MVPDemo-todo-mvp
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。