从google的todo学习MVP
来源:互联网 发布:战龙三国坐骑数据 编辑:程序博客网 时间:2024/06/05 00:21
todo-mvp
点击阅读原文
背景知识
什么是todo?
todo就是简单的记事app
什么是MVP?
网上有很多概念,我也看了很多,一直只有一个模糊的印象。最近通过分析和模仿todo-mvp写了个demo有了完全不同的理解。
Model:提供数据,从本地或从云端
View:View即视图
Presenter:负责逻辑的处理,从Model中取数据,然后处理,传给View层,View将其展示
开始分析todo-MVP
项目结构
data包为整个app提供数据包括local和remote,local包下就是封装了对数据库的操作,这里我们重点关注的是TasksRepository,它封装了数据的获取,从本地或者云端。
tasks包和taskdetail以及addedittask打开后结构是一样的,都包含一个activity一个fragment一个contract一个presener,所以我们只以task包为例分析,既最上面的那张图。
先来看看各个不同的包和不同的接口是怎么配合的。
Repository为M,Presenter为P,View为V
在这之前我们先来说说接口BaseView和BasePresenter,BaseView中只有一个setPresenter(),BasePresenter只有一个star()
然后看看TasksContract`
public interface TasksContract { interface View extends BaseView<Presenter> { .... void showTasks(List<Task> tasks); void showLoadingTasksError(); void showNoTasks(); .... } interface Presenter extends BasePresenter { .... void loadTasks(boolean forceUpdate); void openTaskDetails(@NonNull Task requestedTask); void clearCompletedTasks(); .... }}
contract中有两个接口分别实现BaseView和BasePresenter,Presenter中在加入在Tasks中可能发生的操作,BaseView中加入View的响应操作。
View
接下来看看Fragment即View
public class TasksFragment extends Fragment implements TasksContract.View { .... private TasksContract.Presenter mPresenter; @Override public void onResume() { super.onResume(); mPresenter.start(); } @Override public void setPresenter(@NonNull TasksContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } @Override public void showTasks(List<Task> tasks) { mListAdapter.replaceData(tasks); mTasksView.setVisibility(View.VISIBLE); mNoTasksView.setVisibility(View.GONE); } @Override public void showNoTasks() { showNoTasksViews( getResources().getString(R.string.no_tasks_all), R.drawable.ic_assignment_turned_in_24dp, false ); } private void showNoTasksViews(String mainText, int iconRes, boolean showAddView) { mTasksView.setVisibility(View.GONE); mNoTasksView.setVisibility(View.VISIBLE); mNoTaskMainView.setText(mainText); mNoTaskIcon.setImageDrawable(getResources().getDrawable(iconRes)); mNoTaskAddView.setVisibility(showAddView ? View.VISIBLE : View.GONE); } ....}
注意在OnResume中调用了star(),一般就是从Repository中获取数据,View层根据接口返回的数据进行UI操作,同时根据监听事件调用Presenter相应的方法(presenter中有对View接口的引用,之后会回调)
Presenter
public class TasksPresenter implements TasksContract.Presenter {.... private final TasksRepository mTasksRepository; private final TasksContract.View mTasksView; 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 notice mTasksRepository.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(); } }); } @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); } ....}
在TasksPresenter中在实例化的时候持有了对Repository和TaskContract.View的引用,并且调用setPresenter()给TasksContract.View设置Presenter。当View层调用Presenter的方法时他进行相应的对Repository的操作,完成后根据情况回调TaskContract.View的相应方法。
Model
首先我们先想想需要对数据做什么操作,就todo而言,一般就是需要获取Tasks,删除Task等等,所以我们先抽象出一个接口集
public interface TasksDataSource { .... interface LoadTasksCallback { void onTasksLoaded(List<Task> tasks); void onDataNotAvailable(); } interface GetTaskCallback { void onTaskLoaded(Task task); void onDataNotAvailable(); } void getTasks(@NonNull LoadTasksCallback callback); void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback); void saveTask(@NonNull Task task); void deleteAllTasks(); void deleteTask(@NonNull String taskId); ....}
内部的两个接口是用来将数据回调到外部的,现在来看看repository的实现
public class TasksRepository implements TasksDataSource { .... private static TasksRepository INSTANCE = null; private final TasksDataSource mTasksRemoteDataSource; private final TasksDataSource mTasksLocalDataSource; private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource); } public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE; } @Override public void getTasks(@NonNull final LoadTasksCallback callback) { checkNotNull(callback); if (mCachedTasks != null && !mCacheIsDirty) { callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); return; } if (mCacheIsDirty) { getTasksFromRemoteDataSource(callback); } else { mTasksLocalDataSource.getTasks(new LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { refreshCache(tasks); callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); } @Override public void onDataNotAvailable() { getTasksFromRemoteDataSource(callback); } }); } } @Override public void saveTask(@NonNull Task task) { checkNotNull(task); mTasksRemoteDataSource.saveTask(task); mTasksLocalDataSource.saveTask(task); if (mCachedTasks == null) { mCachedTasks = new LinkedHashMap<>(); } mCachedTasks.put(task.getId(), task); } ....}
从以上可知Repository中,它的作用是给Presenter提供数据,而它来处理从缓存还是从本地数据库或者从网络请求的逻辑操作,它持有一个TasksLocalDataSource和TasksRemoteDataSource的引用,而真正对数据库和网络数据操作的的就是TasksLocalDataSource和TasksRemoteDataSource,他们同样实现了接口TasksDataSource,具体代码可以打开链接查看。
最后我们再来理一下各个类间的关系,TasksPresenter持有TasksContract.View和Repository的引用,实现了TasksContract.View的Fragment持有Presenter的引用,Repository持有TasksLocalDataSoure和TasksRemoteDataSource的引用。
最后我们需要一个Activity来把这所有东西调动以来
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); }}
现在整个MVP就完整了。第一次认真写博客,请多包涵。
- 从google的todo学习MVP
- Google 的android-architecture,todo-mvp解析
- Google官方MVP示例之TODO-MVP
- Google官方MVP示例之TODO-MVP
- Google TODO-MVP-Loaders 简要分析
- 学习TODO-MVP,出现的错误异常:transformClassesWithNewClassShrinkerForDebug error
- Google官方MVP翻译示例之TODO-MVP
- 个人学习_基于Google的MVP架构demo学习
- android-architecture学习——todo‑mvp
- TODO-MVP源码解析
- 理解 todo-mvp-clean
- 【android-architecture】todo-mvp
- Android todo-mvp
- Google MVP Smaple 学习文章
- 基于谷歌todo-mvp写的例子
- //TODO学习的那些事
- Android-Architecture之todo-mvp
- todo-mvp-clean简化版本
- ubuntu下创建桌面快捷方式
- 语义分割--Learning Object Interactions and Descriptions for Semantic Image Segmentation
- 单例设计模式
- RxJava 1.x 参数和参数的类型
- DB2 SQL Error: SQLCODE=-803, SQLSTATE=23505
- 从google的todo学习MVP
- linux下更改Python版本
- Eclipse快捷键
- 【电路第七章之II篇】一阶电路的零状态响应分析
- Linux中tty框架与uart框架之间的调用关系剖析
- Error: Your project contains C++ files but it is not using a supported native build system
- 在React中受控和非受控的表单输入并不需要太复杂
- 这可能是最好的RxJava 2.x 教程
- 嵌入式软件教程2.2