落花踏尽游何处,笑入胡姬酒肆中。这篇文章主要讲述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架构来实现,
如图
文章图片
图中的Repository就是数据源, 即M, 包括Local数据和Remote数据; Fragment为V; Activity中依赖了P, V(Fragment)与P相互依赖, P依赖了M(Repository), 即P分离了M与V; 当然有时我们不需要Fragment, 那么可以直接使用Activity来作为V
项目的主要包结构及Base接口
(不含测试package):
文章图片
除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
文章图片
Task —— 它就是一个java bean
TasksDataSource —— Task数据操作的接口
TasksRepository —— 实现了TasksDataSource, 依赖了TasksLocalDataSource、TasksRemoteDataSource, 实现根据不同情形, 获取Local或Remote的相关数据
TasksRemoteDataSource —— 远程数据, 一般可能走网络, 当然这里没有, 只是模拟
TasksPersistenceContract —— 约定了数据库表字段
TasksDbHelper —— 数据库操作
TasksLocalDataSource —— 本地数据源
注: 项目中默认只操作了Local数据, 若也想操作Remote, 只需要将TasksRepository中的属性mCacheIsDirty= true, 即可tasks模块分析
接下来挑一个业务模块分析一下(其它模块大同小异), 比如tasks
文章图片
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架构实现的业务链就分析完成了
推荐阅读
- Android深入源码分析理解Aidl整体调用流程(雷惊风)
- Android之Activity系列总结--任务和返回栈
- 解决sdk更新时候报错 http://dl-ssl.google.com/android上不去,链接拒绝
- 电脑没有音频设备怎样办,图文详细说明电脑没有音频设备怎样处理
- 谷歌浏览器怎样设置首页,图文详细说明谷歌浏览器怎样设置首页
- 无法加载插件,图文详细说明怎样处理浏览器显示无法加载插件
- 电脑分辨率低,图文详细说明电脑分辨率低怎样办
- excel词典xllex.dll文件失去或损坏,图文详细说明excel词典xllex.dll文件失去或损坏怎样处理
- 出错代码0xc004e003,图文详细说明怎样处理出错代码0xc004e003