博客主页
官方网址: https://github.com/android/ar...
该示例有四个界面功能:任务列表、任务详情、任务添加编辑、任务统计
代码结构:按照功能分包,Activity、Fragment、Contract、Presenter四种类文件
测试代码结构:androidTest(UI层测试)、androidTestMock(UI层测试mock数据支持)、test(业务层单元测试)、mock(业务层单元测试mock数据支持)
1、首先看下两个Base接口的基类:BaseView与BasePresenter,分别是所有View与Presenter的基类
// 接受一个泛型参数,泛型类为Presenter的具体实现类 public interface BaseView<T> { // View必须实现setPresenter方法,View持有对Presenter的引用 void setPresenter(T presenter); }
setPresenter的调用时机在Presenter具体实现的构造方法中,这样View与Presenter关联起来了。
public interface BasePresenter { // 规定Presenter必须要实现start方法 void start(); }
该start方法的作用:Presenter开始获取数据并调用View的方法刷新UI,调用时机是在Activity(或者Fragment)的onResume方法。
2、定义契约类(合同)
Google引用契约类,主要作用是用来统一管理View和Presenter接口,使得View和Presenter中有哪些功能,一目了然,便于维护。下面通过任何列表界面来分析:
public interface TasksContract { interface View extends BaseView<Presenter> { void showTasks(List<Task> tasks); // ... 省略其它的UI操作 } interface Presenter extends BasePresenter { void loadTasks(boolean forceUpdate); // ... 省略其它的业务操作 } }
3、Model层
它的作用主要用来获取数据、存取数据、数据状态更新等。先看下TasksRepository的getTasks方法
/** * Gets tasks from cache, local data source (SQLite) or remote data source, whichever is available first. */ @Override public void getTasks(@NonNull final LoadTasksCallback callback) { checkNotNull(callback); // 判空处理,提前发现异常的方式 // 如果内存中有缓存数据并且不是脏数据,立即返回 if (mCachedTasks != null && !mCacheIsDirty) { callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); return; } if (mCacheIsDirty) { // 如果缓存中是脏数据,需要从网络获取新的数据 getTasksFromRemoteDataSource(callback); } else { // 如果本地SQLite中缓存数据可用,返回;否则从网络获取新的数据 mTasksLocalDataSource.getTasks(new LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { refreshCache(tasks); // 更新内存中缓存数据 callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); // 成功回调 } @Override public void onDataNotAvailable() { // 回调该方法,说明缓存中的数据不可用,从网络获取新的数据 getTasksFromRemoteDataSource(callback); } }); } } private void getTasksFromRemoteDataSource(@NonNull final LoadTasksCallback callback) { mTasksRemoteDataSource.getTasks(new LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { refreshCache(tasks); // 更新内存中缓存数据 refreshLocalDataSource(tasks); // 更新SQLite中数据 callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); // 成功回调数据 } @Override public void onDataNotAvailable() { callback.onDataNotAvailable(); // 没有可用的数据 } }); }
TasksRepository中维护着两个数据源:mTasksRemoteDataSource(从网络中获取的数据源)和 mTasksLocalDataSource(从本地SQLite获取的数据源)
// 远程端数据源 private final TasksDataSource mTasksRemoteDataSource; // 本地SQLite数据源 private final TasksDataSource mTasksLocalDataSource;
这两个数据源都实现了TasksDataSource接口,其中TasksRepository也实现了该接口
public interface TasksDataSource { interface LoadTasksCallback { void onTasksLoaded(List<Task> tasks); void onDataNotAvailable(); } interface GetTaskCallback { void onTaskLoaded(Task task); void onDataNotAvailable(); } void getTasks(@NonNull LoadTasksCallback callback); void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback); void saveTask(@NonNull Task task); void completeTask(@NonNull Task task); void completeTask(@NonNull String taskId); void activateTask(@NonNull Task task); void activateTask(@NonNull String taskId); void clearCompletedTasks(); void refreshTasks(); void deleteAllTasks(); void deleteTask(@NonNull String taskId); }
4、Presenter层
TasksPresenter实现TasksContract.Presenter接口,重写BasePresenter的start()方法。当收到View层的数据请求后,Presenter层控制Model层进行业务逻辑处理,Model层处理完毕后,把数据返回给Presnerer层,然后通知View层进行UI更新。
// TasksPresenter.java @Override public void start() { loadTasks(false); } private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) { if (showLoadingUI) { mTasksView.setLoadingIndicator(true); } if (forceUpdate) { mTasksRepository.refreshTasks(); } mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { List<Task> tasksToShow = new ArrayList<Task>(); // We filter the tasks based on the requestType for (Task task : tasks) { switch (mCurrentFiltering) { case ALL_TASKS: tasksToShow.add(task); break; case ACTIVE_TASKS: if (task.isActive()) { tasksToShow.add(task); } break; case COMPLETED_TASKS: if (task.isCompleted()) { tasksToShow.add(task); } break; default: tasksToShow.add(task); break; } } // The view may not be able to handle UI updates anymore if (!mTasksView.isActive()) { return; } if (showLoadingUI) { mTasksView.setLoadingIndicator(false); } processTasks(tasksToShow); } @Override public void onDataNotAvailable() { // The view may not be able to handle UI updates anymore if (!mTasksView.isActive()) { return; } mTasksView.showLoadingTasksError(); } }); }
5、View层
负责View视图和Presenter的创建,并将二者关联起来;View层持有Presenter后,通过它进行一系列的操作
// TasksActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); // ... // 创建Presenter对象,将当前的View传给Presenter层 mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); } // TasksPresenter.java // TasksPresenter的构造方法 public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); // View与Presenter建立关联 mTasksView.setPresenter(this); }
Fragment作为View层,View和Presenter通过Activity建立关联,Presenter对数据的调用通过TasksRepository完成,而TasksRepository维护着自己的数据源和实现。
1、本地数据库SQLite使用Room框架:
https://developer.android.goo...
2、Android中guava的核心类库com.google.guava:guava:28.1-android
https://github.com/google/guava
3、mockito:A mocking framework used to implement unit tests.
https://site.mockito.org/
4、Android Testing Support Library: A framework used to support UI tests, using both Espresso, and AndroidJUnitRunner.
https://developer.android.goo...
5、Common Android support libraries:Packages in the com.android.support.* namespace provide backwards compatibility and other features.
https://developer.android.goo...
https://github.com/kymjs/TheMVP
用MVP架构开发Android应用: https://kymjs.com/code/2015/1...
TheMVP使用Activity作为Presenter层来处理代码逻辑,通过让Activity包含一个ViewDelegate对象来间接操作View层对外提供方法,从而做到完全解耦视图层。
https://github.com/qibin0506/...
都是将Activity和Fragment作为Presenter。Presenter即我们的Activity或者Fragment,View呢?说白了就是我们从Activity和Fragment中提取出来的和View操作相关的代码
https://github.com/konmik/nuc...
https://github.com/Jude95/Beam