Wechat official account: Android blog
Personal website: chengang.plus/
First look at the flow chart:
One, back to the beginning
Starting point is AbstractProducerToDataSourceAdapter, Because ImagePipeline submitFetchRequest finally call CloseableProducerToDataSourceAdapter. The create method, launched the requests to the process of the decoded image picture, While CloseableProducerToDataSourceAdapter inherited from AbstractProducerToDataSourceAdapter.
1. AbstractProducerToDataSourceAdapter
In the constructor of AbstractProducerToDataSourceAdapter defines the inner classes, and implements the Consumer callback:
//AbstractProducerToDataSourceAdapter
producer.produceResults(createConsumer(), settableProducerContext);
private Consumer<T> createConsumer(a) {
return new BaseConsumer<T>() {
@Override
protected void onNewResultImpl(@Nullable T newResult, @Status int status) {
AbstractProducerToDataSourceAdapter.this.onNewResultImpl(newResult, status);
}
@Override
protected void onFailureImpl(Throwable throwable) {
AbstractProducerToDataSourceAdapter.this.onFailureImpl(throwable);
}
@Override
protected void onCancellationImpl(a) {
AbstractProducerToDataSourceAdapter.this.onCancellationImpl();
}
@Override
protected void onProgressUpdateImpl(float progress) {
AbstractProducerToDataSourceAdapter.this.setProgress(progress); }}; }Copy the code
2. onNewResultImpl
Our analysis from front to back only considered the optimistic situation, the network is smooth, there is no problem with the picture, transcoding, transformation and decoding are smooth, came to the final onNewResultImpl method:
//AbstractProducerToDataSourceAdapter
protected void onNewResultImpl(@Nullable T result, int status) {
if (super.setResult(result, isLast)) {
}
}
//AbstractDataSource
protected boolean setResult(@Nullable T value, boolean isLast) {
boolean result = setResultInternal(value, isLast);
if (result) {
notifyDataSubscribers();
}
return result;
}
Copy the code
3. notifyDataSubscribers
Next, notify message subscribers with the notifyDataSubscribers method:
//AbstractDataSource
private void notifyDataSubscribers(a) {
final boolean isFailure = hasFailed();
final boolean isCancellation = wasCancelled();
for(Pair<DataSubscriber<T>, Executor> pair : mSubscribers) { notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation); }}//AbstractDataSource
private void notifyDataSubscriber(
final DataSubscriber<T> dataSubscriber,
final Executor executor,
final boolean isFailure,
final boolean isCancellation) {
executor.execute(
new Runnable() {
@Override
public void run(a) {
if (isFailure) {
dataSubscriber.onFailure(AbstractDataSource.this);
} else if (isCancellation) {
dataSubscriber.onCancellation(AbstractDataSource.this);
} else {
dataSubscriber.onNewResult(AbstractDataSource.this); }}}); }Copy the code
Who is subscribing to this message?
Second, back to the final consumer
Back in AbstractDraweeController’s submitRequest method:
//AbstractDraweeController
protected void submitRequest(a) {
final DataSubscriber<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> dataSource) {
T image = dataSource.getResult();
if(image ! =null) { onNewResultInternal( id, dataSource, image, progress, isFinished, wasImmediate, hasMultipleResults); }}}}private void onNewResultInternal(a){
Drawable drawable = createDrawable(image);
mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
if(previousImage ! =null && previousImage != image) {
releaseImage(previousImage);
}
}
Copy the code
The code shown has been covered in previous articles and will not be analyzed.
1. Fall in love with new people and release old ones
AbstractDraweeController virtual function (PipelineDraweeController) {PipelineDraweeController}
//PipelineDraweeController
protected void releaseImage(@Nullable CloseableReference
image)
{
CloseableReference.closeSafely(image);
}
Copy the code
The deleteReference method of SharedReference is eventually called:
public void deleteReference(a) {
mResourceReleaser.release(deleted);
}
Copy the code
CloseableReference. Of (decodedBitmap, mBitmapPool); CloseableReference. BucketsBitmapPool >BasePool >Pool >ResourceReleaser
//BasePool
public void release(V value) {
final int bucketedSize = getBucketedSizeForValue(value);
final int sizeInBytes = getSizeInBytes(bucketedSize);
final Bucket<V> bucket = getBucketIfPresent(bucketedSize);
if(! mInUseValues.remove(value)) { free(value); }else {
if (bucket == null|| bucket.isMaxLengthExceeded() || isMaxSizeSoftCapExceeded() || ! isReusable(value)) {if(bucket ! =null) { bucket.decrementInUseCount(); free(value); mUsed.decrement(sizeInBytes); }}else{ bucket.release(value); mFree.increment(sizeInBytes); mUsed.decrement(sizeInBytes); }}}//BucketsBitmapPool
@Override
protected void free(Bitmap value) {
value.recycle();
}
Copy the code
Select * from Set where bitmap (2) is not deleted. If this Bucket is present in the current SparseArray<Bucket> mBuckets, the used and free space sizes are updated and the previously allocated space is added to the mFreeList of the Bucket. 3. If the bucket cannot be found, release the bitmap and update the used space.
2. Release summary
The Pool and Bucket logic makes full use of the inBitmap parameter configuration of BitmapOptions, reusing the allocated space size, and updating the used and unused space size when the bitmap is released. Of course, this size is limited by the parameter configuration. It’s nice to use inBitmap to avoid direct allocation while taking full advantage of the allocated space in retrieving the reused bitmap.
Destroy the view
1. Detach
When the view becomes invisible, the onVisibilityChange callback comes back and the detachController method is called in DraweeHolder:
//DraweeHolder
private void detachController(a) {
if(isControllerValid()) { mController.onDetach(); }}//AbstractDraweeController
@Override
public void onDetach(a) {
mDeferredReleaser.scheduleDeferredRelease(this);
}
//DeferredReleaser
public void scheduleDeferredRelease(Releasable releasable) {
ensureOnUiThread();
if(! mPendingReleasables.add(releasable)) {return;
}
// Posting to the UI queue is an O(n) operation, so we only do it once.
// The one runnable does all the releases.
if (mPendingReleasables.size() == 1) { mUiHandler.post(releaseRunnable); }}//DeferredReleaser
private final Runnable releaseRunnable = new Runnable() {
@Override
public void run(a) {
ensureOnUiThread();
for(Releasable releasable : mPendingReleasables) { releasable.release(); } mPendingReleasables.clear(); }};Copy the code
AbstractDraweeController’s onDetach method shows that the object needs to be freed. The Releasable object is AbstractDraweeController itself, because AbstractDraweeController implements the Releasable interface.
So in DeferredReleaser, you have to make sure that it’s freed in the main thread because of the UI manipulation involved. Look at the AbstractDraweeController release method:
//AbstractDraweeController
@Override
public void release(a) {
if(mSettableDraweeHierarchy ! =null) {
mSettableDraweeHierarchy.reset();
}
releaseFetch();
}
//PipelineDraweeController
private void releaseFetch(a) {
if(mDataSource ! =null) {
mDataSource.close();
mDataSource = null;
}
if(mDrawable ! =null) {
releaseDrawable(mDrawable);
}
mDrawable = null;
if(mFetchedImage ! =null) {
releaseImage(mFetchedImage);
mFetchedImage = null; }}Copy the code
2. release
The reset method in the release method is finally defined in GenericDraweeHierarchy:
//GenericDraweeHierarchy
@Override
public void reset(a) {
//resetActualImages();
mActualImageWrapper.setDrawable(new ColorDrawable(Color.TRANSPARENT));
resetFade();
}
Copy the code
Set the layer we set the image to transparent, reset the other layers, and then refresh.
AbstractDataSource class AbstractDataSource class AbstractDataSource class AbstractDataSource class AbstractDataSource class AbstractDataSource
//AbstractDataSource
@Override
public boolean close(a) {
mResult = null;
}
Copy the code
Close mFetchedImage, which is of type CloseableReference and contains the CloseableStaticBitmap object.
//PipelineDraweeController
@Override
protected void releaseImage(@Nullable CloseableReference<CloseableImage> image) {
CloseableReference.closeSafely(image);
}
//CloseableReference
public static void closeSafely(@Nullable CloseableReference
ref) {
if(ref ! =null) { ref.close(); }}//CloseableReference
@Override
public void close(a) {
synchronized (this) {
if (mIsClosed) {
return;
}
mIsClosed = true;
}
mSharedReference.deleteReference();
}
//SharedReference
public void deleteReference(a) {
if (decreaseRefCount() == 0) {
T deleted;
synchronized (this) {
deleted = mValue;
mValue = null; } mResourceReleaser.release(deleted); removeLiveReference(deleted); }}//BasePool
@Override
public void release(V value) {
// This has been described before
}
//SharedReference
private static void removeLiveReference(Object value) {
synchronized (sLiveObjects) {
Integer count = sLiveObjects.get(value);
if (count == null) {
// Uh oh.
} else if (count == 1) {
sLiveObjects.remove(value);
} else {
sLiveObjects.put(value, count - 1); }}}Copy the code
Release from top to bottom, empty of the empty, remove of the remove, to avoid memory leaks.
Four,
To sum up:
1. Memory cache unencoded data
CloseableReference
The order in which MemoryPooledByteBuffer data is generated is as follows: MemoryPooledByteBufferOutputStream newOutputStream method get MemoryPooledByteBufferOutputStream object, The toByteBuffer is then executed to generate the MemoryPooledByteBuffer.
And the data returned is written in the network request MemoryPooledByteBufferOutputStream object, in the process of writing, the Stream object generated CloseableReference object memory block, and hold him;
2. Memory cache encoded data
CloseableReference
The actual type is CloseableReference, and their inheritance is: CloseableStaticBitmap->CloseableBitmap->CloseableImage
3. Disk cache
Disk cache data is to hold PooledByteBufferInputStream MemoryPooledByteBuffer object, the object held NativeMemoryChunk memory blocks of data, reading data, Read the data in memory to the CountingOutputStream corresponding to the cache file, and the read and write operations are in the native layer, nativeCopyToByteArray.
4. Producer
Is the content producer, and the producer defines Consumer as the Consumer
5. ImagePipeline/ImagePipelineFactory
It is the provider of production tools as well as the initiator of production processes
6. The Controller (AbstractDraweeController/PipelineDraweeController)
Link content production to Hierarchy
7. DraweeHolder
Will DraweeView (GenericDraweeView/SimpleDraweeView) and Hierarchy, and the Controller.
View sets Hierarchy to DraweeHolder, which holds Hierarchy. Hierarchy manages drawables, divides multiple drawables into different presentation levels, and maps them to Controller.
Summary flow chart: