项目地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp-contentproviders/

项目结构

本项目基于todo-mvp-loaders,并使用Content Provider来检索数据。架构图:
这里写图片描述

源码分析

这次从taskdetail模块的入口开始分析
TaskDetailActivity —— 初始化了TaskDetailFragment、LoaderProvider、TaskDetailPresenter。
LoaderProvider 代码:

public class LoaderProvider {

    @NonNull
    private final Context mContext;

    public LoaderProvider(@NonNull Context context) {
        mContext = checkNotNull(context, "context cannot be null");
    }

    public Loader<Cursor> createFilteredTasksLoader(TaskFilter taskFilter) {
        String selection = null;
        String[] selectionArgs = null;

        switch (taskFilter.getTasksFilterType()) {
            case ALL_TASKS:
                selection = null;
                selectionArgs = null;
                break;
            case ACTIVE_TASKS:
                selection = TasksPersistenceContract.TaskEntry.COLUMN_NAME_COMPLETED + " = ? ";
                selectionArgs = new String[]{String.valueOf(0)};
                break;
            case COMPLETED_TASKS:
                selection = TasksPersistenceContract.TaskEntry.COLUMN_NAME_COMPLETED + " = ? ";
                selectionArgs = new String[]{String.valueOf(1)};
                break;
        }

        return new CursorLoader(
                mContext,
                TasksPersistenceContract.TaskEntry.buildTasksUri(),
                TasksPersistenceContract.TaskEntry.TASKS_COLUMNS, selection, selectionArgs, null
        );
    }

    public Loader<Cursor> createTaskLoader(String taskId) {
        return new CursorLoader(mContext, TasksPersistenceContract.TaskEntry.buildTasksUriWith(taskId),
                                null,
                                null,
                                new String[]{String.valueOf(taskId)}, null
        );
    }
}

主要是提供了两种创建CursorLoader的方法。
TaskDetailFragment —— 实现TaskDetailContract.View接口,组合使用了TaskDetailContract.Presenter
TaskDetailPresenter 代码:

public class TaskDetailPresenter implements TaskDetailContract.Presenter, LoaderManager.LoaderCallbacks<Cursor>,TasksDataSource.GetTaskCallback {

    public final static int TASK_LOADER = 2;

    @NonNull
    private final TasksRepository mTasksRepository;
    private TaskDetailContract.View mTaskDetailView;
    private LoaderProvider mLoaderProvider;
    private LoaderManager mLoaderManager;
    private Task mTask;
    private String mTaskId;

    public TaskDetailPresenter(@NonNull String taskId,
                               @NonNull LoaderProvider loaderProvider,
                               @NonNull LoaderManager loaderManager,
                               @NonNull TasksRepository tasksRepository,
                               @NonNull TaskDetailContract.View taskDetailView) {
        mTaskId = checkNotNull(taskId, "taskId cannot be null!");
        mLoaderProvider = checkNotNull(loaderProvider, "loaderProvider cannot be null!");
        mLoaderManager = checkNotNull(loaderManager, "loaderManager cannot be null!");
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
        mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
        mTaskDetailView.setPresenter(this);
    }

    @Override
    public void start() {
        loadTask();
    }

    private void loadTask() {
        mTaskDetailView.setLoadingIndicator(true);
        mTasksRepository.getTask(mTaskId, this);
    }

    @Override
    public void editTask() {
        if (null == mTask) {
            mTaskDetailView.showMissingTask();
            return;
        }
        mTaskDetailView.showEditTask(mTask.getId());
    }

    @Override
    public void deleteTask() {
        if (Strings.isNullOrEmpty(mTaskId)) {
            mTaskDetailView.showMissingTask();
            return;
        }
        mTasksRepository.deleteTask(mTaskId);
        mTaskDetailView.showTaskDeleted();
    }

    public void completeTask() {
        if (Strings.isNullOrEmpty(mTaskId)) {
            mTaskDetailView.showMissingTask();
            return;
        }
        mTasksRepository.completeTask(mTask);
        mTaskDetailView.showTaskMarkedComplete();
    }

    @Override
    public void activateTask() {
        if (Strings.isNullOrEmpty(mTaskId)) {
            mTaskDetailView.showMissingTask();
            return;
        }
        mTasksRepository.activateTask(mTask);
        mTaskDetailView.showTaskMarkedActive();
    }

    private void showTask(Cursor data) {
        mTask = Task.from(data);

        String title = mTask.getTitle();
        String description = mTask.getDescription();

        if (Strings.isNullOrEmpty(title)) {
            mTaskDetailView.hideTitle();
        } else {
            mTaskDetailView.showTitle(title);
        }

        if (Strings.isNullOrEmpty(description)) {
            mTaskDetailView.hideDescription();
        } else {
            mTaskDetailView.showDescription(description);
        }
        mTaskDetailView.showCompletionStatus(mTask.isCompleted());
        mTaskDetailView.setLoadingIndicator(false);
    }

    public void onDataLoaded(Cursor data) {
        showTask(data);
    }

    public void onDataEmpty() {
        mTaskDetailView.showMissingTask();
    }

    @Override
    public void onTaskLoaded(Task task) {
        // the data is refreshed locally now but
        // we don't need this result since the CursorLoader will load it for us
        mLoaderManager.initLoader(TASK_LOADER, null, this);
    }

    public void onDataNotAvailable() {
        mTaskDetailView.showMissingTask();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return mLoaderProvider.createTaskLoader(mTaskId);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (data != null) {
            if (data.moveToLast()) {
                onDataLoaded(data);
            } else {
                onDataEmpty();
            }
        } else {
            onDataNotAvailable();
        }
    }

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

结合之前MVP和Loaders项目来分析。start(),会被fragment.onResume()调用,它是一个默认的执行流程入口。srtart()->loadTask()->mTasksRepository.getTask(mTaskId, this),至此就会从仓库获取数据Task。this指代的是GetTaskCallback,对应的回调方法为onTaskLoaded()。onTaskLoaded()中初始化Loader,会再继续执行LoaderCallbacks的方法:onCreateLoader()、onLoadFinished()。onLoadFinished()中,获取到数据后,会调用onDataLoaded()。最终将数据显示TaskDetailView(V)上。

再分析下TasksRepository 是如何操作数据的。这里就直接说了,它是直接通过TasksLocalDataSource来操作数据的。TasksLocalDataSource中使用了ContentResolver。ContentResolver操作的ContentProvider就是TasksProvider。

注:ContentProvider是可以为当前或其他App提供共享数据的。通过一套Uri匹配规则,由ContentResolver发起调用。ContentProvider的底层数据存储不局限于DB方式。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐