程序员

学习笔记| (一) MVP

2018-09-20  本文已影响25人  yanerly

前言:

前几天去面试,有一家公司问到会不会mvp,说说对他的理解,你的项目中是是如何实现的,然而我只是看了很多讲mvp的文章,并没有真正的在项目中用过,毕竟也都是小项目......然后的然后就让我现场写mvp.....

推荐一篇通俗易懂的文章

https://www.jianshu.com/p/389c9ae1a82c
之前也看了很多的文章,但是看得越多就越乱,看到最后也没有真正弄懂mvp到底是怎么个回事,结合google的todomvp和这篇文章,大致理清了mvp是咋回事了。

什么是mvp?

既然说presenter是中间层,那在哪实现的presenter呢?

这个是当时问到我的一个题目。准确的说应该是在Activity中创建的presenter实例,将view和model作为参数传过去了。

什么是view?

根据google官方的例子,view其实是Fragment,Activity只是一个全局的控制者,负责创建view以及presenter实例。

view和presenter是怎么关联的?

在presenter的构造函数中调用view的setPresenter(this)就可以将二者绑定了。

前面做了这么多铺垫,接下来分析一下google官方demo(参考上面的链接分析的):

编辑页.jpg 统计页.jpg
public interface BasePresenter {

    //presenter开始获取数据并调用view中方法改变界面显示,其调用时机是在Fragment类的onResume方法中。
    void start();

}
public interface BaseView<T> {

    //view要持有presenter的引用,在将presenter实例传入view中,其调用时机是presenter实现类的构造函数中。
    void setPresenter(T presenter);
}
/**
 * 契约类来统一管理view与presenter的所有的接口
 */
public interface TaskDetailContract {

    interface View extends BaseView<Presenter> {
        //定义了该界面(功能)中所有的UI状态情况
        // 设置数据加载状态
        void setLoadingIndicator(boolean active);

        // 处理task加载失败的情况
        void showMissingTask();

        // 隐藏待办事项title
        void hideTitle();

        // 显示待办事项title
        void showTitle(String title);

        // 隐藏待办事项的描述
        void hideDescription();

        // 显示待办事项的描述
        void showDescription(String description);

        //显示待办事项的状态,是否已完成
        void showCompletionStatus(boolean complete);

        //显示要编辑的待办事项
        void showEditTask(String taskId);

        //显示已删除的待办事项
        void showTaskDeleted();

        //显示已完成的待办事项
        void showTaskMarkedComplete();

        //显示未完成的点半事项
        void showTaskMarkedActive();

        //是否未完成
        boolean isActive();
    }

    interface Presenter extends BasePresenter {
        //定义了该界面(功能)中所有的用户操作事件
        // 修改待办事项
        void editTask();

        // 删除待办事项
        void deleteTask();

        // 标记完成
        void completeTask();

        // 标记未完成
        void activateTask();
    }
}

这样写了之后,这个功能有哪些功能操作,以及ui上会有哪些变化都很清楚

public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
private TaskDetailContract.Presenter mPresenter;
     .....
  @Override
    public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
       //判空,在Presenter中会将view和presenter进行绑定,这里是检查传入的presenter是否为空
        mPresenter = checkNotNull(presenter);
    }
      ......
    @Override
    public void onResume() {
        super.onResume();
        //开始获取model中的数据
        mPresenter.start();
    }
      ......
  @Override
    public void hideDescription() {
        //隐藏待办事件的描述
        mDetailDescription.setVisibility(View.GONE);
    }
      ......
}

TaskDetailFragment 作为view层,主要负责ui相关的状态更新,实现了契约类中的View接口后,在相关的代码中实现具体的内容。

/**
 * Activity在项目中是一个全局的控制者,负责创建view以及presenter实例,并将二者联系起来。
 */
public class TaskDetailActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState){
   //创建view
  TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager()
                .findFragmentById(R.id.contentFrame);

 //创建presenter实例
        new TaskDetailPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                taskDetailFragment);
 }
}

创建Presenter


/**
 * 作为Presenter层,实现了该接口,如此 TaskDetailPresenter 则只关注业务层的逻辑相关,UI的更新只需调用View的状态方法。
 */
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
   /**
     * 构造函数,presenter作为中间层链接model和view,为了相互交互,则要持有他们的引用
     * @param taskId : 待办事项id
     * @param tasksRepository : model
     * @param taskDetailView : view
     */
    public TaskDetailPresenter(@Nullable String taskId,
                               @NonNull TasksRepository tasksRepository,
                               @NonNull TaskDetailContract.View taskDetailView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");

        // 保持对View(TaskDetailFragment)的引用
        mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");

        // 使View(TaskDetailFragment)也保持对自身(TaskDetailPresenter)的引用
        mTaskDetailView.setPresenter(this);
    }

  @Override
    public void start() {
        //开始获取数据
        openTask();
    }
}
/**
     * 获取数据
     */
    private void openTask() {
        // 判空处理
        if (Strings.isNullOrEmpty(mTaskId)) {
            //加载数据的时候,如果taskId为空,则调用view的加载失败的情况
            mTaskDetailView.showMissingTask();
            return;
        }

        // 更新状态,加载成功的时候,设置加载的状态,
        mTaskDetailView.setLoadingIndicator(true);

        // modele,获取该条Task数据
        mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                //数据加载成功
                // View已经被用户回退(已销毁)
                if (!mTaskDetailView.isActive()) {
                    return;
                }

                // 获取到task数据,并更新UI
                mTaskDetailView.setLoadingIndicator(false);
                if (null == task) {
                    //如果没有加载到task,则显示加载失败的ui
                    mTaskDetailView.showMissingTask();
                } else {
                    //否则,显示这个待办事项
                    showTask(task);
                }
            }

            @Override
            public void onDataNotAvailable() {
                // 显示数据获取失败时的状态
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.showMissingTask();
            }
        });
    }

再看一下TasksRepository的getTask():

@Override
    public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
        // 判空处理
        checkNotNull(taskId);
        checkNotNull(callback);

        // 获取缓存数据
        Task cachedTask = getTaskWithId(taskId);

        // Respond immediately with cache if available
        if (cachedTask != null) {
            callback.onTaskLoaded(cachedTask);
            return;
        }

        // Load from server/persisted if needed.

        // Is the task in the local data source? If not, query the network.
        // 从本地数据源(SQLite数据库)中获取,没有获取到,再从网络数据源获取
        mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                // Do in memory cache update to keep the app UI up to date
                if (mCachedTasks == null) {
                    mCachedTasks = new LinkedHashMap<>();
                }
                mCachedTasks.put(task.getId(), task);
                // 成功,则回调
                callback.onTaskLoaded(task);
            }

            @Override
            public void onDataNotAvailable() {
                // 失败,则从远程数据源(网络)中获取
                mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() {
                    @Override
                    public void onTaskLoaded(Task task) {
                        // Do in memory cache update to keep the app UI up to date
                        if (mCachedTasks == null) {
                            mCachedTasks = new LinkedHashMap<>();
                        }
                        mCachedTasks.put(task.getId(), task);
                        // 回调成功时的方法
                        callback.onTaskLoaded(task);
                    }

                    @Override
                    public void onDataNotAvailable() {
                        // 回调失败时的方法
                        callback.onDataNotAvailable();
                    }
                });
            }
        });
    }
public interface TasksDataSource {

    //加载数据的回调
    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    //获取数据的回调
    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    /**
     * 获取所有task
     * @param callback
     */
    void getTasks(@NonNull LoadTasksCallback callback);

    /**
     * 根据id获取单个task
     * @param taskId
     * @param callback
     */
    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    /**
     * 保存task
     * @param task
     */
    void saveTask(@NonNull Task task);

    /**
     * 标记为完成
     * @param task
     */
    void completeTask(@NonNull Task task);

    /**
     * 标记为完成
     * @param taskId
     */
    void completeTask(@NonNull String taskId);

    /**
     * 标记为未完成
     * @param task
     */
    void activateTask(@NonNull Task task);

    /**
     * 标记为未完成
     * @param taskId
     */
    void activateTask(@NonNull String taskId);

    /**
     * 删除所有已完成task
     */
    void clearCompletedTasks();

    /**
     * 刷新task
     */
    void refreshTasks();

    /**
     * 删除所有task
     */
    void deleteAllTasks();

    /**
     * 删除单个task
     * @param taskId
     */
    void deleteTask(@NonNull String taskId);
}

可以把TasksRepository理解为一个大粮仓,里面又有2个小粮仓,有我的粮食库,也有你的粮食库,我们需要粮食的时候都要到对应的粮食库中取。所有的粮食库都实现TasksDataSource接口

   //远程数据源
    private final TasksDataSource mTasksRemoteDataSource;

    //本地数据源
    private final TasksDataSource mTasksLocalDataSource;
  private static TasksRepository INSTANCE = null;

  // Prevent direct instantiation.
    private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                            @NonNull TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
        mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
    }

    public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
                                              TasksDataSource tasksLocalDataSource) {
        if (INSTANCE == null) {
            INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
        }
        return INSTANCE;
    }
  ......
@Override
    public void deleteTask(@NonNull String taskId) {
     //根据id删除Task
        ① mTasksRemoteDataSource.deleteTask(checkNotNull(taskId));
        ② mTasksLocalDataSource.deleteTask(checkNotNull(taskId));

        mCachedTasks.remove(taskId);
    }
  ......
public class TasksLocalDataSource implements TasksDataSource {
    private static volatile TasksLocalDataSource INSTANCE;

  // Prevent direct instantiation.
    private TasksLocalDataSource(@NonNull AppExecutors appExecutors,
            @NonNull TasksDao tasksDao) {
        mAppExecutors = appExecutors;
        mTasksDao = tasksDao;
    }

    public static TasksLocalDataSource getInstance(@NonNull AppExecutors appExecutors,
            @NonNull TasksDao tasksDao) {
        if (INSTANCE == null) {
            synchronized (TasksLocalDataSource.class) {
                if (INSTANCE == null) {
                    INSTANCE = new TasksLocalDataSource(appExecutors, tasksDao);
                }
            }
        }
        return INSTANCE;
    }
}

这个类是一个单例,实现了TasksDataSource ,刚刚在上面说过"本地数据源"就相当于一个小粮仓,要从里面取粮食就要实现TasksDataSource 这个接口才行。

然后实现接口后做具体的实现:

  ......
 @Override
    public void saveTask(@NonNull final Task task) {
        checkNotNull(task);
        Runnable saveRunnable = new Runnable() {
            @Override
            public void run() {
              //在数据库中插入数据
                mTasksDao.insertTask(task);
            }
        };
        mAppExecutors.diskIO().execute(saveRunnable);
    }
    ......
    @Override
    public void completeTask(@NonNull final Task task) {
        Runnable completeRunnable = new Runnable() {
            @Override
            public void run() {
                  //在数据库中更新数据
                mTasksDao.updateCompleted(task.getId(), true);
            }
        };

        mAppExecutors.diskIO().execute(completeRunnable);
    }
    ......
public class Injection {

    /**
     * 返回的是model
     * @param context
     * @return
     */
    public static TasksRepository provideTasksRepository(@NonNull Context context) {
        checkNotNull(context);
        //创建数据库
        ToDoDatabase database = ToDoDatabase.getInstance(context);

        //创建TasksRepository :远程数据源、本地数据源
        return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
                TasksLocalDataSource.getInstance(new AppExecutors(), database.taskDao()));
    }
}

它的代码不多,提供了一个静态方法供外部调用,而这个方法居然返回的是 TasksRepository ,根据上面的分析,知道他相当于是model,为什么要这么写呢?

//创建presenter实例
        new TaskDetailPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                taskDetailFragment);

原来在一开始创建Presenter的时候就使用了,Presenter要连接view和model,所有就要传入他们的引用。

现在整个逻辑就很清楚了。

public class TasksRemoteDataSource implements TasksDataSource {
private static TasksRemoteDataSource INSTANCE;

 // Prevent direct instantiation.
private TasksRemoteDataSource() {}

public static TasksRemoteDataSource getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new TasksRemoteDataSource();
        }
        return INSTANCE;
    }
}

其余的和本地数据源一样,只是它是从服务端读取数据

over.....

上一篇下一篇

猜你喜欢

热点阅读