Fresco源码解析 - DataSource怎样存储数据
datasource
是一个独立的 package
,与FB导入的guava
包都在同一个工程内 - fbcore
。
datasource
的类关系比较简单,一张类图基本就可以描述清楚它们间的关系。
DataSource
是一个 interface
, 功能与JDK中的Future
类似,但是相比于Future
,它的先进之处则在于 不仅仅只生产一个单一的结果,而是能够提供系列结果。
Unlike Futures, DataSource can issue a series of results, rather than just one.
最典型的用途就是渐进式加载图片时可以提供加载中的中间数据。
DataSubscriber
和 DataSource
构成了一个观察者模式。
Datasource
提供了注册方法。
void subscribe(DataSubscriber dataSubscriber, Executor executor);
- 1
通过 subscribe
方法我们可以把 DataSubscriber
注册成为 DataSource
的观察者,然后当 DataSource
的数据发生变化时,在 Executor
中通知所有的观察者 - DataSubscriber
。
DataSubscriber
会响应数据的四种变化。
- onNewResult
- onFailure
- onCancellation
- onProgressUpdate
使用Executor
来通知观察者是比较高明的,这样做可以让回调方法的执行线程交由 DataSubscriber
来处理,增加了灵活性。
DataSource
只是一个接口,没有提供任何实现,AbstractDataSource
实现了 DataSource
后封装了一些基础的操作,例如 通知观察者,记录数据状态。
Datasource 的状态记录使用了一个枚举类型。
private enum DataSourceStatus { // data source has not finished yet IN_PROGRESS, // data source has finished with success SUCCESS, // data source has finished with failure FAILURE,}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这三种状态保存在一个成员变量(mDataSourceStatus
)中。
@GuardedBy("this")private DataSourceStatus mDataSourceStatus;
- 1
- 2
AbstractDataSource
构造时,会把 mDataSourceStatus
设置为 IN_PROGRESS
。
protected AbstractDataSource() { mIsClosed = false; mDataSourceStatus = DataSourceStatus.IN_PROGRESS; mSubscribers = new ConcurrentLinkedQueue<>();}
- 1
- 2
- 3
- 4
- 5
所有的观察者(订阅者)会被放在一个列表中 - mSubscribers
。
private final ConcurrentLinkedQueue , Executor>> mSubscribers;
- 1
如果当前的数据请求没有关闭并且满足mDataSourceStatus == DataSourceStatus.IN_PROGRESS
时才能注册成功观察者,因为只有当数据发生变化的时候,观察者才有存在的意义。
@Overridepublic void subscribe(final DataSubscriber dataSubscriber, final Executor executor) { Preconditions.checkNotNull(dataSubscriber); Preconditions.checkNotNull(executor); boolean shouldNotify; synchronized( this) { if (mIsClosed) { return; } if (mDataSourceStatus == DataSourceStatus.IN_PROGRESS) { mSubscribers.add(Pair.create(dataSubscriber, executor)); } shouldNotify = hasResult() || isFinished() || wasCancelled(); } if (shouldNotify) { notifyDataSubscriber(dataSubscriber, executor, hasFailed(), wasCancelled()); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
如果 DataSource
有了新的数据或者请求已经结束掉或被取消掉,会通知观察者。
private void notifyDataSubscribers() { final boolean isFailure = hasFailed(); final boolean isCancellation = wasCancelled(); for (Pair , Executor> pair : mSubscribers) { notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation); }} private void notifyDataSubscriber( final DataSubscriber dataSubscriber, final Executor executor, final boolean isFailure, final boolean isCancellation) { executor.execute( new Runnable() { @Override public void run() { if (isFailure) { dataSubscriber.onFailure(AbstractDataSource. this); } else if (isCancellation) { dataSubscriber.onCancellation(AbstractDataSource. this); } else { dataSubscriber.onNewResult(AbstractDataSource. this); } } });}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
使用 DataSource
很重要的一点:不要产生内存泄露,也就是说,用过的资源一定要释放掉。
DataSubscriber
注册成为了观察者后,回调方法都会带回一个 DataSource
的实例,如果请求已经结束后者失败了,拿到数据后一定要把 DataSource
给 close
掉,否则很容易造成 OOM。 BaseDataSubscriber
就是为了防止OOM,它本身的设计也很巧妙。
在毁掉方法 onNewResult
和 onFailure
中加了一个 try - catch
, 在 try 的 block 中调用子类必须重载的onNewResultImpl
方法,然后在 finally
的 block 中 close DataSource
。
DataSourceSubscriber.java
public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> { @Override public void onNewResult(DataSource dataSource) { try { onNewResultImpl(dataSource); } finally { if (dataSource.isFinished()) { dataSource.close(); } } } @Override public void onFailure(DataSource dataSource) { try { onFailureImpl(dataSource); } finally { dataSource.close(); } } @Override public void onCancellation(DataSource dataSource) { } @Override public void onProgressUpdate(DataSource dataSource) { } protected abstract void onNewResultImpl(DataSource dataSource); protected abstract void onFailureImpl(DataSource dataSource);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
IncreasingQualityDataSourceSupplier
和 FirstAvailableDataSourceSupplier
是 DataSource
的两种不同的数据存储形式,等后面用到了再做分析。
Supplier
是一个设计比较巧妙的借口,用途非常广泛。
A class that can supply objects of a single type. Semantically, this could be a factory, generator, builder, closure, or something else entirely. No guarantees are implied by this interface.
SettableDataSource
在 set
方法中使用 Guava
的 Preconditions
来做数据合法性检验,它与 DataSource
的区别也是仅此而已。
Preconditions
- checkArgument
- checkState
- checkNotNull
- checkElementIndex
- checkPositionIndex
- checkPositionIndexes
如果 check 结果为 false, 则抛出异常。Fresco 的错误处理基本上是用异常做的。