在看这篇博客之前请先看下Android 源码分析之TODO MVP 以及Android 进阶之设计模式 二 MVP模式。整个代码的流程在Android 源码分析之TODO MVP已经介绍过了,这篇博客将只介绍差异的部分。

还是老样子从TasksActivity开始分析:

public class TasksActivity extends AppCompatActivity {

private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY";

private DrawerLayout mDrawerLayout;

private TasksPresenter mTasksPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//....................
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}

// Create the presenter
TasksRepository repository = Injection.provideTasksRepository(getApplicationContext());
TasksLoader tasksLoader = new TasksLoader(getApplicationContext(), repository);

mTasksPresenter = new TasksPresenter(
tasksLoader,
getSupportLoaderManager(),
repository,
tasksFragment
);

//......................
}

//.........................

由于这个部分只是数据加载方式做了修改,所以View层TasksFragment没有多大的改变。 我们来看下数据层的代码:

public class TasksRepository implements TasksDataSource {

private static TasksRepository INSTANCE = null;

//远程数据源
private final TasksDataSource mTasksRemoteDataSource;
//本地数据源
private final TasksDataSource mTasksLocalDataSource;
//数据源监听器
private List<TasksRepositoryObserver> mObservers = new ArrayList<TasksRepositoryObserver>();

/**
* 内存缓存区
*/
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;

/**
* 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
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}

/**
* Used to force {@link #getInstance(TasksDataSource, TasksDataSource)} to create a new instance
* next time it's called.
*/
public static void destroyInstance() {
INSTANCE = null;
}

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

//注册数据源变化监听器
public void addContentObserver(TasksRepositoryObserver observer) {
if (!mObservers.contains(observer)) {
mObservers.add(observer);
}
}

//移除数据源变化监听器
public void removeContentObserver(TasksRepositoryObserver observer) {
if (mObservers.contains(observer)) {
mObservers.remove(observer);
}
}

//通知监听者数据源发生变化
private void notifyContentObserver() {
for (TasksRepositoryObserver observer : mObservers) {
observer.onTasksChanged();
}
}

/**
*
* Gets tasks from cache, local data source (SQLite) or remote data source, whichever is
* available first. This is done synchronously because it's used by the {@link TasksLoader},
* which implements the async mechanism.
*/
@Nullable
@Override
public List<Task> getTasks() {
List<Task> tasks = null;
if (!mCacheIsDirty) {
//表示缓存中数据还可以用
// Respond immediately with cache if available and not dirty
if (mCachedTasks != null) {
//从缓存中获取数据
tasks = getCachedTasks();
return tasks;
} else {
//这个是第一次的情况下,从数据库中加载数据
// Query the local storage if available.
tasks = mTasksLocalDataSource.getTasks();
}
}
// To simplify, we'll consider the local data source fresh when it has data.
//如果从本地数据库中加载完就会跑到这里,对结果进行判断,如果是空则表示,数据库中没有想要的数据,那么就从远程数据源中获取。
if (tasks == null || tasks.isEmpty()) {
// Grab remote data if cache is dirty or local data not available.
tasks = mTasksRemoteDataSource.getTasks();
// We copy the data to the device so we don't need to query the network next time
//从远程数据源中获取后将其缓存到数据库中,供下一次使用。
saveTasksInLocalDataSource(tasks);
}
//不论是从数据库中加载还是从远程数据源中加载数据都需要将数据缓存到内存中
processLoadedTasks(tasks);
return getCachedTasks();

}

//判断缓存数据是否可用
public boolean cachedTasksAvailable() {
return mCachedTasks != null && !mCacheIsDirty;
}

//获取缓存中的数据
public List<Task> getCachedTasks() {
return mCachedTasks == null ? null : new ArrayList<>(mCachedTasks.values());
}

//获取缓存中的指定数据
public Task getCachedTask(String taskId) {
return mCachedTasks.get(taskId);
}

//将数据保存到数据库中
private void saveTasksInLocalDataSource(List<Task> tasks) {
if (tasks != null) {
for (Task task : tasks) {
mTasksLocalDataSource.saveTask(task);
}
}
}

//将数据缓存到内存上
private void processLoadedTasks(List<Task> tasks) {
if (tasks == null) {
mCachedTasks = null;
mCacheIsDirty = false;
return;
}
if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
mCachedTasks.clear();
for (Task task : tasks) {
mCachedTasks.put(task.getId(), task);
}
mCacheIsDirty = false;
}

//保存任务
@Override
public void saveTask(@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 = new LinkedHashMap<>();
}
mCachedTasks.put(task.getId(), task);
// Update the UI
notifyContentObserver();
}

//添加完成的任务
@Override
public void completeTask(@NonNull Task task) {
checkNotNull(task);
mTasksRemoteDataSource.completeTask(task);
mTasksLocalDataSource.completeTask(task);
Task completedTask = new Task(task.getTitle(), task.getDescription(), task.getId(), true);

// Do in memory cache update to keep the app UI up to date
if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
mCachedTasks.put(task.getId(), completedTask);
// Update the UI
notifyContentObserver();
}

//添加完成的任务
@Override
public void completeTask(@NonNull String taskId) {
checkNotNull(taskId);
completeTask(getTaskWithId(taskId));
}

//添加激活的任务
@Override
public void activateTask(@NonNull Task task) {
checkNotNull(task);
mTasksRemoteDataSource.activateTask(task);
mTasksLocalDataSource.activateTask(task);

Task activeTask = new Task(task.getTitle(), task.getDescription(), task.getId());

// Do in memory cache update to keep the app UI up to date
if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
mCachedTasks.put(task.getId(), activeTask);

// Update the UI
notifyContentObserver();
}

//添加激活的任务
@Override
public void activateTask(@NonNull String taskId) {
checkNotNull(taskId);
activateTask(getTaskWithId(taskId));
}

//清除完成的任务
@Override
public void clearCompletedTasks() {
mTasksRemoteDataSource.clearCompletedTasks();
mTasksLocalDataSource.clearCompletedTasks();

// Do in memory cache update to keep the app UI up to date
if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
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();
}
}

// Update the UI
notifyContentObserver();
}

/**
* 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.
*/
@Override
public Task getTask(@NonNull final String taskId) {
checkNotNull(taskId);

Task cachedTask = getTaskWithId(taskId);

// Respond immediately with cache if we have one
if (cachedTask != null) {
return cachedTask;
}

// Is the task in the local data source? If not, query the network.
Task task = mTasksLocalDataSource.getTask(taskId);
if (task == null) {
task = mTasksRemoteDataSource.getTask(taskId);
}

return task;
}

@Nullable
private Task getTaskWithId(@NonNull String id) {
checkNotNull(id);
if (mCachedTasks == null || mCachedTasks.isEmpty()) {
return null;
} else {
return mCachedTasks.get(id);
}
}

@Override
public void refreshTasks() {
mCacheIsDirty = true;
notifyContentObserver();
}

@Override
public void deleteAllTasks() {
mTasksRemoteDataSource.deleteAllTasks();
mTasksLocalDataSource.deleteAllTasks();

if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
mCachedTasks.clear();

// Update the UI
notifyContentObserver();
}

@Override
public void deleteTask(@NonNull String taskId) {
mTasksRemoteDataSource.deleteTask(checkNotNull(taskId));
mTasksLocalDataSource.deleteTask(checkNotNull(taskId));

mCachedTasks.remove(taskId);

// Update the UI
notifyContentObserver();
}

public interface TasksRepositoryObserver {

void onTasksChanged();

}
}

从上面代码来看其实TasksRepository 主要的变化不大,主要是将原先的Callback回调变成了观察者模式。TasksLocalDataSource,以及TasksRemoteDataSource变化也不大。最大的变化应当属于TasksLoader
它是继承自AsyncTaskLoader以及实现了TasksRepositoryObserver。AsyncTaskLoader 这个之前都没介绍过,这个将会在后续的博客中补上。
一提到AsyncTaskLoader估计就会想到CursorLoader以及AysncTask,其实也差不多,它就是用于在后台中加载数据的。

public class TasksLoader extends AsyncTaskLoader<List<Task>>
implements TasksRepository.TasksRepositoryObserver{

private TasksRepository mRepository;

public TasksLoader(Context context, @NonNull TasksRepository repository) {
super(context);
checkNotNull(repository);
mRepository = repository;
}

//在后台加载数据
@Override
public List<Task> loadInBackground() {
return mRepository.getTasks();
}

@Override
public void deliverResult(List<Task> data) {

//如果后台加载线程没有开始,就直接返回
if (isReset()) {
return;
}

//将结果传递给UI线程
if (isStarted()) {
super.deliverResult(data);
}

}

@Override
protected void onStartLoading() {

//如果缓存中有任何可用的数据那么直接返回
// Deliver any previously loaded data immediately if available.
if (mRepository.cachedTasksAvailable()) {
deliverResult(mRepository.getCachedTasks());
}

//监听数据集的变化
// Begin monitoring the underlying data source.
mRepository.addContentObserver(this);

//如果内容有改变或者内存缓存内的任务不可用那么强制执行一次加载
if (takeContentChanged() || !mRepository.cachedTasksAvailable()) {
// When a change has been delivered or the repository cache isn't available, we force
// a load.
forceLoad();
}
}

@Override
protected void onStopLoading() {
cancelLoad();
}

@Override
protected void onReset() {
cancelLoad();
mRepository.removeContentObserver(this);
}

//如果数据集中发生变化启动一次加载
@Override
public void onTasksChanged() {
if (isStarted()) {
forceLoad();
}
}
}
public class TasksPresenter implements TasksContract.Presenter,
LoaderManager.LoaderCallbacks<List<Task>> {

private final static int TASKS_QUERY = 1;

private final TasksRepository mTasksRepository;

private final TasksContract.View mTasksView;

private final TasksLoader mLoader;

private final LoaderManager mLoaderManager;

private List<Task> mCurrentTasks;

private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS;

private boolean mFirstLoad;

public TasksPresenter(@NonNull TasksLoader loader, @NonNull LoaderManager loaderManager,
@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mLoader = checkNotNull(loader, "loader cannot be null!");
mLoaderManager = checkNotNull(loaderManager, "loader manager cannot be null");
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");
mTasksView.setPresenter(this);
}

@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) {
mTasksView.showSuccessfullySavedMessage();
}
}

//这里启动AsyncTaskLoader
@Override
public void start() {
mLoaderManager.initLoader(TASKS_QUERY, null, this);
}

@Override
public Loader<List<Task>> onCreateLoader(int id, Bundle args) {
mTasksView.setLoadingIndicator(true);
//返回从TaskActivity中传来的TasksLoader
return mLoader;
}

@Override
public void onLoadFinished(Loader<List<Task>> loader, List<Task> data) {
mTasksView.setLoadingIndicator(false);

mCurrentTasks = data;
if (mCurrentTasks == null) {
mTasksView.showLoadingTasksError();
} else {
showFilteredTasks();
}
}

private void showFilteredTasks() {
List<Task> tasksToDisplay = new ArrayList<>();
if (mCurrentTasks != null) {
for (Task task : mCurrentTasks) {
switch (mCurrentFiltering) {
case ALL_TASKS:
tasksToDisplay.add(task);
break;
case ACTIVE_TASKS:
if (task.isActive()) {
tasksToDisplay.add(task);
}
break;
case COMPLETED_TASKS:
if (task.isCompleted()) {
tasksToDisplay.add(task);
}
break;
default:
tasksToDisplay.add(task);
break;
}
}
}
processTasks(tasksToDisplay);
}

@Override
public void onLoaderReset(Loader<List<Task>> loader) {
// no-op
}

/**
* @param forceUpdate Pass in true to refresh the data in the {@link TasksDataSource}
*/
public void loadTasks(boolean forceUpdate) {
if (forceUpdate || mFirstLoad) {
mFirstLoad = false;
mTasksRepository.refreshTasks();
} else {
showFilteredTasks();
}
}

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;
}
}

@Override
public void addNewTask() {
mTasksView.showAddTask();
}

@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);
}

@Override
public void activateTask(@NonNull Task activeTask) {
checkNotNull(activeTask, "activeTask cannot be null!");
mTasksRepository.activateTask(activeTask);
mTasksView.showTaskMarkedActive();
loadTasks(false);
}

@Override
public void clearCompletedTasks() {
mTasksRepository.clearCompletedTasks();
mTasksView.showCompletedTasksCleared();
loadTasks(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;
}
}

我们返过头看下,整个代码和之前的差别不是很大,差别主要是增加了TasksLoader,将原先在主要线程加载数据改成使用Loader在后台加载数据,并且使用Observer来监听数据集合的变化,来代替回调的方式。

下面是整个代码的结构:

Contents