本文将解读有名的android-architecture中的mvp相关分支
mvp是一种设计方式 ,设计方式首先要适应需求。因此, 其app建议大家先试用一下。非常简单,总共四个模块 。 但是已经基本具备了实用app的功能,其核心数据是Task, 页面主要有列表展示、详情、增加、统计。
以下会简单带大家捋一捋其思路
1.mvp (最基础的部分)
基本架构,后面的几个分支都是依托在此分支上略加修改的。
结构并不复杂,是按照模块划分的,我们主要探讨的是其中的设计模式,因此现在只保留其中的一小部分:
├── BasePresenter.java
├── BaseView.java
├── data
│ ├── Task.java
│ └── source
│ ├── TasksDataSource.java
│ ├── TasksRepository.java
├── tasks
│ ├── TasksActivity.java
│ ├── TasksContract.java
│ ├── TasksFilterType.java
│ ├── TasksFragment.java
│ └── TasksPresenter.java
其主要对应关系是:
(TaskRepository 扮演的是Model的角色)
熟悉mvp的同学也可以顺便参考一下其实现细节。
[1]. Activity负责Presenter和View(Fragment)的创建,也保有Presenter的实例。
[2]. 分层清晰,三个模块中只有Presenter持有Model。TasksPresenter和TaskFragment互相持有,但是只依赖了其接口(依赖倒置原则:只依赖其抽象)。
[3]. model层也负责了网络请求等。
View 层的实现非常简单,View层的实现者是Fragment来做
用例1:
#TaskPresenter.java
@Override
public void loadTasks(boolean forceUpdate) {
// Simplification for sample: a network reload will be forced on first load.
loadTasks(forceUpdate || mFirstLoad, true);
mFirstLoad = false;
}
/**
* @param forceUpdate Pass in true to refresh the data in the {@link TasksDataSource}
* @param showLoadingUI Pass in true to display a loading icon in the UI
*/
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) {
...
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) {
return;
}
if (showLoadingUI) {
mTasksView.setLoadingIndicator(false);
}
processTasks(tasks);
}
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) {
return;
}
mTasksView.showLoadingTasksError();
}
});
}
用例2:
#TasksPresenter.java
@Override
public void completeTask(@NonNull Task completedTask) {
checkNotNull(completedTask, "completedTask cannot be null!");
mTasksRepository.completeTask(completedTask);
mTasksView.showTaskMarkedComplete();
//在之后需要重新载入一次数据
loadTasks(false, false);
}
2.mvp-clean
我们发现,在实际的操作过程中,Presenter作为主要的逻辑处理者,到最后可能会变得非常臃肿。
而mvp-clean中就采用细化拆分的方式为Presenter瘦身。
几个用例被拆分成不同的类,分担了之前的业务逻辑代码。
UserCase的使用是比较典型的命令模式。多数同学可能对这个不太熟悉。
关于命令模式跳转
重点关注一下UserCase这个类:
public abstract class UseCase<Q extends UseCase.RequestValues, P extends UseCase.ResponseValue> {
//请求参数被抽象
private Q mRequestValues;
// 返回结果也被抽象
private UseCaseCallback<P> mUseCaseCallback;
...
void run() {
executeUseCase(mRequestValues);
}
//excute方法是命令方式一个比较明显的特征
protected abstract void executeUseCase(Q requestValues);
/**
* Data passed to a request.
*/
public interface RequestValues {
}
/**
* Data received from a request.
*/
public interface ResponseValue {
}
public interface UseCaseCallback<R> {
void onSuccess(R response);
void onError();
}
}
这里比较新奇的是有两个空接口,RequestValue以及ResponseValue。
空接口在这里其实是语义上的声明。
另外有一点需要特别注意的是:
UseCase可能已经是业务上的最小划分了。在实际工程中遇到了两个页面需要不同的Presenter,而两个Presenter可能需要使用同一个用例。这个时候可以根据需要拼装已经写好的UseCase。
层级的划分,必然会减少重复代码。
(泡泡中遇到这种场景使用的是Helper,如对feed的点赞等。其实是一样的拆分原理。)
关于mvp-clean,其实最重要的是