前言
这篇文章将对android-architecture项目中的todo‑mvp‑clean设计模式进行分析。
由于todo‑mvp‑clean项目是在todo‑mvp项目的基础上扩展出来的,所以在读这篇文章之前最好有了解过todo‑mvp的代码,并且读过上一篇文章android-architecture源码分析(一)——todo‑mvp。
架构思路
todo-mvp-clean架构模式是基于标准MVP架构和Clean Architecture的概念相结合来设计实现的。
- Presentation Layer:这里可以直接理解标准MVP架构下的View层和Presenter层。
- Domain Layer:抽象出所有的业务逻辑,通过一个个Use Case来是实现Presenter层和Data层的通信。
- Data Layer:数据操作实现层,包括本地数据和远程数据,这里可以简单理解成标准MVP架构下的Modle层。
从上可以看出,较之标准MVP框架这里多出了一个Domain层,Domain层是Presenter层和Data层之间通信的桥梁,并且对其传递的数据进行业务处理。这一层是纯粹的java模块,没有任何的Android依赖性。
Use Case是对业务逻辑的抽象,将Data层的方法抽象到Domain层,从而进一步将Presenter层和Data层解耦。而且Use Case作为类来实现,更方便代码的复用和维护。
代码分析
我们先分析相关Domain层的几个类
/**
* Use cases are the entry points to the domain layer.
*
* @param <Q> the request type
* @param <P> the response type
*/
public abstract class UseCase<Q extends UseCase.RequestValues, P extends UseCase.ResponseValue> {
private Q mRequestValues;
private UseCaseCallback<P> mUseCaseCallback;
public void setRequestValues(Q requestValues) {
mRequestValues = requestValues;
}
public Q getRequestValues() {
return mRequestValues;
}
public UseCaseCallback<P> getUseCaseCallback() {
return mUseCaseCallback;
}
public void setUseCaseCallback(UseCaseCallback<P> useCaseCallback) {
mUseCaseCallback = useCaseCallback;
}
void run() {
executeUseCase(mRequestValues);
}
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();
}
}
UseCase类使用到了两个泛型RequestValues和ResponseValue,分别代表请求参数和返回数据,然后封装了UseCaseCallback的回调接口,包含执行成功和执行失败两个回调方法,run()方法则调用了executeUseCase的抽象方法,executeUseCase表示逻辑执行方法,由具体的业务逻辑子类实现。
/**
* Interface for schedulers, see {@link UseCaseThreadPoolScheduler}.
*/
public interface UseCaseScheduler {
void execute(Runnable runnable);
<V extends UseCase.ResponseValue> void notifyResponse(final V response,
final UseCase.UseCaseCallback<V> useCaseCallback);
<V extends UseCase.ResponseValue> void onError(
final UseCase.UseCaseCallback<V> useCaseCallback);
}
UseCaseScheduler接口,execute表示参数线程执行,notifyResponse表示通知回调成功,onError表示通知回调失败。
/**
* Executes asynchronous tasks using a {@link ThreadPoolExecutor}.
* <p>
* See also {@link Executors} for a list of factory methods to create common
* {@link java.util.concurrent.ExecutorService}s for different scenarios.
*/
public class UseCaseThreadPoolScheduler implements UseCaseScheduler {
private final Handler mHandler = new Handler();
public static final int POOL_SIZE = 2;
public static final int MAX_POOL_SIZE = 4;
public static final int TIMEOUT = 30;
ThreadPoolExecutor mThreadPoolExecutor;
public UseCaseThreadPoolScheduler() {
mThreadPoolExecutor = new ThreadPoolExecutor(POOL_SIZE, MAX_POOL_SIZE, TIMEOUT,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(POOL_SIZE));
}
@Override
public void execute(Runnable runnable) {
mThreadPoolExecutor.execute(runnable);
}
@Override
public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
final UseCase.UseCaseCallback<V> useCaseCallback) {
mHandler.post(new Runnable() {
@Override
public void run() {
useCaseCallback.onSuccess(response);
}
});
}
@Override
public <V extends UseCase.ResponseValue> void onError(
final UseCase.UseCaseCallback<V> useCaseCallback) {
mHandler.post(new Runnable() {
@Override
public void run() {
useCaseCallback.onError();
}
});
}
}
UseCaseThreadPoolScheduler实现了UseCaseScheduler接口,内部创建了一个线程池和handler,execute方法的线程参数由线程池执行,而notifyResponse和onError方法则由handler在主线程中执行回调方法。
/**
* Runs {@link UseCase}s using a {@link UseCaseScheduler}.
*/
public class UseCaseHandler {
...
public <T extends UseCase.RequestValues, R extends UseCase.ResponseValue> void execute(
final UseCase<T, R> useCase, T values, UseCase.UseCaseCallback<R> callback) {
useCase.setRequestValues(values);
useCase.setUseCaseCallback(new UiCallbackWrapper(callback, this));
// 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
mUseCaseScheduler.execute(new Runnable() {
@Override
public void run() {
useCase.run();
// 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.
}
}
});
}
...
}
UseCaseHandler类可以理解为UseCase的调度器,它采用单例模式,并在构造函数中创建了UseCaseThreadPoolScheduler线程池对象,从而当某个业务逻辑UseCase需要执行的时候,会线程池中创建一个新的线程去执行该UseCase的逻辑代码。
以上代码分析可以看出,UseCase的执行操作会由线程池创建一个新的线程来实行,而UseCase的执行结果的回调方法,则由handler在主线程中执行。
分析完Domain层的几个类以后,大概流程已经逐步清晰,下面我们来分析具体业务逻辑代码。为了方便与todo‑mvp做比较,还是使用addedittask模块来展示代码。
上图可以看到,addedittask包下面多了domain.usecase包,下面的3个类DeleteTask、GetTask、SaveTask分别封装了删除、获取和保存3个业务逻辑代码。以GetTask为例进行分析:
/**
* Retrieves a {@link Task} from the {@link TasksRepository}.
*/
public class GetTask extends UseCase<GetTask.RequestValues, GetTask.ResponseValue> {
private final TasksRepository mTasksRepository;
public GetTask(@NonNull TasksRepository tasksRepository) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
}
@Override
protected void executeUseCase(final RequestValues values) {
mTasksRepository.getTask(values.getTaskId(), new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
if (task != null) {
ResponseValue responseValue = new ResponseValue(task);
getUseCaseCallback().onSuccess(responseValue);
} else {
getUseCaseCallback().onError();
}
}
@Override
public void onDataNotAvailable() {
getUseCaseCallback().onError();
}
});
}
public static final class RequestValues implements UseCase.RequestValues {
private final String mTaskId;
public RequestValues(@NonNull String taskId) {
mTaskId = checkNotNull(taskId, "taskId cannot be null!");
}
public String getTaskId() {
return mTaskId;
}
}
public static final class ResponseValue implements UseCase.ResponseValue {
private Task mTask;
public ResponseValue(@NonNull Task task) {
mTask = checkNotNull(task, "task cannot be null!");
}
public Task getTask() {
return mTask;
}
}
}
GetTask继承了UseCase类重写了executeUseCase方法,在executeUseCase方法中直接使用TasksRepository对象调用getTask方法来获取数据,这也证明了UseCase是对业务逻辑的抽象这一说法。而RequestValues和ResponseValue分别实现了UseCase.RequestValues和UseCase.ResponseValue接口,分别作为请求参数和返回参数。
/**
* Listens to user actions from the UI ({@link AddEditTaskFragment}), retrieves the data and
* updates
* the UI as required.
*/
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter {
private final AddEditTaskContract.View mAddTaskView;
private final GetTask mGetTask;
private final SaveTask mSaveTask;
private final UseCaseHandler mUseCaseHandler;
@Nullable
private String mTaskId;
/**
* Creates a presenter for the add/edit view.
*
* @param taskId ID of the task to edit or null for a new task
* @param addTaskView the add/edit view
*/
public AddEditTaskPresenter(@NonNull UseCaseHandler useCaseHandler, @Nullable String taskId,
@NonNull AddEditTaskContract.View addTaskView, @NonNull GetTask getTask,
@NonNull SaveTask saveTask) {
mUseCaseHandler = checkNotNull(useCaseHandler, "useCaseHandler cannot be null!");
mTaskId = taskId;
mAddTaskView = checkNotNull(addTaskView, "addTaskView cannot be null!");
mGetTask = checkNotNull(getTask, "getTask cannot be null!");
mSaveTask = checkNotNull(saveTask, "saveTask cannot be null!");
mAddTaskView.setPresenter(this);
}
@Override
public void start() {
if (mTaskId != null) {
populateTask();
}
}
...
@Override
public void populateTask() {
if (mTaskId == null) {
throw new RuntimeException("populateTask() was called but task is new.");
}
mUseCaseHandler.execute(mGetTask, new GetTask.RequestValues(mTaskId),
new UseCase.UseCaseCallback<GetTask.ResponseValue>() {
@Override
public void onSuccess(GetTask.ResponseValue response) {
showTask(response.getTask());
}
@Override
public void onError() {
showEmptyTaskError();
}
});
}
...
}
AddEditTaskPresenter类中的构造函数,直接获取了UseCaseHandler实例、taskId参数、View实例、GetTask实例、SaveTask实例,mUseCaseHandler直接执行execute方法,参数分别是GetTask实例、RequestValues请求参数对象、UseCaseCallback回调接口。
至此,一套完整的调用逻辑流程已经清晰:
- Presenter实例调用逻辑方法。
- UseCaseHandler对象启动子线程执行UseCase实例。
- UseCase实例执行executeUseCase方法,调用Data层实例对象执行数据操作。
- Data层实例对象完成数据操作以后执行UseCase实例中UseCaseCallback回调接口方法。
- 在主线程中执行UseCaseCallback的回调方法,渲染View层显示数据。
总结
Clean Architecture最大特点是引入了Domain层,使得Presenter层和Model层进一步解耦;而UseCase则增强了业务逻辑代码的复用性和可读性,从而维护起来更为简单方便。