todo-mvp/ - Basic Model-View-Presenter architecture. todo-mvp-loaders/ - Based on todo-mvp, fetches data using Loaders. todo-mvp-databinding/ - Based on todo-mvp, uses the Data Binding Library. todo-mvp-clean/ - Based on todo-mvp, uses concepts from Clean Architecture. todo-mvp-dagger/ - Based on todo-mvp, uses Dagger2 for Dependency Injection todo-mvp-contentproviders/ - Based on todo-mvp-loaders, fetches data using Loaders anduses Content Providers dev-todo-mvp-rxjava/ - Based on todo-mvp, uses RxJava for concurrency and data layer abstraction. todo-mvp-fragmentless/ - Based on todo-mvp, uses Android views instead of Fragments.
privatestaticTasksRepositoryINSTANCE = null; //模拟网络层数据来源 private final TasksDataSource mTasksRemoteDataSource; //模拟本地数据来源 private final TasksDataSource mTasksLocalDataSource; /** * 内存缓存 */ Map<String, Task> mCachedTasks;
/** * 用于表示当前缓存数据是否有效 * Marks the cache as invalid, to force an update the next time data is requested. This variable * has package local visibility so it can be accessed from tests. */ boolean mCacheIsDirty = false;
/** * Returns the single instance of this class, creating it if necessary. * * @param tasksRemoteDataSource the backend data source * @param tasksLocalDataSource the device storage data source * @return the {@link TasksRepository} instance */ // 使用一个单例创建TasksRepository publicstaticTasksRepositorygetInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = newTasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } returnINSTANCE; }
/** * Used to force {@link #getInstance(TasksDataSource, TasksDataSource)} to create a new instance * next time it's called. */ publicstaticvoiddestroyInstance() { INSTANCE = null; }
/** * 从缓存,本地数据源(SQLite)或者远程数据源中获取 Task * Gets tasks from cache, local data source (SQLite) or remote data source, whichever is * available first. * <p> * Note: {@link LoadTasksCallback#onDataNotAvailable()} is fired if all data sources fail to * get the data. */ @Override publicvoidgetTasks(@NonNull final LoadTasksCallback callback) {
checkNotNull(callback);
// Respond immediately with cache if available and not dirty // 如果内存中有内容并且缓存中的数据是可用的那么直接将缓存中的内容传递出去 if (mCachedTasks != null && !mCacheIsDirty) { callback.onTasksLoaded(newArrayList<>(mCachedTasks.values())); return; }
if (mCacheIsDirty) { ///如果缓存中的数据不可用那么需要从网络中获取新的数据 // If the cache is dirty we need to fetch new data from the network. getTasksFromRemoteDataSource(callback); } else { // Query the local storage if available. If not, query the network. //这个一般发生在第一次,也就是这时候mCachedTasks = null 并且mCacheIsDirty = false mTasksLocalDataSource.getTasks(newLoadTasksCallback() { @Override publicvoidonTasksLoaded(List<Task> tasks) { //从数据库中加载数据到缓存 refreshCache(tasks); callback.onTasksLoaded(newArrayList<>(mCachedTasks.values())); } @Override publicvoidonDataNotAvailable() { //如果数据库中没有数据那么就从缓存中获取 getTasksFromRemoteDataSource(callback); } }); } }
/** * 要保存的时候将数据存储到远程数据库,本地数据库,以及缓存中 */ @Override publicvoidsaveTask(@NonNull Task task) { checkNotNull(task); //保存到远程数据源中 mTasksRemoteDataSource.saveTask(task); //保存到本地数据库中 mTasksLocalDataSource.saveTask(task); // Do in memory cache update to keep the app UI up to date //保存到缓存中 if (mCachedTasks == null) { mCachedTasks = newLinkedHashMap<>(); } mCachedTasks.put(task.getId(), task); }
checkNotNull(task); mTasksRemoteDataSource.completeTask(task); mTasksLocalDataSource.completeTask(task); Task completedTask = newTask(task.getTitle(), task.getDescription(), task.getId(), true); // Do in memory cache update to keep the app UI up to date if (mCachedTasks == null) { mCachedTasks = newLinkedHashMap<>(); } mCachedTasks.put(task.getId(), completedTask); }
checkNotNull(task); mTasksRemoteDataSource.activateTask(task); mTasksLocalDataSource.activateTask(task); Task activeTask = newTask(task.getTitle(), task.getDescription(), task.getId()); // Do in memory cache update to keep the app UI up to date if (mCachedTasks == null) { mCachedTasks = newLinkedHashMap<>(); } mCachedTasks.put(task.getId(), activeTask); }
// Do in memory cache update to keep the app UI up to date if (mCachedTasks == null) { mCachedTasks = newLinkedHashMap<>(); } Iterator<Map.Entry<String, Task>> it = mCachedTasks.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, Task> entry = it.next(); if (entry.getValue().isCompleted()) { it.remove(); } } }
/** * Gets tasks from local data source (sqlite) unless the table is new or empty. In that case it * uses the network data source. This is done to simplify the sample. * <p> * Note: {@link LoadTasksCallback#onDataNotAvailable()} is fired if both data sources fail to * get the data. */
/** * 依次从缓存,本地,远程中查找某个任务 * @paramtaskId * @paramcallback */ @Override publicvoidgetTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
checkNotNull(taskId); checkNotNull(callback);
//从缓存中获取指定任务id的任务 Task cachedTask = getTaskWithId(taskId); // Respond immediately with cache if available if (cachedTask != null) { callback.onTaskLoaded(cachedTask); return; }
//从本地数据源以及远程数据源获取指定id的任务数据 // Load from server/persisted if needed. // Is the task in the local data source? If not, query the network. mTasksLocalDataSource.getTask(taskId, newGetTaskCallback() { @Override publicvoidonTaskLoaded(Task task) { callback.onTaskLoaded(task); }
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { //数据层 mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); //视图层 mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); //将Presenter赋值给View层 mTasksView.setPresenter(this); }
//加载任务数据 @Override public void start() { loadTasks(false); }
//处理返回值 @Override public void result(int requestCode, int resultCode) { // If a task was successfully added, show snackbar if (AddEditTaskActivity.REQUEST_ADD_TASK == requestCode && Activity.RESULT_OK == resultCode) { //显示SnakeBar mTasksView.showSuccessfullySavedMessage(); } }
//加载数据 @Override public void loadTasks(booleanforceUpdate) { // Simplification for sample: a network reload will be forced on first load. loadTasks(forceUpdate||mFirstLoad, true); mFirstLoad = false; }
/** * @param forceUpdate Pass in true to refresh the data in the {@link TasksDataSource} * @param showLoadingUI Pass in true to display a loading icon in the UI */ //加载数据 private void loadTasks(booleanforceUpdate, finalbooleanshowLoadingUI) {
if (showLoadingUI) { mTasksView.setLoadingIndicator(true); }
if (forceUpdate) { mTasksRepository.refreshTasks(); }
// The network request might be handled in a different thread so make sure Espresso knows // that the app is busy until the response is handled. EspressoIdlingResource.increment(); // App is busy until further notice
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) {
//创建需要显示的Task List<Task> tasksToShow = new ArrayList<Task>();
// This callback may be called twice, once for the cache and once for loading // the data from the server API, so we check before decrementing, otherwise // it throws "Counter has been corrupted!" exception. if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) { EspressoIdlingResource.decrement(); // Set app as idle. }
// We filter the tasks based on the requestType for (Task task : tasks) { //根据mCurrentFiltering 显示那种类型的任务 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(); } }); }
private void processTasks(List<Task> tasks) { if (tasks.isEmpty()) { // Show a message indicating there are no tasks for that filter type. //根据类型显示当前需要显示的空加载信息 processEmptyTasks(); } else { // Show the list of tasks //显示任务 mTasksView.showTasks(tasks); // Set the filter label's text. showFilterLabel(); } }
private void showFilterLabel() { switch (mCurrentFiltering) { case ACTIVE_TASKS: mTasksView.showActiveFilterLabel(); break; case COMPLETED_TASKS: mTasksView.showCompletedFilterLabel(); break; default: mTasksView.showAllFilterLabel(); break; } }
private void processEmptyTasks() { switch (mCurrentFiltering) { case ACTIVE_TASKS: mTasksView.showNoActiveTasks(); break; case COMPLETED_TASKS: mTasksView.showNoCompletedTasks(); break; default: mTasksView.showNoTasks(); break; } }
//AddEditTaskActivity @Override public void addNewTask() { mTasksView.showAddTask(); }
//TaskDetailActivity @Override public void openTaskDetails(@NonNull Task requestedTask) { checkNotNull(requestedTask, "requestedTask cannot be null!"); mTasksView.showTaskDetailsUi(requestedTask.getId()); }
@Override public void completeTask(@NonNull Task completedTask) { checkNotNull(completedTask, "completedTask cannot be null!"); mTasksRepository.completeTask(completedTask); mTasksView.showTaskMarkedComplete(); loadTasks(false, false); }
@Override public void activateTask(@NonNull Task activeTask) { checkNotNull(activeTask, "activeTask cannot be null!"); mTasksRepository.activateTask(activeTask); mTasksView.showTaskMarkedActive(); loadTasks(false, false); }
@Override public void clearCompletedTasks() { mTasksRepository.clearCompletedTasks(); mTasksView.showCompletedTasksCleared(); loadTasks(false, false); }
/** * Sets the current task filtering type. * * @param requestType Can be {@link TasksFilterType#ALL_TASKS}, * {@link TasksFilterType#COMPLETED_TASKS}, or * {@link TasksFilterType#ACTIVE_TASKS} */ @Override public void setFiltering(TasksFilterType requestType) { mCurrentFiltering = requestType; }
@Override public TasksFilterType getFiltering() { return mCurrentFiltering; }