Fresco源碼解析,fresco源碼
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<T> dataSubscriber, Executor executor);
通過 subscribe 方法我們可以把 DataSubscriber 註冊成為 DataSource 的觀察者,然後當 DataSource 的資料發生變化時,在 Executor 中通知所有的觀察者 - DataSubscriber。
DataSubscriber 會響應資料的四種變化。
使用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,}
這三種狀態儲存在一個成員變數(mDataSourceStatus)中。
@GuardedBy("this")private DataSourceStatus mDataSourceStatus;
AbstractDataSource 構造時,會把 mDataSourceStatus 設定為 IN_PROGRESS。
protected AbstractDataSource() { mIsClosed = false; mDataSourceStatus = DataSourceStatus.IN_PROGRESS; mSubscribers = new ConcurrentLinkedQueue<>();}
所有的觀察者(訂閱者)會被放在一個列表中 - mSubscribers。
private final ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>> mSubscribers;
如果當前的資料請求沒有關閉並且滿足mDataSourceStatus == DataSourceStatus.IN_PROGRESS時才能註冊成功觀察者,因為只有當資料發生變化的時候,觀察者才有存在的意義。
@Overridepublic 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 很重要的一點:不要產生記憶體泄露,也就是說,用過的資源一定要釋放掉。
使用 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<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 是一個設計比較巧妙的借口,用途非常廣泛。
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 的錯誤處理基本上是用異常做的。