Android之Fresco框架(四)--ImagePipeline的呼叫和使用
之前大致把ImagePipeline的配置和底層實現都講了一下,這一篇來重點講一下我們在傳送圖片請求的時候是怎麼把請求傳給ImagePipeline的,以及我們如何自己直接對ImagePipeline例項進行請求,記憶體管理等操作。
SimpleDraweeView中ImagePipeline的呼叫
在第一篇的時候,我們當時只需要對SimpleDraweeView進行setImageURI()方法,setController()方法之後,都能夠成功發起圖片請求,那底層是怎麼實現的呢。先來看一下SimpleDraweeView中的相關程式碼:
/** * Displays an image given by the uri. * * @param uri uri of the image * @undeprecate */ @Override public void setImageURI(Uri uri) { setImageURI(uri, null); } /** * Displays an image given by the uri string. * * @param uriString uri string of the image */ public void setImageURI(@Nullable String uriString) { setImageURI(uriString, null); } /** * Displays an image given by the uri. * * @param uri uri of the image * @param callerContext caller context */ //最終都會執行該方法 public void setImageURI(Uri uri, @Nullable Object callerContext) { DraweeController controller = mSimpleDraweeControllerBuilder .setCallerContext(callerContext) .setUri(uri) .setOldController(getController()) .build(); //所有setImageURI最終呼叫方法 setController(controller); } /** * Displays an image given by the uri string. * * @param uriString uri string of the image * @param callerContext caller context */ public void setImageURI(@Nullable String uriString, @Nullable Object callerContext) { Uri uri = (uriString != null) ? Uri.parse(uriString) : null; setImageURI(uri, callerContext); }
可以看出,不管用的是哪一個過載的setImageURI,最終都是會呼叫到同一個方法中,而該方法中又會執行setController()方法。那我們來看一下setController()方法,所以我們用不管setImageURI()還是setController()最終都是殊途同歸。setController()在父類DraweeView()中。DraweeView是一個繼承於ImageView的子類:
private DraweeHolder<DH> mDraweeHolder; public void setController(@Nullable DraweeController draweeController) { mDraweeHolder.setController(draweeController); super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); }
所以方法又呼叫了DraweeHolder裡面的setController()方法,對於DraweeHolder,個人感覺是起到了介面卡的作用,也使得我們的程式碼更加易讀吧。先不瞎扯看下DraweeHolder中的程式碼:
public void setController(@Nullable DraweeController draweeController) { boolean wasAttached = mIsControllerAttached; if (wasAttached) { detachController(); } //清除掉舊的Controller if (isControllerValid()) { //mEventTracker起到一個紀錄的作用 mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER); mController.setHierarchy(null); } mController = draweeController; if (mController != null) { mEventTracker.recordEvent(Event.ON_SET_CONTROLLER); mController.setHierarchy(mHierarchy); } else { mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER); } if (wasAttached) { //呼叫到該方法 attachController(); } } private void attachController() { if (mIsControllerAttached) { return; } mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER); mIsControllerAttached = true; if (mController != null && mController.getHierarchy() != null) { //呼叫Controller的onAttach()方法 mController.onAttach(); } }
接下來就是看Controller的onAttach方法,在SimpleDraweeView中設定的PipelineDraweeController例項,onAttach方法定義於它的父類AbstractDraweeController中:
@Override
public void onAttach() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: onAttach: %s",
System.identityHashCode(this),
mId,
mIsRequestSubmitted ? "request already submitted" : "request needs submit");
}
mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
Preconditions.checkNotNull(mSettableDraweeHierarchy);
mDeferredReleaser.cancelDeferredRelease(this);
mIsAttached = true;
if (!mIsRequestSubmitted) {
//呼叫的方法
submitRequest();
}
}
前面的都不重要,最主要是這裡又呼叫了submitRequest()方法,從這個名字來看我們離目標就已經很近了。再看一下它的程式碼:
protected void submitRequest() {
final T closeableImage = getCachedImage();
if (closeableImage != null) {
mDataSource = null;
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mEventTracker.recordEvent(Event.ON_SUBMIT_CACHE_HIT);
getControllerListener().onSubmit(mId, mCallerContext);
onNewResultInternal(mId, mDataSource, closeableImage, 1.0f, true, true);
return;
}
mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
getControllerListener().onSubmit(mId, mCallerContext);
mSettableDraweeHierarchy.setProgress(0, true);
mIsRequestSubmitted = true;
mHasFetchFailed = false;
//獲得圖片對應的DataSource
mDataSource = getDataSource();
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: submitRequest: dataSource: %x",
System.identityHashCode(this),
mId,
System.identityHashCode(mDataSource));
}
final String id = mId;
final boolean wasImmediate = mDataSource.hasResult();
//用一個subscriber來管理獲取的結果,保證DataSource最終會被關閉
final DataSubscriber<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> dataSource) {
// isFinished must be obtained before image, otherwise we might set intermediate result
// as final image.
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
T image = dataSource.getResult();
if (image != null) {
onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
} else if (isFinished) {
onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
}
}
@Override
public void onFailureImpl(DataSource<T> dataSource) {
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
@Override
public void onProgressUpdate(DataSource<T> dataSource) {
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
onProgressUpdateInternal(id, dataSource, progress, isFinished);
}
};
//為DataSource增加subscriber
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
}
這裡可以看到我們的圖片資源獲取是通過getDatasource()方法來進行的,看下程式碼:
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(TAG, "controller %x: getDataSource", System.identityHashCode(this));
}
return mDataSourceSupplier.get();
}
//Supplier的get方法在AbstractDraweeControllerBuilder中的getDataSourceSupplierForRequest方法中被重寫
@Override
public DataSource<IMAGE> get() {
return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);
}
看下getDataSourceForRequest方法:
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
//接下來要進入的方法
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel));
}
所以...我們終於進入到了ImagePipeline裡面了...
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
try {
//獲得Producer管道
Producer<CloseableReference<CloseableImage>> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
//接下來要進入的方法
return submitFetchRequest(
producerSequence,
imageRequest,
lowestPermittedRequestLevelOnSubmit,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
首先是將管道搞定了,這個在上一篇中講到過,然後就是submitFetchRequest方法:
private <T> DataSource<CloseableReference<T>> submitFetchRequest(
Producer<CloseableReference<T>> producerSequence,
ImageRequest imageRequest,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit,
Object callerContext) {
final RequestListener requestListener = getRequestListenerForRequest(imageRequest);
try {
//各種設定
ImageRequest.RequestLevel lowestPermittedRequestLevel =
ImageRequest.RequestLevel.getMax(
imageRequest.getLowestPermittedRequestLevel(),
lowestPermittedRequestLevelOnSubmit);
SettableProducerContext settableProducerContext = new SettableProducerContext(
imageRequest,
generateUniqueFutureId(),
requestListener,
callerContext,
lowestPermittedRequestLevel,
/* isPrefetch */ false,
imageRequest.getProgressiveRenderingEnabled() ||
imageRequest.getMediaVariations() != null ||
!UriUtil.isNetworkUri(imageRequest.getSourceUri()),
imageRequest.getPriority());
//接下來要進入的方法
return CloseableProducerToDataSourceAdapter.create(
producerSequence,
settableProducerContext,
requestListener);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
前面一堆就不說了直接看CloseableProducerToDataSourceAdapter.create方法:
public static <T> DataSource<CloseableReference<T>> create(
Producer<CloseableReference<T>> producer,
SettableProducerContext settableProducerContext,
RequestListener listener) {
return new CloseableProducerToDataSourceAdapter<T>(
producer, settableProducerContext, listener);
}
private CloseableProducerToDataSourceAdapter(
Producer<CloseableReference<T>> producer,
SettableProducerContext settableProducerContext,
RequestListener listener) {
super(producer, settableProducerContext, listener);
}
看下它的父類AbstractProducerToDataSourceAdapter是怎麼初始化的:
protected AbstractProducerToDataSourceAdapter(
Producer<T> producer,
SettableProducerContext settableProducerContext,
RequestListener requestListener) {
mSettableProducerContext = settableProducerContext;
mRequestListener = requestListener;
mRequestListener.onRequestStart(
settableProducerContext.getImageRequest(),
mSettableProducerContext.getCallerContext(),
mSettableProducerContext.getId(),
mSettableProducerContext.isPrefetch());
//管道中的producer開始呼叫produceResults執行請求了
producer.produceResults(createConsumer(), settableProducerContext);
}
當看到最後一行的時候,終於看到了結局,producer.produceResults()上一篇已經說過了,就是pipeline中的producer開始執行自己的工作了。所以到這裡,我們的整個請求也就正式跟上面的內容接軌了。實在是...很曲折...
這個是我們通過呼叫DraweeView來呼叫到ImagePipeline發起圖片請求的,我們也可以直接獲得Fresco中的ImagePipeline,直接傳入我們的ImageRequest來實現請求操作。
在看ImagePipeline之前先看一下幾個其他的類:
DataSource與DataSubscriber
DataSource跟Future相似,都會儲存我們的非同步計算的結果,不過不同的是,DataSource會返回一系列的結果。DataSubscriber是一個用於對DataSource進行監聽的類,當DataSource中的資料處理有了結果,不管是成功還是失敗,都會向Subscriber發出通知,讓Subscriber進行相應的處理,這類似於設計模式中的觀察者模式。
我們看一下AbstractDataSource中的程式碼:
public abstract class AbstractDataSource<T> implements DataSource<T> {
...
//儲存所有的subscriber
private final ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>> mSubscribers;
protected AbstractDataSource() {
mIsClosed = false;
mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
mSubscribers = new ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>>();
}
...
@Override
public boolean close() {
T resultToClose;
synchronized (this) {
if (mIsClosed) {
return false;
}
mIsClosed = true;
resultToClose = mResult;
mResult = null;
}
if (resultToClose != null) {
closeResult(resultToClose);
}
if (!isFinished()) {
notifyDataSubscribers();
}
synchronized (this) {
mSubscribers.clear();
}
return true;
}
...
//新增Subscriber以及執行Subscriber對應的executor
@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));
}
//當這三個狀態有一個為true時,需要同時Subscriber進行處理
shouldNotify = hasResult() || isFinished() || wasCancelled();
}
if (shouldNotify) {
notifyDataSubscriber(dataSubscriber, executor, hasFailed(), wasCancelled());
}
}
//通知Subscriber
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);
}
}
//通知某一個Subscriber
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);
}
}
});
}
...
//通知Subscriber下載程序有更新
protected void notifyProgressUpdate() {
for (Pair<DataSubscriber<T>, Executor> pair : mSubscribers) {
final DataSubscriber<T> subscriber = pair.first;
Executor executor = pair.second;
executor.execute(
new Runnable() {
@Override
public void run() {
subscriber.onProgressUpdate(AbstractDataSource.this);
}
});
}
}
}
這裡就不將所有的程式碼,主要是看一下DataSource與Subscriber之間的互動部分。在訂閱的時候,需要傳入一個Executor,這是為了讓Subscriber在該Executor上執行我們的操作,這也方便我們進行定製,例如要直接在主執行緒對source進行處理或者是先放在其他子執行緒進行一些耗時的操作。其他的方法主要是涉及到一些下載狀態量的setter和getter。再看一下BaseDataSubscriber的程式碼:
public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> {
@Override
public void onNewResult(DataSource<T> dataSource) {
final boolean shouldClose = dataSource.isFinished();
try {
onNewResultImpl(dataSource);
} finally {
//保證source最後會被關閉,防止記憶體洩漏
if (shouldClose) {
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);
}
從這個程式碼可以看到,Subscriber中再onNewResult和onFailure中對保證了記憶體不會洩漏,而我們一般在重寫BaseDataSource的時候一般只需要重寫onNewResultImpl和onFailureImpl,這樣能夠保證安全。不過有一個點就是,dataSource在onNewResult或onFailure方法執行結束之後就會被close(),所以我們如果要在方法外面用到時,需要對資料進行復制。
ImagePipeline
之前我們在講程式碼的時候已經提到了ImagePipeline中的fetchDecodedImage方法和submitFetchRequest方法的程式碼了。除了這些之外,還有其他一些方法。
例如針對影象請求的,有:
fetchEncodedImage:獲取到未解碼的資料
fetchImageFromBitmapCache:只能從MemoryCache中獲取資料
這些都是構造出對應的ProduceSequence,然後由submitFetchRequest方法來發出請求操作。
針對影象預快取的,有:
prefetchToBitmapCache:將資料先快取Cache中
prefetchToDiskCache:將資料先快取到Disk中
這些也是構造出對應的ProduceSequence,然後由submitPrefetchRequest方法來發出請求操作。
針對記憶體管理的,有:
evictFromMemoryCache:將某個uri對應的資料從MemoryCache中刪除掉
evictFromDiskCache:將某個uri或ImageRequest對應的資料從Disk中刪除掉
evictFromCache:將某個uri對應的資料從所有快取中刪除掉
clearMemoryCaches:清除MemoryCache中的內容
clearDiskCaches:清除Disk中的內容
clearCaches:清除所有快取中的內容
以上就是一些我們主要會用到的方法了。接下來看一下我們怎麼直接使用ImagePipeline進行影象請求。
自定義ImageRequest
//構造ImageRequest
private ImageRequest getImageRequest() {
//後處理器
Postprocessor processor = new MyPostprocessor();
ImageRequest request = ImageRequestBuilder
//設定URI
.newBuilderWithSource(uri)
//自動旋轉
.setAutoRotateEnabled(true)
//最低級別請求
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
.setProgressiveRenderingEnabled(false)
.setPostprocessor(processor)
.build();
return request;
}
獲取Fresco的ImagePipeline併發從ImageRequest:
//獲取ImagePipeline
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageRequest request = getImageRequest();
//傳送請求,獲取已經解碼的圖片資料
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(request,this);
//這裡是繼承了BaseBitmapDataSubscriber,會將CloseableImage轉為CloseableBitmap
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
protected void onNewResultImpl(Bitmap bitmap) {
//複製資料
Bitmap newBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
//利用Handler傳到UI執行緒中
Message message = new Message();
message.what = 0;
message.obj = newBitmap;
handler.sendMessage(message);
Log.d(TAG, "onNewResultImpl: ");
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
Log.d(TAG, "onFailureImpl: ");
}
//構造一個單執行緒的Executor
}, Executors.newSingleThreadExecutor());
在這裡因為我們是在子執行緒中執行Subscriber的方法,所以我們要進行UI操作的話需要傳到UI執行緒中。
關於ImagePipeline的大致就是這樣啦~現在真正實踐的還不多,等以後用到的時候需要補充的再回來補吧。