Google-todo-clean读书笔记

本文深入解析了Clean架构原理及其在Android应用中的实现方式。通过对比MVP架构,详细介绍了Clean架构的分层概念,并以Todo-clean项目为例,展示了如何构建一个遵循Clean架构的应用。

Google-todo-clean读书笔记

题外:从现在开始,坚持一次只看一个项目,并且做笔记。

首先,需要重新看一下Clean框架的体系:

Clean一般是指,代码以洋葱的形状依据一定的依赖规则被划分为多层:内层对于外层一无所知。这就意味着依赖只能由外向内

用图表示如下。
clean架构图
clean加固图2
clean架构图3

关于clean 架构建议去看这个博客讲的,真的很清晰。

下面是摘抄自博客的:

一个Android应用通用结构如下:

  • 外层包:UI、存储、网络
  • 中间层:Presenter、Converter
  • 内层包:Interactor、Model、Repository、Executor

外层:该层是框架的具体细节所在

  • UI :这是所有Activity、Fragment、适配模块和其他与用户接口相关的Android代码存在的地方。

  • 存储 :Interactor访问和存储数据所需要使用的接口代码。例如,它包括了ContentProvider或者DBFlow等ORM。

    网络——包括了Retrofit等。

中间层:负责将实现细节和业务逻辑连接起来的粘合层

  • Presenter——Presenter负责处理来自UI的事件(如用户点击等)和内层模块(如Interaactor等)的回调。
  • Converter——Converter对象负责内层模型与外层模块的相互转换工作。

内层:

该层包含了绝大部分高级代码。其中所有的类都是POJO。该层中的类和对象对于Android应用相关的东西一无所知,因此可以被轻易移植到任何运行JVM的机器中

  • Interactor——这就是包含实际的业务逻辑代码的类。他们在后台运行,并通过回调函数将事件报告给上层。在一些项目中,他们也被称作用例。通常情况下,项目中可能包含很多小的Interactor类,用来分别解决特定的问题。这符合了单一职责原则,也比较容易对类进行理解。

  • 模型——这些就是在业务逻辑中进行处理的业务模型。

  • Repository——该包只包含了数据库或者其他外层实现的接口。Interactor使用这些接口来访问和存储数据。这就是所谓的repository模式.

现在以Todo-clean为例子,先看整体分包,可以看到是先根据模块分包,再根据职能分包:
clean分包

接下来以TaskActivity为例子,给出各个层在代码中对应的类:
clean类图
其实谷歌官方也说了,clean架构和mvp架构的区别在于,在p层和repositoies之间多了一个domain layer。
odo-clean-google
现在通过时序图来看一下调用taskPresener.loadTask()方法的过程:
clean时序图
我们可以看到,调用的过程的确是从外到内,再从内到外的。

从google这个官方的demo中,我们其实就可以抽取出一个简单的数据逻辑处理层的框架:

负责具体数据操作的UseCase基类:

/**
 * 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();
    }
}

UseCaseHandler:负责进行UseCase和线程协调的类:

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;
    }
}

UseCaseSchedule:进行前后台线程切换的类:

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();
            }
        });
    }

}

好了,这几个类是在任何项目中都通用,可以封装到底层的。下面是使用的例子:

  1. 从最里面开始写起,先定义repository,也就是支持的数据操作:
public interface TasksDataSource {

    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }
    void getTasks(@NonNull LoadTasksCallback callback);
}
public class TasksRepository implements TasksDataSource{

  private final TasksDataSource mTasksRemoteDataSource; //TasksDataSource的实现类,从网络获取数据

    private final TasksDataSource mTasksLocalDataSource;//TasksDataSource的实现类,从数据库获取数据

  public void getTasks(@NonNull final LoadTasksCallback callback) {
        checkNotNull(callback);

        // Respond immediately with cache if available and not dirty
        if (mCachedTasks != null && !mCacheIsDirty) {
            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            return;
        }

        if (mCacheIsDirty) {
            // If the cache is dirty we need to fetch new data from the network.
            getTasksFromRemoteDataSource(callback);
        } else {
           getTasksFromLocalDataSource(callback);
           }
    }

}

2 定义UseCase,它里面包含Repository的实例:

public class GetTasks extends UseCase<GetTasks.RequestValues, GetTasks.ResponseValue> {

    private final TasksRepository mTasksRepository;

    private final FilterFactory mFilterFactory;

    public GetTasks(@NonNull TasksRepository tasksRepository, @NonNull FilterFactory filterFactory) {
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
        mFilterFactory = checkNotNull(filterFactory, "filterFactory cannot be null!");
    }

  //自己定义的请求参数
   public static final class RequestValues implements UseCase.RequestValues {
        //请求参数
        private final TasksFilterType mCurrentFiltering;
        private final boolean mForceUpdate;

        public RequestValues(boolean forceUpdate, @NonNull TasksFilterType currentFiltering) {
            mForceUpdate = forceUpdate;
            mCurrentFiltering = checkNotNull(currentFiltering, "currentFiltering cannot be null!");
        }

        public boolean isForceUpdate() {
            return mForceUpdate;
        }

        public TasksFilterType getCurrentFiltering() {
            return mCurrentFiltering;
        }
    }

  //自己定义的返回参数
    public static final class ResponseValue implements UseCase.ResponseValue {
        //返回任务列表
        private final List<Task> mTasks;

        public ResponseValue(@NonNull List<Task> tasks) {
            mTasks = checkNotNull(tasks, "tasks cannot be null!");
        }

        public List<Task> getTasks() {
            return mTasks;
        }
    }


  //重点是看对基类中executeUseCase(RequestValue)的实现:
   protected void executeUseCase(final RequestValues values) {
        if (values.isForceUpdate()) {
            mTasksRepository.refreshTasks();
        }

        mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
            @Override
            public void onTasksLoaded(List<Task> tasks) {
              //取出请求参数
                TasksFilterType currentFiltering = values.getCurrentFiltering();
              //这两步可以根据请求参数去获取结果
                TaskFilter taskFilter = mFilterFactory.create(currentFiltering);
                List<Task> tasksFiltered = taskFilter.filter(tasks);
              //最后将请求结果通过回调返回
                ResponseValue responseValue = new ResponseValue(tasksFiltered);
                getUseCaseCallback().onSuccess(responseValue);
            }

            @Override
            public void onDataNotAvailable() {
                getUseCaseCallback().onError();
            }
        });

    }
}

3 最后只需要在presenter的方法中调用UseCaseHandler.execute的方法,传入Usecase和请求参数就可以完成获取数据的操作啦。。。

mUseCaseHandler = UseCaseHandler.getInstance();//这里为了简单,就直接在这里实例化,最好还是从构造函数中传递进来,降低耦合,或者采用Dagger2进行注入
mUseCaseHandler.execute(mGetTasks, requestValue,我们自定义的回调);

准备把这一部分应用到项目中,虽然可能类写起来有点多。不过我自己觉得层次很清晰。

参考的博客:使用使用Clean Architecture模式开发Android应用的详细教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值