首先,不了解 Clean 架構的可以看看這個,不過也沒關系,閱讀本文后你也許會對Clean架構思想有一個認識。
對比MVP項目的結構圖,我們發現不同之處是新增的這個Domain Layer這層,來隔離Presentation Layer和Data Layer,負責了所有的業務邏輯交互,那么本文就主要來分析這層的設計和實現。
我們仍然以Sample中詳情界面(功能)來進行分析,下圖展示了在 TaskDetailFragment 中點擊刪除按鈕,然后所經歷的邏輯交互以及最后完成了界面更新。
1、在 TaskDetailFragment 中點擊菜單,調用 Presenter 的 deleteTask() 方法:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
mPresenter.deleteTask();
return true;
}
return false;
}
2、Presenter 的 deleteTask() 方法,是使用UseCaseHandler類中的 UseCaseScheduler 對象來執行任務,并處理結果和異常。
// TaskDetailPresenter 中deleteTask() 方法
@Override
public void deleteTask() {
mUseCaseHandler.execute(mDeleteTask, new DeleteTask.RequestValues(mTaskId),
new UseCase.UseCaseCallback<DeleteTask.ResponseValue>() {
@Override
public void onSuccess(DeleteTask.ResponseValue response) {
mTaskDetailView.showTaskDeleted();
}
@Override
public void onError(Error error) {
// Show error, log, etc.
}
});
}
// UseCaseHandler 中 execute() 方法
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.
}
}
});
}
3、UseCaseHandler 類中維護了 UseCaseThreadPoolScheduler 的示例對象,在其中定義了 ThreadPoolExecutor 來執行異步任務,任務的結果回調則使用 Handler post的方式來切換到主線程。
// UseCaseHandler 中初始化 UseCaseThreadPoolScheduler 對象
public static UseCaseHandler getInstance() {
if (INSTANCE == null) {
INSTANCE = new UseCaseHandler(new UseCaseThreadPoolScheduler());
}
return INSTANCE;
}
// UseCaseThreadPoolScheduler 中定義了 ThreadPoolExecutor 對象
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);
}
// response使用Handler來處理結果回調
@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);
}
});
}
4、UseCaseHandler 中其實還是調用 UseCase 的 run() 方法,而 UseCase 的 executeUseCase() 是抽象方法,最終邏輯是由各個Task子類實現的,比如 DeleteTask:
@Override
protected void executeUseCase(final RequestValues values) {
mTasksRepository.deleteTask(values.getTaskId());
getUseCaseCallback().onSuccess(new ResponseValue());
}
從以上的分析流程中可以看出,原本位于 Presenter 中的業務邏輯轉移到了 UseCaseHandler 中,UseCaseHandler 使用 UseCaseScheduler 來切換工作線程和UI線程,使用 UseCase 來處理每個 Task 的邏輯。其類圖如下:
** 總結 **
- 與MVP最大的不同是加入了 Domain layer 和 use cases,把原本位于 Presenter 中臃腫的邏輯代碼移到了 Domain layer 中,減輕了 Presenter 的體量,而 use cases 定義了每個業務的具體操作,細化了業務粒度,也有效提高了代碼的重用性。
- 使用 UseCaseScheduler 來提供線程池執行異步任務,并可以切換工作線程和UI線程。