https://github.com/googlesamples/android-architecture/tree/todo-mvp-clean/
在上一篇关于MVP结构的学习中,我们明显感觉到了P层的臃肿,它直接连接V和M层的作用导致它需要处理太多的业务逻辑,于是进一步对它进行解耦也就成了必然。这时,MVP+Clean的模式也就孕育而生了。其层级关系如下所示:
我们还是先看下项目的整体结构
可以看到相比MVP结构多了一层domain,业务逻辑放入这里进一步解耦。同时多了一些UseCase(这些类大量使用了Java的泛型)。我们还是分析addedittask这个模块。查看相关类绘制类图如下:
UserCase是一个抽象的泛型集合,内部持有一个泛型对象Q、一个泛型集合UseCaseCallback<P>及它们的set、get方法,同时定义了三个接口RequestValues、ResponseValue、UseCaseCallback<R>。
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();
}
}
我们知道抽象类是不能实例化的,它又被谁继承了呢?SaveTask、GetTask、DeleteTask(泛型的好处)。
public class SaveTask extends UseCase<SaveTask.RequestValues, SaveTask.ResponseValue> {
//持有一个对接数据层的对象
private final TasksRepository mTasksRepository;
public SaveTask(@NonNull TasksRepository tasksRepository) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
}
@Override
protected void executeUseCase(final RequestValues values) {
Task task = values.getTask();
mTasksRepository.saveTask(task);
getUseCaseCallback().onSuccess(new ResponseValue(task));
}
public static final class RequestValues implements UseCase.RequestValues {
private final Task mTask;
public RequestValues(@NonNull Task task) {
mTask = checkNotNull(task, "task cannot be null!");
}
public Task getTask() {
return mTask;
}
}
public static final class ResponseValue implements UseCase.ResponseValue {
private final Task mTask;
public ResponseValue(@NonNull Task task) {
mTask = checkNotNull(task, "task cannot be null!");
}
public Task getTask() {
return mTask;
}
}
}
同时它们也都定义了两个静态内部类分别继承其父类定义的两个接口:RequestValues、ResponseValue。
然后我们看下UseCaseScheduler这个接口:
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);
}
又是两个使用泛型参数的方法。我们看这个接口的命名,猜测应该会用到线程池了。果然,它的实现类创建了线程池:
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();
}
});
}
与原始的MVP结构相比,线程的灵活切换对性能是一个很好的改进。
如何使用这个线程池来切换UI线程与子线程呢,来看最后一个类UseCaseHandler
public class UseCaseHandler {
private static UseCaseHandler INSTANCE;
private final UseCaseScheduler mUseCaseScheduler;
public UseCaseHandler(UseCaseScheduler useCaseScheduler) {
mUseCaseScheduler = useCaseScheduler;
}
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.
}
}
});
}
public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
final UseCase.UseCaseCallback<V> useCaseCallback) {
mUseCaseScheduler.notifyResponse(response, useCaseCallback);
}
private <V extends UseCase.ResponseValue> void notifyError(
final UseCase.UseCaseCallback<V> useCaseCallback) {
mUseCaseScheduler.onError(useCaseCallback);
}
private static final class UiCallbackWrapper<V extends UseCase.ResponseValue> implements
UseCase.UseCaseCallback<V> {
private final UseCase.UseCaseCallback<V> mCallback;
private final UseCaseHandler mUseCaseHandler;
public UiCallbackWrapper(UseCase.UseCaseCallback<V> callback,
UseCaseHandler useCaseHandler) {
mCallback = callback;
mUseCaseHandler = useCaseHandler;
}
@Override
public void onSuccess(V response) {
mUseCaseHandler.notifyResponse(response, mCallback);
}
@Override
public void onError() {
mUseCaseHandler.notifyError(mCallback);
}
}
public static UseCaseHandler getInstance() {
if (INSTANCE == null) {
INSTANCE = new UseCaseHandler(new UseCaseThreadPoolScheduler());
}
return INSTANCE;
}
它的静态内部类UiCallbackWrapper实现了UseCase中定义的最后一个接口UseCaSeCallback<R>,同时内部持有一个final UseCaseHandler对象。
整体是如何工作的呢,我们来画一遍时序图:
由于泛型的大量使用(还是平时基本不使用导致太不熟练),线程切换的函数调用过程看起来有点费劲,需要多花点时间消化一下。