之前大致把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 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 dataSubscriber =
new BaseDataSubscriber() {
@Override
public void onNewResultImpl(DataSource 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 dataSource) {
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
@Override
public void onProgressUpdate(DataSource dataSource) {
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
onProgressUpdateInternal(id, dataSource, progress, isFinished);
}
};
//为DataSource增加subscriber
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
}
这里可以看到我们的图片资源获取是通过getDatasource()方法来进行的,看下代码:
@Override
protected DataSource> 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 get() {
return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);
}
看下getDataSourceForRequest方法:
@Override
protected DataSource> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
//接下来要进入的方法
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel));
}
所以...我们终于进入到了ImagePipeline里面了...
public DataSource> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
try {
//获得Producer管道
Producer> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
//接下来要进入的方法
return submitFetchRequest(
producerSequence,
imageRequest,
lowestPermittedRequestLevelOnSubmit,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
首先是将管道搞定了,这个在上一篇中讲到过,然后就是submitFetchRequest方法:
private DataSource> submitFetchRequest(
Producer> 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 DataSource> create(
Producer> producer,
SettableProducerContext settableProducerContext,
RequestListener listener) {
return new CloseableProducerToDataSourceAdapter(
producer, settableProducerContext, listener);
}
private CloseableProducerToDataSourceAdapter(
Producer> producer,
SettableProducerContext settableProducerContext,
RequestListener listener) {
super(producer, settableProducerContext, listener);
}
看下它的父类AbstractProducerToDataSourceAdapter是怎么初始化的:
protected AbstractProducerToDataSourceAdapter(
Producer 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 implements DataSource {
...
//存储所有的subscriber
private final ConcurrentLinkedQueue, Executor>> mSubscribers;
protected AbstractDataSource() {
mIsClosed = false;
mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
mSubscribers = new ConcurrentLinkedQueue, 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 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, Executor> pair : mSubscribers) {
notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation);
}
}
//通知某一个Subscriber
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);
}
}
});
}
...
//通知Subscriber下载进程有更新
protected void notifyProgressUpdate() {
for (Pair, Executor> pair : mSubscribers) {
final DataSubscriber 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 implements DataSubscriber {
@Override
public void onNewResult(DataSource dataSource) {
final boolean shouldClose = dataSource.isFinished();
try {
onNewResultImpl(dataSource);
} finally {
//保证source最后会被关闭,防止内存泄漏
if (shouldClose) {
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);
}
从这个代码可以看到,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> 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> dataSource) {
Log.d(TAG, "onFailureImpl: ");
}
//构造一个单线程的Executor
}, Executors.newSingleThreadExecutor());
在这里因为我们是在子线程中执行Subscriber的方法,所以我们要进行UI操作的话需要传到UI线程中。
关于ImagePipeline的大致就是这样啦~现在真正实践的还不多,等以后用到的时候需要补充的再回来补吧。