datasource
是1個獨立的 package
,與FB導入的guava
包都在同1個工程內 - fbcore
。
datasource
的類關系比較簡單,1張類圖基本就能夠描寫清楚它們間的關系。
DataSource
是1個 interface
, 功能與JDK中的Future
類似,但是相比于Future
,它的先進的地方則在于 不單單只生產1個單1的結果,而是能夠提供系列結果。
Unlike Futures, DataSource can issue a series of results, rather than just one.
最典型的用處就是漸進式加載圖片時可以提供加載中的中間數據。
DataSubscriber
和 DataSource
構成了1個視察者模式。
Datasource
提供了注冊方法。
void subscribe(DataSubscriber<T> dataSubscriber, Executor executor);
通過 subscribe
方法我們可以把 DataSubscriber
注冊成為 DataSource
的視察者,然后當 DataSource
的數據產生變化時,在 Executor
中通知所有的視察者 - DataSubscriber
。
DataSubscriber
會響應數據的4種變化。
使用Executor
來通知視察者是比較高明的,這樣做可讓回調方法的履行線程交由 DataSubscriber
來處理,增加了靈活性。
DataSource
只是1個接口,沒有提供任何實現,AbstractDataSource
實現了 DataSource
后封裝了1些基礎的操作,例如 通知視察者,記錄數據狀態。
Datasource 的狀態記錄使用了1個枚舉類型。
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,
}
這3種狀態保存在1個成員變量(mDataSourceStatus
)中。
@GuardedBy("this")
private DataSourceStatus mDataSourceStatus;
AbstractDataSource
構造時,會把 mDataSourceStatus
設置為 IN_PROGRESS
。
protected AbstractDataSource() {
mIsClosed = false;
mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
mSubscribers = new ConcurrentLinkedQueue<>();
}
所有的視察者(定閱者)會被放在1個列表中 - mSubscribers
。
private final ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>> mSubscribers;
如果當前的數據要求沒有關閉并且滿足mDataSourceStatus == DataSourceStatus.IN_PROGRESS
時才能注冊成功視察者,由于只有當數據產生變化的時候,視察者才有存在的意義。
@Override
public void subscribe(final DataSubscriber<T> 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());
}
}
如果 DataSource
有了新的數據或要求已結束掉或被取消掉,會通知視察者。
private void notifyDataSubscribers() {
final boolean isFailure = hasFailed();
final boolean isCancellation = wasCancelled();
for (Pair<DataSubscriber<T>, Executor> pair : mSubscribers) {
notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation);
}
}
private void notifyDataSubscriber(
final DataSubscriber<T> 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);
}
}
});
}
使用 DataSource
很重要的1點:不要產生內存泄漏,也就是說,用過的資源1定要釋放掉。
使用 DataSubscriber
注冊成了視察者后,回調方法都會帶回1個 DataSource
的實例,如果要求已結束后者失敗了,拿到數據后1定要把 DataSource
給 close
掉,否則很容易造成 OOM。
BaseDataSubscriber
就是為了避免OOM,它本身的設計也很奇妙。
在毀掉方法 onNewResult
和 onFailure
中加了1個 try - catch
, 在 try 的 block 中調用子類必須重載的 onNewResultImpl
方法,然后在 finally
的 block 中 close DataSource
。
DataSourceSubscriber.java
public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> {
@Override
public void onNewResult(DataSource<T> dataSource) {
try {
onNewResultImpl(dataSource);
} finally {
if (dataSource.isFinished()) {
dataSource.close();
}
}
}
@Override
public void onFailure(DataSource<T> dataSource) {
try {
onFailureImpl(dataSource);
} finally {
dataSource.close();
}
}
@Override
public void onCancellation(DataSource<T> dataSource) {
}
@Override
public void onProgressUpdate(DataSource<T> dataSource) {
}
protected abstract void onNewResultImpl(DataSource<T> dataSource);
protected abstract void onFailureImpl(DataSource<T> dataSource);
}
IncreasingQualityDataSourceSupplier
和 FirstAvailableDataSourceSupplier
是 DataSource
的兩種不同的數據存儲情勢,等后面用到了再做分析。
Supplier
是1個設計比較奇妙的借口,用處非常廣泛。
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
如果 check 結果為 false, 則拋出異常。Fresco 的毛病處理基本上是用異常做的。