---书接上回 点击打开链接
再然后,看android library module 也就是所谓的data layer。它采用reposittory pattern负责数据的获取。repository pattern的核心:数据的使用者不依赖于具体数据的获取途径,而是依赖于抽象 (reposittory pattern的一个简单介绍 点击打开链接)。你要用到什么数据源(网络/db/mock)再去实现就可以。怎么搞? 接口
public interface DataStore {
Observable<? extends ReturnResult> getEntriesObs();
Observable<? extends ReturnResult> getEntryObs(int entryId);
Observable<? extends ReturnResult> deleteEntryObs(int entryId);
Observable<Drawable> getImageFromURL(String URL);
}
看接口名字DataStore,应该能猜出是管理数据源头的。但是返回类Observable是个什么鬼?RxAndroid里面的,一会儿介绍。现在,可以先认为这个Observable这个类可以封装一些操作,类似Runable那样。那么 getEntriesObs()就是获取要显示的DisplayEntry列表,那么这个函数返回不是DisplayEntry列表,而是封装了获取DisplayEntry列表这一功能的一个Observable。要实现DataStore,先要获取数据,再搞一个接口DataFetcher。在DataFetcher的实现里面会依赖数据源和缓存,具体不同数据的缓存方案可以在这里引入,比如一些较大的数据(图片)可以用rom来缓存,一些较小的数据可以用内存来缓存。数据源的依赖还是通过接口RestApi,RestApi的实现类需要根据服务器的具体协议来写。当然没有服务器的时候,也可以写一个FakeAPIconnection implements RestApi来用。我的demo里就是采用了这个方法,搞了一个模拟从服务器获取数据的类FakeAPIconnection,里面加入一些Thread.sleep()来模拟网络数据延迟。
小结:reposittory pattern可以掩盖数据的具体获取方式,从最底层的数据获取,到中间层次的数据缓存,再经过重组/封装(封装到bservable,后面会讲),最后给到UI来展示,每一层都通过接口传递。这样的reposittory pattern是很灵活的,像上面DataFetcher的缓存层在Fernando Cejas的demo里面并没有,我可以很方便的加上这样一层。P.S. 自己写一写还是很有好处的,我写着写着才悟到这个reposittory pattern可以灵活地重组数据。
好,关键的时刻来了,基于RxJava/RxAndroid的UseCase。 稍微说点废话:注意在gradle.build里面添加RxJava的依赖,不然找不到相关的类。看代码之前再回忆一下UseCase的作用,从repository pattern的数据源获取需要的数据,给到ui显示。好,来看代码。
首先,它是一个抽象类,他有下面这个抽象方法
protected abstract Observable buildUseCaseObservable();
返回类型Observable,还记得之前说的那个可以封装一些操作的Observable吧,就是在这里返回的。所以这个抽象方法就是UseCase获取数据的接口。每一个UseCase的实现类都要实现这个抽象方法来获取数据。那么每一个UseCase的实现类需要依赖到刚才说的DataStore接口的实现类吧,通过构造或者setters设置就可以。这里在多说几句,关于dagger,dagger的作用就是传递这些依赖关系的(行话:依赖注入),通过构造或者setters方法来注入依赖有什么区别呢?没啥区别。所以,如果不用dagger完全OK。
好,这是说怎么传入数据的,再看下面怎么给到UI。 使用execute方法,传入一个Subscriber ,顾名思义这是一个订阅者,它订阅了传出来的数据。这个Subscriber 要在处理UI的模块(MVP的部分)中实现。
public void execute(Subscriber UseCaseSubscriber) {
this.subscription = this.buildUseCaseObservable()
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler())
.subscribe(UseCaseSubscriber);
}
.subscribeOn(Schedulers.from(threadExecutor)) 的作用:设置Observable里面封装的所有操作在非UI线程执行!
.observeOn(postExecutionThread.getScheduler()) 的作用:设置Observable里面封装的操作给出的结果送到UI线程中的Subscriber执行 !
.subscribe(UseCaseSubscriber)的作用:把Observable里面封装的所有操作的结果传给Subscriber, 这个Subscriber在UI处理的模块里(也就是Presenter),可以把这个步骤看成MVP里面Model的数据来源。MVP模式应该是相对而言中文信息比较丰富,大家比较熟悉的了,如果你熟悉MVP的话,到这里,你应该已经搞清楚了这个clean Architecture。
其实这个异步传输的功能,用AsyncTask也可以实现,用RxJava/RxAndroid的好处是什么? 要理解这个好处,可能需要深入学习响应式编程(reactive programming)。响应式编程有什么好处,用RxJava/RxAndroid就有什么好处。
这方面的资源很多,印象最深的(个人感觉讲得最好的)是这个。 点击打开链接
最后MVP的处理就很简单了,搞一个Presenter,持有一个UseCase的引用,以及所需View的引用。在Presenter里面调用UseCase的execute方法来把数据给UI,execute方法需要一个Subscriber对象,在这个Subscriber对象里面处理UI更新。具体可以参考我的demo。点击打开链接
注意我的代码和Fernando Cejas的有一点小区别,我把UseCase放到data layer里面了,而Fernando Cejas是放在domain layer里面。这样的话,可以用UseCase直接传递Android skd里面的类,而不只是domain layer 里的JAVA bean。 而且这样并没有违反Fernando Cejas提出的下面这个依赖原则。