欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android MVP模式介绍和讲解

程序员文章站 2022-04-05 17:09:27
...

Android MVP模式介绍和讲解

作者:谭东

先给个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数据。有增加一条、删除一条、获取一条、获取全部任务的功能。

先看原版的实现设计图:

Android MVP模式介绍和讲解

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

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

Android MVP模式介绍和讲解




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

Android 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

Android MVP模式介绍和讲解

相关标签: mvp