Android 官方示例(android-architecture 学习笔记之todo-mvp)

落花踏尽游何处,笑入胡姬酒肆中。这篇文章主要讲述Android 官方示例:android-architecture 学习笔记之todo-mvp相关的知识,希望能为你提供帮助。
【Android 官方示例(android-architecture 学习笔记之todo-mvp)】项目地址: https://github.com/googlesamples/android-architecture/tree/todo-mvp/
在第一篇说过, todo-mvp只是android-architecture项目的一个分支
项目结构
todo-mvp主要使用了mvp架构来实现, 如图

Android 官方示例(android-architecture 学习笔记之todo-mvp)

文章图片

图中的Repository就是数据源, 即M, 包括Local数据和Remote数据; Fragment为V; Activity中依赖了P, V(Fragment)与P相互依赖, P依赖了M(Repository), 即P分离了M与V; 当然有时我们不需要Fragment, 那么可以直接使用Activity来作为V
项目的主要包结构及Base接口
(不含测试package):
Android 官方示例(android-architecture 学习笔记之todo-mvp)

文章图片

除BaseView、BasePresenter两个接口, 其他package都以业务功能来划分的:
addedittask —— 添加任务
data —— 数据源
statistics —— 任务统计
taskdetail —— 任务详情
tasks —— 任务列表
util —— 工具类
BaseView、BasePresenter两个接口:
public interface BaseView< T> { void setPresenter(T presenter); } public interface BasePresenter { void start(); }

数据源Model
先来看下data模块, 即M
Android 官方示例(android-architecture 学习笔记之todo-mvp)

文章图片

Task —— 它就是一个java bean
TasksDataSource —— Task数据操作的接口
TasksRepository —— 实现了TasksDataSource, 依赖了TasksLocalDataSource、TasksRemoteDataSource, 实现根据不同情形, 获取Local或Remote的相关数据
TasksRemoteDataSource —— 远程数据, 一般可能走网络, 当然这里没有, 只是模拟
TasksPersistenceContract —— 约定了数据库表字段
TasksDbHelper —— 数据库操作
TasksLocalDataSource —— 本地数据源
注: 项目中默认只操作了Local数据, 若也想操作Remote, 只需要将TasksRepository中的属性mCacheIsDirty= true, 即可
tasks模块分析
接下来挑一个业务模块分析一下(其它模块大同小异), 比如tasks
Android 官方示例(android-architecture 学习笔记之todo-mvp)

文章图片

ScrollChildSwipeRefreshLayout —— 这是一个自定义Layout, 不用理会
TasksActivity —— Activity
TasksContract —— 契约接口(每个功能模块都有一个), 约定了两个子接口View和Presenter, 及各自的公共方法; 分别实现BaseView、BasePresenter两个接口
TasksFilterType —— enum类, 任务过滤类型
TasksFragment —— Fragment, 实现TasksContract.View
TasksPresenter —— Presenter, 实现TasksContract.Presenter
再来分析一个具体的业务功能, 比如展示tasks列表
在TasksContract#Presenter中, 有一个方法:
void loadTasks(boolean forceUpdate);

相应的TasksContract#View中, 有一个方法:
void showTasks(List< Task> tasks);

看下TasksPresenter的相关的一些代码:
public class TasksPresenter implements TasksContract.Presenter {private final TasksRepository mTasksRepository; private final TasksContract.View mTasksView; private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS; private boolean mFirstLoad = true; public TasksPresenter(@ NonNull TasksRepository tasksRepository, @ NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, " tasksRepository cannot be null" ); mTasksView = checkNotNull(tasksView, " tasksView cannot be null!" ); mTasksView.setPresenter(this); }@ Override public void start() { loadTasks(false); }@ Override public void loadTasks(boolean forceUpdate) { // Simplification for sample: a network reload will be forced on first load. loadTasks(forceUpdate || mFirstLoad, true); mFirstLoad = false; }private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) { 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 noticemTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() { @ Override public void onTasksLoaded(List< Task> tasks) { 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) { 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(); } } }

从上, 看出TasksPresenter依赖了TasksRepository、TasksContract.View, 即P依赖了M和V; 当loadTasks(boolean forceUpdate, final boolean showLoadingUI)被调用后, 先从M中获取数据, 再调用processTasks(List tasks), 其内部调用mTasksView.showTasks(tasks)将数据显示在V上; 最后还要说的一点是在TasksPresenter的构造方法中, mTasksView.setPresenter(this) 将P传递给了V
再来看下V如何通过P, 来获取数据并显示
TasksFragment, 即V的主要代码:
public class TasksFragment extends Fragment implements TasksContract.View {private TasksContract.Presenter mPresenter; public TasksFragment() { // Requires empty public constructor }public static TasksFragment newInstance() { return new TasksFragment(); }@ Override public void onCreate(@ Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListAdapter = new TasksAdapter(new ArrayList< Task> (0), mItemListener); }@ Override public void onResume() { super.onResume(); mPresenter.start(); }@ Override public void setPresenter(@ NonNull TasksContract.Presenter presenter) { mPresenter = checkNotNull(presenter); }@ Nullable @ Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @ Override public void onRefresh() { mPresenter.loadTasks(false); } }); return root; }@ Override public void showTasks(List< Task> tasks) { mListAdapter.replaceData(tasks); mTasksView.setVisibility(View.VISIBLE); mNoTasksView.setVisibility(View.GONE); } }

看TasksFragment的onResume()中, 调用了mPresenter.start(), 而mPresenter.start()中, 就调用了TasksPresenter#loadTasks(false); TasksFragment的onCreateView()中还注册了一个监听回调, 即下拉刷新时, 也会调用TasksPresenter#loadTasks(false);
P和V的初始化在Activity中完成
TasksActivity的主要代码:
public class TasksActivity extends AppCompatActivity {private TasksPresenter mTasksPresenter; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); 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 mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); // Load previously saved state, if available. if (savedInstanceState != null) { TasksFilterType currentFiltering = (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); mTasksPresenter.setFiltering(currentFiltering); } } }

至此, 一条完整的MVP架构实现的业务链就分析完成了

    推荐阅读