Android MVP MVVM框架梳理 (一)

Android MVP框架梳理 梳理下Google推荐的MVP MVVM Demo
源码Git地址

  • todo‑mvp
  • todo‑mvp‑clean
  • todo‑mvp‑dagger
  • todo‑mvp‑rxjava
  • todo‑mvvm‑databinding
  • todo‑mvvm‑live

todo‑mvp

todo‑mvp源码Git地址

这个项目中还用到了guava room等框架包 会逐渐梳理出来~

1.MVP简介

MVC是基于MVC模式框架上发展而来的,在Android中代码框架中 大家都会使用MVC模式 MVC在代码中的应用如下:

Modle:对应Bean对象和数据
View:对应XML布局
Control:对应Activity

但是Activity中经常需要处理View的好多逻辑业务,导致Activity业务很臃肿 复杂。MVP 模式可以很好的解决这种问题 使代码结构更清晰 业务更明确 更易于理解阅读
MVP框架职责如下:

Modle:依然对应Bean对象和数据
View:对应Activity Fragment 只做View的展示 
Presenter:支持人角色 负责业务逻辑处理

项目包结构如下:

android 在mvvm模式 里如何实现页面跳转 android mvvm mvp_android

项目类结构如下:

android 在mvvm模式 里如何实现页面跳转 android mvvm mvp_ide_02


除了util工具类 其余的包都是按照业务逻辑在自己的包中,每个包都符合MVP模式

2.包中功能解释

2.1data包

android 在mvvm模式 里如何实现页面跳转 android mvvm mvp_mvp_03

1.定义了bean对象 Task
2.定义了数据源的接口 TasksDataSource
3.实现了数据仓库接口:本地数据仓库,远程数据仓库
4.实现综合数据仓库TasksRepository 里边有本地和远程的数据仓库的对象 可以实现数据源的不同存储,缓存的功能

2.2addedittask包

addedittask,statistics,taskdetail,tasks里边有事一个MVP模式 只梳理下addedittask中的逻辑 其他包是类似的

android 在mvvm模式 里如何实现页面跳转 android mvvm mvp_ide_04

AddEditTaskActivity:初始化View层和Presenter层 并实现Presenter和View的绑定
AddEditTaskContract:定义了View和Presenter的接口
AddEditTaskFragment:View层实现了AddEditTaskContract中的View接口
AddEditTaskPresenter :Presenter层 实现了AddEditTaskContract的Presenter接口
2.2.1 AddEditTaskContract.java

定义了View和Presenter的接口

public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void saveTask(String title, String description);

        void populateTask();

        boolean isDataMissing();
    }
}
2.2.2 AddEditTaskFragment.java
//实现View层的接口
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
...

    //点击事件交给Presenter层去处理
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
        }
    });
...
    //Presenter层处理完逻辑 回调之View层展现
    @Override
    public void showEmptyTaskError() {
        Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();
    }

    @Override
    public void showTasksList() {
        getActivity().setResult(Activity.RESULT_OK);
        getActivity().finish();
    }
2.2.3 AddEditTaskPresenter.java
//实现Presenter的接口
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
        TasksDataSource.GetTaskCallback {
    //处理View层发送来的请求
    @Override
    public void saveTask(String title, String description) {
        if (isNewTask()) {
            createTask(title, description);
        } else {
            updateTask(title, description);
        }
    }

    private void updateTask(String title, String description) {
        if (isNewTask()) {
            throw new RuntimeException("updateTask() was called but task is new.");
        }
        //交于综合数据仓库去处理数据
        mTasksRepository.saveTask(new Task(title, description, mTaskId));
        //处理完后回调View层展示处理结果
        mAddTaskView.showTasksList(); // After an edit, go back to the list.
    }
}
2.2.4 AddEditTaskActivity.java

初始化View层和Presenter层 并实现Presenter和View的绑定

//初始化View层
    if (addEditTaskFragment == null) {
        addEditTaskFragment = AddEditTaskFragment.newInstance();
    }

    boolean shouldLoadDataFromRepo = true;

    // Prevent the presenter from loading data from the repository if this is a config change.
    if (savedInstanceState != null) {
        // Data might not have loaded when the config change happen, so we saved the state.
        shouldLoadDataFromRepo = savedInstanceState.getBoolean(SHOULD_LOAD_DATA_FROM_REPO_KEY);
        }

    //初始化View层和Presenter层 并实现Presenter和View的绑定
    mAddEditTaskPresenter = new AddEditTaskPresenter(
        taskId,
        Injection.provideTasksRepository(getApplicationContext()),
        addEditTaskFragment,
        shouldLoadDataFromRepo);
2.2.5 TasksDataSource 数据处理相关的
private void updateTask(String title, String description) {
        if (isNewTask()) {
            throw new RuntimeException("updateTask() was called but task is new.");
        }
        //交于综合数据仓库去处理数据
        mTasksRepository.saveTask(new Task(title, description, mTaskId));
        //处理完后回调View层展示处理结果
        mAddTaskView.showTasksList(); // After an edit, go back to the list.
    }

    /**
     * 综合数据仓储处理
     * TasksRepository.java
     * @param task
     */
    @Override
    public void saveTask(@NonNull Task task) {
        checkNotNull(task);
        //远程数据仓储处理
        mTasksRemoteDataSource.saveTask(task);
        //本地数据仓储处理
        mTasksLocalDataSource.saveTask(task);

        // 加入本地缓存
        if (mCachedTasks == null) {
            mCachedTasks = new LinkedHashMap<>();
        }
        mCachedTasks.put(task.getId(), task);
    }

    /**
     * 本地数据存储
     * 因为操作数据库需要放在非UI线程处理
     * TasksLocalDataSource.java
     * @param task
     */
    @Override
    public void saveTask(@NonNull final Task task) {
        checkNotNull(task);
        //启动线程 在非UI进程进行数据库操作
        Runnable saveRunnable = new Runnable() {
            @Override
            public void run() {
                mTasksDao.insertTask(task);
            }
        };
        mAppExecutors.diskIO().execute(saveRunnable);
    }

    /**
     * 远程数据操作处理
     * 此处代码为模拟的
     * TasksRemoteDataSource.java
     *
     * @param task
     */
    @Override
    public void saveTask(@NonNull Task task) {
        TASKS_SERVICE_DATA.put(task.getId(), task);
    }