Fresco Gif加載解析
普通Gif加載
val controller = Fresco.newDraweeControllerBuilder()
.setAutoPlayAnimations(true)//就是添加了這一句代碼触趴,就可以播放動圖
.setImageRequest(request)
.setOldController(sivBanner?.controller)
.build()
sivBanner?.controller = controller
先看性能表現(xiàn)效果圖:
從上面三張圖可以看的出來普通的加載會頻繁GC,這種情況比較嚴重,并且CPU使用率比較高宏所,50%左右,并且通過Dump內(nèi)存的分布可以看出來叽掘,F(xiàn)resco的緩存了太多的圖片楣铁,并且占用的是BitmapMemory,這3種表現(xiàn)在加載Gif的時候是無法接受的更扁。我們希望達到的效果是CPU的使用率能夠降下來盖腕,并且盡量少的占用Fresco的內(nèi)存緩存,如果想達到目標浓镜,只能去看看Fresco的Gif加載的代碼溃列。
這里再提一點,為什么會想到去優(yōu)化Fresco的Gif加載膛薛。因為看到android-gif-drawable的表現(xiàn)后听隐,發(fā)現(xiàn)android-gif-drawable其實是依賴于GifLib來做的底層支撐,而Fresco也是基于GifLib哄啄。因為兩個框架底層用的是一樣的雅任,那么從理論上來說,F(xiàn)resco就應(yīng)該有優(yōu)化的空間咨跌。
Gif加載流程分析
先從頭分析下
AbstractDraweeController::onNewResultInternal()會把生成的Drawable設(shè)置給DraweeHierachy沪么,先找到最終生成的是什么Drawable ----->
PipelineDraweeController::createDrawable()---->
mDefaultDrawableFactory::createDrawable()---->
mAnimatedDrawableFactory::createDrawable()---->
ExperimentalBitmapAnimationDrawableFactory::createDrawable()---->
最終生成的是AnimatedDrawable2
@Override
public AnimatedDrawable2 createDrawable(CloseableImage image) {
return new AnimatedDrawable2(
//生成AnimationBackend,傳入AnimatedDrawable2
createAnimationBackend(
((CloseableAnimatedImage) image).getImageResult()));
}
private AnimationBackend createAnimationBackend(AnimatedImageResult animatedImageResult) {
AnimatedDrawableBackend animatedDrawableBackend =
createAnimatedDrawableBackend(animatedImageResult);
//設(shè)置幀緩存
BitmapFrameCache bitmapFrameCache = createBitmapFrameCache(animatedImageResult);
BitmapFrameRenderer bitmapFrameRenderer =
new AnimatedDrawableBackendFrameRenderer(bitmapFrameCache, animatedDrawableBackend);
int numberOfFramesToPrefetch = mNumberOfFramesToPrepareSupplier.get();//預(yù)先獲取幀的個數(shù)
BitmapFramePreparationStrategy bitmapFramePreparationStrategy = null;
BitmapFramePreparer bitmapFramePreparer = null;//圖片幀的準備者
if (numberOfFramesToPrefetch > 0) {
bitmapFramePreparationStrategy =
new FixedNumberBitmapFramePreparationStrategy(numberOfFramesToPrefetch);
bitmapFramePreparer = createBitmapFramePreparer(bitmapFrameRenderer);
}
BitmapAnimationBackend bitmapAnimationBackend = new BitmapAnimationBackend(
mPlatformBitmapFactory,
bitmapFrameCache,
new AnimatedDrawableBackendAnimationInformation(animatedDrawableBackend),
bitmapFrameRenderer,
bitmapFramePreparationStrategy,
bitmapFramePreparer);
return AnimationBackendDelegateWithInactivityCheck.createForBackend(
bitmapAnimationBackend,
mMonotonicClock,
mScheduledExecutorServiceForUiThread);
}
最終設(shè)置給DraweeHierachy的是AnimatedDrawable2锌半,
先看下AnmatedDrawable2
AnimatedDrawable2
@Override
public void draw(Canvas canvas) {
.....
// 繪制當(dāng)前幀禽车,把繪制的操作交給了mAnimationBackend
boolean frameDrawn = mAnimationBackend.drawFrame(this, canvas, frameNumberToDraw);
....
long targetRenderTimeForNextFrameMs = FrameScheduler.NO_NEXT_TARGET_RENDER_TIME;
long scheduledRenderTimeForNextFrameMs = -1;
long actualRenderTimeEnd = now();
if (mIsRunning) {
// Schedule the next frame if needed.
targetRenderTimeForNextFrameMs =
mFrameScheduler.getTargetRenderTimeForNextFrameMs(actualRenderTimeEnd - mStartTimeMs);
if (targetRenderTimeForNextFrameMs != FrameScheduler.NO_NEXT_TARGET_RENDER_TIME) {
scheduledRenderTimeForNextFrameMs =
targetRenderTimeForNextFrameMs + mFrameSchedulingDelayMs;
//繪制下一幀
scheduleNextFrame(scheduledRenderTimeForNextFrameMs);
}
}
....
}
private void scheduleNextFrame(long targetAnimationTimeMs) {
mExpectedRenderTimeMs = mStartTimeMs + targetAnimationTimeMs;
scheduleSelf(mInvalidateRunnable, mExpectedRenderTimeMs);
}
private final Runnable mInvalidateRunnable = new Runnable() {
@Override
public void run() {
unscheduleSelf(mInvalidateRunnable);
//繪制下一幀
invalidateSelf();
}
};
BitmapAnimationBackend::drawFrame()---->
BitmapAnimationBackend::drawFrameOrFallback()---->
@Override
public boolean drawFrame(
Drawable parent,
Canvas canvas,
int frameNumber) {
...
//繪制當(dāng)前幀
boolean drawn = drawFrameOrFallback(canvas, frameNumber, FRAME_TYPE_CACHED);
...
//準備下面的幾幀
if (mBitmapFramePreparationStrategy != null && mBitmapFramePreparer != null) {
mBitmapFramePreparationStrategy.prepareFrames(
mBitmapFramePreparer,
mBitmapFrameCache,
this,
frameNumber);
}
return drawn;
}
//drawFrameOrFallback,繪制當(dāng)前幀或者后備幀
//1. FRAME_TYPE_CACHED 首先嘗試從幀緩存里面取當(dāng)前幀的bitmap刊殉,如果取到就繪制和緩存當(dāng)前幀
//2. FRAME_TYPE_REUSED 嘗試獲取可以重用的bitmap殉摔,獲取到就繪制和緩存當(dāng)前幀
//3. FRAME_TYPE_CREATED 創(chuàng)建Bitmap,然后繪制和緩存當(dāng)前幀
//4. FRAME_TYPE_FALLBACK 獲取給定幀編號的后備幀记焊。 如果繪制幀的所有其他嘗試都失敗逸月,則調(diào)用此方法。 返回的Bitmap可以是最后繪制的幀(如果有的話)
private boolean drawFrameOrFallback(Canvas canvas, int frameNumber, @FrameType int frameType) {
CloseableReference<Bitmap> bitmapReference = null;
boolean drawn = false;
int nextFrameType = FRAME_TYPE_UNKNOWN;
try {
switch (frameType) {
case FRAME_TYPE_CACHED:
bitmapReference = mBitmapFrameCache.getCachedFrame(frameNumber);
drawn = drawBitmapAndCache(frameNumber, bitmapReference, canvas, FRAME_TYPE_CACHED);
nextFrameType = FRAME_TYPE_REUSED;
break;
case FRAME_TYPE_REUSED:
bitmapReference =
mBitmapFrameCache.getBitmapToReuseForFrame(frameNumber, mBitmapWidth, mBitmapHeight);
// Try to render the frame and draw on the canvas immediately after
drawn = renderFrameInBitmap(frameNumber, bitmapReference) &&
drawBitmapAndCache(frameNumber, bitmapReference, canvas, FRAME_TYPE_REUSED);
nextFrameType = FRAME_TYPE_CREATED;
break;
case FRAME_TYPE_CREATED:
try {
bitmapReference =
mPlatformBitmapFactory.createBitmap(mBitmapWidth, mBitmapHeight, mBitmapConfig);
} catch (RuntimeException e) {
return false;
}
drawn = renderFrameInBitmap(frameNumber, bitmapReference) &&
drawBitmapAndCache(frameNumber, bitmapReference, canvas, FRAME_TYPE_CREATED);
nextFrameType = FRAME_TYPE_FALLBACK;
break;
case FRAME_TYPE_FALLBACK:
//獲取給定幀編號的后備幀遍膜。 如果繪制幀的所有其他嘗試都失敗碗硬,則調(diào)用此方法腐缤。 返回的位圖例如可以是最后繪制的幀(如果有的話)。
bitmapReference = mBitmapFrameCache.getFallbackFrame(frameNumber);
drawn = drawBitmapAndCache(frameNumber, bitmapReference, canvas, FRAME_TYPE_FALLBACK);
break;
default:
return false;
}
} finally {
CloseableReference.closeSafely(bitmapReference);
}
if (drawn || nextFrameType == FRAME_TYPE_UNKNOWN) {
return drawn;
} else {
return drawFrameOrFallback(canvas, frameNumber, nextFrameType);
}
}
...
//繪制和緩存當(dāng)前幀
private boolean drawBitmapAndCache(
int frameNumber,
@Nullable CloseableReference<Bitmap> bitmapReference,
Canvas canvas,
@FrameType int frameType) {
if (!CloseableReference.isValid(bitmapReference)) {
return false;
}
//這里的bitmap是經(jīng)過native層修改像素信息后的下一幀bitmap
if (mBounds == null) {
canvas.drawBitmap(bitmapReference.get(), 0f, 0f, mPaint);
} else {
canvas.drawBitmap(bitmapReference.get(), null, mBounds, mPaint);
}
// 這里時告訴BitmapFrameCache肛响,當(dāng)前幀繪制好了岭粤,可以做響應(yīng)的緩存操作
if (frameType != FRAME_TYPE_FALLBACK) {
mBitmapFrameCache.onFrameRendered(
frameNumber,
bitmapReference,
frameType);
}
if (mFrameListener != null) {
mFrameListener.onFrameDrawn(this, frameNumber, frameType);
}
return true;
}
/**
* 嘗試在給定的bitmap上繪制當(dāng)前幀,如果繪制失敗特笋,bitmap引用會關(guān)閉并且返回false剃浇,如果繪制成功,這個bitmap就會被draw猎物,并且在繪制完成后會自動關(guān)閉
*/
private boolean renderFrameInBitmap(
int frameNumber,
@Nullable CloseableReference<Bitmap> targetBitmap) {
...
//繼續(xù)向下傳遞虎囚,渲染當(dāng)前幀
boolean frameRendered =
mBitmapFrameRenderer.renderFrame(frameNumber, targetBitmap.get());
if (!frameRendered) {
CloseableReference.closeSafely(targetBitmap);
}
return frameRendered;
}
renderFrameInBitmap() 在Bitmap上渲染當(dāng)前幀,這個方法就跟android-gif-drawable中的renderFrame()是一樣的作用蔫磨,就是在native層修改掉bitmap的像素信息淘讥,然后調(diào)用drawBitmapAndCache()
BitmapAnimationBackend::renderFrameInBitmap()---->
AnimatedDrawableBackendFrameRenderer::renderFrame()---->
AnimatedImageCompositor::renderFrame()---->
AnimatedDrawableBackendImpl::renderFrame()---->
AnimatedDrawableBackendImpl::renderImageSupportsScaling()---->
GifFrame::renderFrame()---->
GifFrame::nativeRenderFrame()---->
下面分析下最終渲染幀的地方,最終傳到了AnimatedDrawableBackend 實現(xiàn)類是AnimatedDrawableBackendImpl
private @Nullable Bitmap mTempBitmap;
@Override
public void renderFrame(int frameNumber, Canvas canvas) {
AnimatedImageFrame frame = mAnimatedImage.getFrame(frameNumber);
try {
if (mAnimatedImage.doesRenderSupportScaling()) {
renderImageSupportsScaling(canvas, frame);
} else {
renderImageDoesNotSupportScaling(canvas, frame);
}
} finally {
frame.dispose();
}
}
private void renderImageSupportsScaling(Canvas canvas, AnimatedImageFrame frame) {
....
synchronized (this) {
...
//修改mTempBitmap的像素信息
frame.renderFrame(frameWidth, frameHeight, mTempBitmap);
...
//把mTempBitmap的像素信息堤如,渲染到canvas上Bitmap的蒲列,也就是在drawFrameOrFallback傳入進來的bitmap
canvas.drawBitmap(mTempBitmap, mRenderSrcRect, mRenderDstRect, null);
}
}
gif.cpp
void GifFrame_nativeRenderFrame(
JNIEnv* pEnv,
jobject thiz,
jint width,
jint height,
jobject bitmap) {
....
uint8_t* pixels;
if (AndroidBitmap_lockPixels(pEnv, bitmap, (void**) &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
throwIllegalStateException(pEnv, "Bad bitmap");
return;
}
blitNormal(
pixels,
width,
height,
bitmapInfo.stride,
pSavedImage,
spNativeContext->spGifWrapper->getRasterBits(),
pColorMap,
spNativeContext->transparentIndex);
AndroidBitmap_unlockPixels(pEnv, bitmap);
}
根據(jù)上面的代碼可以看出來,最終也是先鎖定像素信息搀罢,然后修改蝗岖,最終unlock。
static void blitNormal(
uint8_t* pDest,
int destWidth,
int destHeight,
int destStride,
const SavedImage* pFrame,
const GifByteType* pSrcRasterBits,
const ColorMapObject* cmap,
int transparentIndex) {
GifWord copyWidth = pFrame->ImageDesc.Width;
if (copyWidth > destWidth) {
copyWidth = destWidth;
}
GifWord copyHeight = pFrame->ImageDesc.Height;
if (copyHeight > destHeight) {
copyHeight = destHeight;
}
for (; copyHeight > 0; copyHeight--) {
blitLine((PixelType32*) pDest, pSrcRasterBits, cmap, transparentIndex, copyWidth);
pSrcRasterBits += pFrame->ImageDesc.Width;
pDest += destStride;
}
}
static void blitLine(
PixelType32* pDest,
const GifByteType* pSource,
const ColorMapObject* pColorMap,
int transparentIndex,
int width) {
//std::transform 應(yīng)用給定的函數(shù)到范圍并存儲結(jié)果于始于 d_first 的另一范圍榔至。
// 1) 應(yīng)用一元函數(shù) [=] (uint8_t color) 到 [pSource, pSource + width) 所定義的范圍亏娜。
/**
*對于一元操作粗井,將op應(yīng)用于[first1, last1]范圍內(nèi)的每個元素筝闹,并將每個操作返回的值存儲在以result開頭的范圍內(nèi)奥邮。給定的op將被連續(xù)調(diào)用last1-first1+1次。op可以是函數(shù)指針或函數(shù)對象或lambda表達式枫弟。
*/
std::transform(pSource, pSource + width, pDest, [=] (uint8_t color) {
if (color == transparentIndex) {
return TRANSPARENT;
}
return getColorFromTable(color, pColorMap);
});
}
這是最終繪制當(dāng)前幀的地方邢享,在AnimatedDrawableBackendImpl類中,有個屬性 mTempBitmap媒区,其實這個bitmap就是傳給native層驼仪,修改像素信息為下一幀的像素后掸犬,響應(yīng)到Java層的袜漩。最后把MtempBitmap的像素信息渲染到canvas上的Bitmap,也就是在drawFrameOrFallback傳入進來的bitmap
然后再回到BitmapAnimationBackend::drawFrameOrFallback()湾碎,renderFrameInBitmap方法走完了宙攻,還需要繼續(xù)走drawBitmapAndCache()
private boolean drawBitmapAndCache(
int frameNumber,
@Nullable CloseableReference<Bitmap> bitmapReference,
Canvas canvas,
@FrameType int frameType) {
if (!CloseableReference.isValid(bitmapReference)) {
return false;
}
//這里bitmap是承載了經(jīng)過native層修改像素信息后的bitmap
if (mBounds == null) {
canvas.drawBitmap(bitmapReference.get(), 0f, 0f, mPaint);
} else {
canvas.drawBitmap(bitmapReference.get(), null, mBounds, mPaint);
}
// 這里傳給了BitmapFrameCache,當(dāng)前幀繪制好了介褥,可以做響應(yīng)的緩存操作
if (frameType != FRAME_TYPE_FALLBACK) {
mBitmapFrameCache.onFrameRendered(
frameNumber,
bitmapReference,
frameType);
}
if (mFrameListener != null) {
mFrameListener.onFrameDrawn(this, frameNumber, frameType);
}
return true;
}
BitmapFrameCache
FrescoFramCache:Fresco加載Gif默認的BitmapFrameCache座掘,AnimatedFrameCache是操作幀緩存的
KeepLastFrameCache:保存上一幀的Bitmap递惋,用于復(fù)用
NoOpCache: 無操作緩存,就是沒緩存
BitmapFramCahce的創(chuàng)建
BitmapFrameCache創(chuàng)建的地方在:
AnimatedFactoryV2Impl
public class AnimatedFactoryV2Impl implements AnimatedFactory {
private static final int NUMBER_OF_FRAMES_TO_PREPARE = 3;
...
private DrawableFactory createDrawableFactory() {
//傳遞給ExperimentalBitmapAnimationDrawableFactory溢陪,設(shè)置BitmapFrameCache的實現(xiàn)類
Supplier<Integer> cachingStrategySupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return ExperimentalBitmapAnimationDrawableFactory.CACHING_STRATEGY_NO_CACHE;
}
};
final SerialExecutorService serialExecutorServiceForFramePreparing =
new DefaultSerialExecutorService(mExecutorSupplier.forDecode());
//設(shè)置動圖的預(yù)先加載的幀數(shù)萍虽,默認是3幀
Supplier<Integer> numberOfFramesToPrepareSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return NUMBER_OF_FRAMES_TO_PREPARE;
}
};
return new ExperimentalBitmapAnimationDrawableFactory(
getAnimatedDrawableBackendProvider(),
UiThreadImmediateExecutorService.getInstance(),
serialExecutorServiceForFramePreparing,
RealtimeSinceBootClock.get(),
mPlatformBitmapFactory,
mBackingCache,
cachingStrategySupplier,
numberOfFramesToPrepareSupplier);
}
...
}
public class ExperimentalBitmapAnimationDrawableFactory implements DrawableFactory {
...
@Override
public AnimatedDrawable2 createDrawable(CloseableImage image) {
return new AnimatedDrawable2(
createAnimationBackend(
((CloseableAnimatedImage) image).getImageResult()));
}
private AnimationBackend createAnimationBackend(AnimatedImageResult animatedImageResult) {
AnimatedDrawableBackend animatedDrawableBackend =
createAnimatedDrawableBackend(animatedImageResult);
BitmapFrameCache bitmapFrameCache = createBitmapFrameCache(animatedImageResult);//創(chuàng)建BitmapFrameCache
BitmapFrameRenderer bitmapFrameRenderer =
new AnimatedDrawableBackendFrameRenderer(bitmapFrameCache, animatedDrawableBackend);
int numberOfFramesToPrefetch = mNumberOfFramesToPrepareSupplier.get();
BitmapFramePreparationStrategy bitmapFramePreparationStrategy = null;
BitmapFramePreparer bitmapFramePreparer = null;
if (numberOfFramesToPrefetch > 0) {
bitmapFramePreparationStrategy =
new FixedNumberBitmapFramePreparationStrategy(numberOfFramesToPrefetch);
bitmapFramePreparer = createBitmapFramePreparer(bitmapFrameRenderer);
}
BitmapAnimationBackend bitmapAnimationBackend = new BitmapAnimationBackend(
mPlatformBitmapFactory,
bitmapFrameCache,
new AnimatedDrawableBackendAnimationInformation(animatedDrawableBackend),
bitmapFrameRenderer,
bitmapFramePreparationStrategy,
bitmapFramePreparer);
return AnimationBackendDelegateWithInactivityCheck.createForBackend(
bitmapAnimationBackend,
mMonotonicClock,
mScheduledExecutorServiceForUiThread);
}
...
private BitmapFrameCache createBitmapFrameCache(AnimatedImageResult animatedImageResult) {
switch (mCachingStrategySupplier.get()) {
case CACHING_STRATEGY_FRESCO_CACHE:
//創(chuàng)建FrescoFrameCache,并且設(shè)置可復(fù)用形真,即mEnableBitmapReusing為true
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), true); case CACHING_STRATEGY_FRESCO_CACHE_NO_REUSING:
//創(chuàng)建FrescoFrameCache杉编,并且設(shè)置不可復(fù)用,即mEnableBitmapReusing為false
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), false);
case CACHING_STRATEGY_KEEP_LAST_CACHE:
return new KeepLastFrameCache();//保存上一幀的bitmap以復(fù)用
case CACHING_STRATEGY_NO_CACHE:
default:
return new NoOpCache();//無緩存操作
}
}
...
}
FrescoFrameCache
在Fresco中的BitmapFrameCache默認的是的FrescoFrameCache咆霜,并且在KeepLastFrameCache邓馒,NoOpCache和FrescoFrameCache中,最復(fù)雜的就是FrescoFrameCache蛾坯,把FrescoFrameCache分析清楚了光酣,剩下的兩個就會都明白了
public class FrescoFrameCache implements BitmapFrameCache {
//執(zhí)行幀緩存的類
private final AnimatedFrameCache mAnimatedFrameCache;
private final boolean mEnableBitmapReusing;
@GuardedBy("this")
private final SparseArray<CloseableReference<CloseableImage>> mPreparedPendingFrames;//存儲準備的后面的幀
@GuardedBy("this")
@Nullable
private CloseableReference<CloseableImage> mLastRenderedItem;//上一幀的圖片
public FrescoFrameCache(AnimatedFrameCache animatedFrameCache, boolean enableBitmapReusing) {
mAnimatedFrameCache = animatedFrameCache;
mEnableBitmapReusing = enableBitmapReusing;
mPreparedPendingFrames = new SparseArray<>();
}
//根據(jù)frameNumber獲取對應(yīng)幀的Bitmap
@Nullable
@Override
public synchronized CloseableReference<Bitmap> getCachedFrame(int frameNumber) {
return convertToBitmapReferenceAndClose(mAnimatedFrameCache.get(frameNumber));
}
//根據(jù)frameNumber獲取對應(yīng)后備幀
@Nullable
@Override
public synchronized CloseableReference<Bitmap> getFallbackFrame(int frameNumber) {
return convertToBitmapReferenceAndClose(CloseableReference.cloneOrNull(mLastRenderedItem));
}
//根據(jù)frameNumber獲取對應(yīng)幀的復(fù)用bitmap
@Nullable
@Override
public synchronized CloseableReference<Bitmap> getBitmapToReuseForFrame(
int frameNumber,
int width,
int height) {
if (!mEnableBitmapReusing) {//是否可復(fù)用,允許復(fù)用的話才會往下走
return null;
}
return convertToBitmapReferenceAndClose(mAnimatedFrameCache.getForReuse());
}
//內(nèi)存緩存是否含有對應(yīng)frameNumber幀的圖片
@Override
public synchronized boolean contains(int frameNumber) {
return mAnimatedFrameCache.contains(frameNumber);
}
@Override
public synchronized int getSizeInBytes() {
// This currently does not include the size of the animated frame cache
return getBitmapSizeBytes(mLastRenderedItem) + getPreparedPendingFramesSizeBytes();
}
//清除緩存脉课,把對應(yīng)的引用關(guān)閉救军,特別是準備的幀
@Override
public synchronized void clear() {
CloseableReference.closeSafely(mLastRenderedItem);
mLastRenderedItem = null;
for (int i = 0; i < mPreparedPendingFrames.size(); i++) {
CloseableReference.closeSafely(mPreparedPendingFrames.valueAt(i));
}
mPreparedPendingFrames.clear();
}
//當(dāng)對應(yīng)幀在繪制后,執(zhí)行響應(yīng)的緩存操作
@Override
public synchronized void onFrameRendered(
int frameNumber,
CloseableReference<Bitmap> bitmapReference,
@BitmapAnimationBackend.FrameType int frameType) {
Preconditions.checkNotNull(bitmapReference);
//關(guān)閉存儲在mPreparedPendingFrames的當(dāng)前幀圖片的引用倘零,便于內(nèi)存緩存可以回收
removePreparedReference(frameNumber);
//緩存對應(yīng)幀
CloseableReference<CloseableImage> closableReference = null;
try {
closableReference = createImageReference(bitmapReference);
if (closableReference != null) {
CloseableReference.closeSafely(mLastRenderedItem);
mLastRenderedItem = mAnimatedFrameCache.cache(frameNumber, closableReference);
}
} finally {
CloseableReference.closeSafely(closableReference);
}
}
//在提前準備幀的時候缤言,把前面的幾幀緩存起來,便于渲染時直接從內(nèi)存中獲取视事,提升性能
@Override
public synchronized void onFramePrepared(
int frameNumber,
CloseableReference<Bitmap> bitmapReference,
@BitmapAnimationBackend.FrameType int frameType) {
Preconditions.checkNotNull(bitmapReference);
CloseableReference<CloseableImage> closableReference = null;
try {
closableReference = createImageReference(bitmapReference);
if (closableReference == null) {
return;
}
CloseableReference<CloseableImage> newReference =
mAnimatedFrameCache.cache(frameNumber, closableReference);
if (CloseableReference.isValid(newReference)) {
CloseableReference<CloseableImage> oldReference = mPreparedPendingFrames.get(frameNumber);
CloseableReference.closeSafely(oldReference);
// For performance reasons, we don't clone the reference and close the original one
// 為了提升性能胆萧,防止對應(yīng)的幀從內(nèi)存中移除,所以存儲在mPreparedPendingFrames中俐东,保持對后面幀的引用
mPreparedPendingFrames.put(frameNumber, newReference);
...
}
} finally {
CloseableReference.closeSafely(closableReference);
}
}
...
private synchronized int getPreparedPendingFramesSizeBytes() {
int size = 0;
for (int i = 0; i < mPreparedPendingFrames.size(); i++) {
size += getBitmapSizeBytes(mPreparedPendingFrames.valueAt(i));
}
return size;
}
private synchronized void removePreparedReference(int frameNumber) {
CloseableReference<CloseableImage> existingPendingReference =
mPreparedPendingFrames.get(frameNumber);
if (existingPendingReference != null) {
mPreparedPendingFrames.delete(frameNumber);
CloseableReference.closeSafely(existingPendingReference);
...
}
}
//把對應(yīng)的CloseableReference<CloseableImage> 轉(zhuǎn)換成CloseableReference<Bitmap>
@VisibleForTesting
@Nullable
static CloseableReference<Bitmap> convertToBitmapReferenceAndClose(
final @Nullable CloseableReference<CloseableImage> closeableImage) {
try {
if (CloseableReference.isValid(closeableImage) &&
closeableImage.get() instanceof CloseableStaticBitmap) {
CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) closeableImage.get();
if (closeableStaticBitmap != null) {
return closeableStaticBitmap.cloneUnderlyingBitmapReference();
}
}
return null;
} finally {
CloseableReference.closeSafely(closeableImage);
}
}
...
根據(jù)上面的代碼跌穗,主要的工作是在AnimatedFrameCache中完成響應(yīng)的緩存的操作,繼續(xù)分析AnimatedFrameCache
public class AnimatedFrameCache {
//動圖中對應(yīng)幀在內(nèi)存緩存中的CacheKey虏辫,根據(jù)幀索引和mImageCacheKey生成
@VisibleForTesting
static class FrameKey implements CacheKey {
private final CacheKey mImageCacheKey;
private final int mFrameIndex;
public FrameKey(CacheKey imageCacheKey, int frameIndex) {
mImageCacheKey = imageCacheKey;
mFrameIndex = frameIndex;
}
...
}
private final CacheKey mImageCacheKey;//動圖的CacheKey
private final CountingMemoryCache<CacheKey, CloseableImage> mBackingCache;//Fresco默認的內(nèi)存緩存蚌吸,就是Bitmapmemory,從外面?zhèn)鬟M來的
private final CountingMemoryCache.EntryStateObserver<CacheKey> mEntryStateObserver;////監(jiān)聽緩存的Entry的狀態(tài)變化砌庄,Entry是CountingMemoryCache對CloseableReference 緩存value的包裝
@GuardedBy("this")
private final LinkedHashSet<CacheKey> mFreeItemsPool;//在內(nèi)存緩存中已經(jīng)沒有對象引用的值羹唠,存儲可以復(fù)用的幀的CacheKey
public AnimatedFrameCache(
CacheKey imageCacheKey,
final CountingMemoryCache<CacheKey, CloseableImage> backingCache) {
mImageCacheKey = imageCacheKey;
mBackingCache = backingCache;
mFreeItemsPool = new LinkedHashSet<>();
mEntryStateObserver = new CountingMemoryCache.EntryStateObserver<CacheKey>() {
@Override
public void onExclusivityChanged(CacheKey key, boolean isExclusive) {
AnimatedFrameCache.this.onReusabilityChange(key, isExclusive);
}
};
}
public synchronized void onReusabilityChange(CacheKey key, boolean isReusable) {
if (isReusable) {
mFreeItemsPool.add(key);
} else {
mFreeItemsPool.remove(key);
}
}
//根據(jù)FrameKey往內(nèi)存緩存中存儲
@Nullable
public CloseableReference<CloseableImage> cache(
int frameIndex,
CloseableReference<CloseableImage> imageRef) {
return mBackingCache.cache(keyFor(frameIndex), imageRef, mEntryStateObserver);
}
//獲取對應(yīng)幀的緩存
@Nullable
public CloseableReference<CloseableImage> get(int frameIndex) {
return mBackingCache.get(keyFor(frameIndex));
}
//查詢是否含有對應(yīng)幀
public boolean contains(int frameIndex) {
return mBackingCache.contains(keyFor(frameIndex));
}
//獲取可以復(fù)用的Bitmap,提高性能
@Nullable
public CloseableReference<CloseableImage> getForReuse() {
while (true) {
CacheKey key = popFirstFreeItemKey();
if (key == null) {
return null;
}
CloseableReference<CloseableImage> imageRef = mBackingCache.reuse(key);
if (imageRef != null) {
return imageRef;
}
}
}
//找到可以復(fù)用的CacheKey娄昆,然后從內(nèi)存緩存中取出對應(yīng)幀的圖片
@Nullable
private synchronized CacheKey popFirstFreeItemKey() {
CacheKey cacheKey = null;
Iterator<CacheKey> iterator = mFreeItemsPool.iterator();
if (iterator.hasNext()) {
cacheKey = iterator.next();
iterator.remove();
}
return cacheKey;
}
//根據(jù)索引生成FrameKey
private FrameKey keyFor(int frameIndex) {
return new FrameKey(mImageCacheKey, frameIndex);
}
}
根據(jù)上面的分析佩微,緩存這一塊的邏輯已經(jīng)比較清晰了,就是在繪制的對應(yīng)幀的時候萌焰,會傳遞給FrescoFrameCache哺眯,F(xiàn)rescoFrameCache會傳遞給AnimatedFrameCache,并且執(zhí)行相應(yīng)的緩存扒俯,最終的存儲位置就是Fresco的bitmap緩存奶卓。還有一個很重要的邏輯時復(fù)用Bitmap一疯,這塊的邏輯是會在BitmapAnimationBackend::drawFrameOrFallback()中,復(fù)用的Bitmap其實就是在內(nèi)存緩存中符合條件的沒有其他對象引用的bitmap夺姑。
BitmapFramePreparer
在動圖的渲染的過程中墩邀,繪制和緩存的操作說完了,下面還有一個比較重要的步驟盏浙,就是準備接下來的幾幀磕蒲,下面先找到準備的入口,就是在BitmapAnimationBackend::drawFrame()中只盹,在drawFrameOrFallback 代碼就不貼了辣往,自己找一下
首先看一下BitmapFramePreparer創(chuàng)建的地方,在ExperimentalBitmapAnimationDrawableFactory
ExperimentalBitmapAnimationDrawableFactory
private AnimationBackend createAnimationBackend(AnimatedImageResult animatedImageResult) {
...
int numberOfFramesToPrefetch = mNumberOfFramesToPrepareSupplier.get();//外面?zhèn)鬟M來的殖卑,默認是3幀
BitmapFramePreparationStrategy bitmapFramePreparationStrategy = null;
BitmapFramePreparer bitmapFramePreparer = null;
if (numberOfFramesToPrefetch > 0) {//如果提前準備的幀數(shù)大于0站削,才會創(chuàng)建bitmapFramePreparationStrategy和BitmapFramePreparer
bitmapFramePreparationStrategy =
new FixedNumberBitmapFramePreparationStrategy(numberOfFramesToPrefetch);
bitmapFramePreparer = createBitmapFramePreparer(bitmapFrameRenderer);
}
...
}
private BitmapFramePreparer createBitmapFramePreparer(BitmapFrameRenderer bitmapFrameRenderer) {
return new DefaultBitmapFramePreparer(
mPlatformBitmapFactory,
bitmapFrameRenderer,
Bitmap.Config.ARGB_8888,
mExecutorServiceForFramePreparing);
}
上面設(shè)計到兩個類,BitmapFramePreparationStrategy和BitmapFramePreparer孵稽,一個是策略许起,一個是具體操作,下面繼續(xù)分析響應(yīng)的類
public class FixedNumberBitmapFramePreparationStrategy implements BitmapFramePreparationStrategy {
...
private static final int DEFAULT_FRAMES_TO_PREPARE = 3;
private final int mFramesToPrepare;
public FixedNumberBitmapFramePreparationStrategy() {
this(DEFAULT_FRAMES_TO_PREPARE);
}
public FixedNumberBitmapFramePreparationStrategy(int framesToPrepare) {
mFramesToPrepare = framesToPrepare;
}
@Override
public void prepareFrames(
BitmapFramePreparer bitmapFramePreparer,
BitmapFrameCache bitmapFrameCache,
AnimationBackend animationBackend,
int lastDrawnFrameNumber) {
for (int i = 1; i <= mFramesToPrepare; i++) {
int nextFrameNumber = (lastDrawnFrameNumber + i) % animationBackend.getFrameCount();
//調(diào)用bitmapFramePreparer.prepareFrame(),做提前準備幀的操作
if (!bitmapFramePreparer.prepareFrame(
bitmapFrameCache,
animationBackend,
nextFrameNumber)) {
return;
}
}
}
}
public class DefaultBitmapFramePreparer implements BitmapFramePreparer {
...
@Override
public boolean prepareFrame(
BitmapFrameCache bitmapFrameCache,
AnimationBackend animationBackend,
int frameNumber) {
...
Runnable frameDecodeRunnable = new FrameDecodeRunnable(
animationBackend,
bitmapFrameCache,
frameNumber,
frameId);
mPendingFrameDecodeJobs.put(frameId, frameDecodeRunnable);
//執(zhí)行frameDecodeRunnable
mExecutorService.execute(frameDecodeRunnable);
}
return true;
}
...
}
private class FrameDecodeRunnable implements Runnable {
private final BitmapFrameCache mBitmapFrameCache;
private final AnimationBackend mAnimationBackend;
private final int mFrameNumber;
private final int mHashCode;
public FrameDecodeRunnable(
AnimationBackend animationBackend,
BitmapFrameCache bitmapFrameCache,
int frameNumber,
int hashCode) {
mAnimationBackend = animationBackend;
mBitmapFrameCache = bitmapFrameCache;
mFrameNumber = frameNumber;
mHashCode = hashCode;
}
@Override
public void run() {
try {
...
// 準備幀
if (prepareFrameAndCache(mFrameNumber, BitmapAnimationBackend.FRAME_TYPE_REUSED)) {
FLog.v(TAG, "Prepared frame frame %d.", mFrameNumber);
} else {
FLog.e(TAG, "Could not prepare frame %d.", mFrameNumber);
}
} finally {
synchronized (mPendingFrameDecodeJobs) {
mPendingFrameDecodeJobs.remove(mHashCode);
}
}
}
//這里跟BitmapAnimationBackend::drawFramorFallback()邏輯類似菩鲜,準備幀并且緩存园细,跟BitmapAnimationBackend::drawBitmapAndCache()類似,就不再分析了
private boolean prepareFrameAndCache(
int frameNumber,
@BitmapAnimationBackend.FrameType int frameType) {
CloseableReference<Bitmap> bitmapReference = null;
boolean created;
int nextFrameType;
try {
switch (frameType) {
case BitmapAnimationBackend.FRAME_TYPE_REUSED:
bitmapReference =
mBitmapFrameCache.getBitmapToReuseForFrame(
frameNumber,
mAnimationBackend.getIntrinsicWidth(),
mAnimationBackend.getIntrinsicHeight());
nextFrameType = BitmapAnimationBackend.FRAME_TYPE_CREATED;
break;
case BitmapAnimationBackend.FRAME_TYPE_CREATED:
try {
bitmapReference =
mPlatformBitmapFactory.createBitmap(
mAnimationBackend.getIntrinsicWidth(),
mAnimationBackend.getIntrinsicHeight(),
mBitmapConfig);
} catch (RuntimeException e) {
...
return false;
}
nextFrameType = BitmapAnimationBackend.FRAME_TYPE_UNKNOWN;
break;
default:
return false;
}
//嘗試渲染并且緩存接校,這里的渲染不會幀的繪制猛频,只是把bitmapReference對應(yīng)的Bitmap的像素信息,變成之后的幾幀蛛勉,并且緩存起來
created = renderFrameAndCache(frameNumber, bitmapReference, frameType);
} finally {
CloseableReference.closeSafely(bitmapReference);
}
if (created || nextFrameType == BitmapAnimationBackend.FRAME_TYPE_UNKNOWN) {
return created;
} else {
return prepareFrameAndCache(frameNumber, nextFrameType);
}
}
private boolean renderFrameAndCache(
int frameNumber,
CloseableReference<Bitmap> bitmapReference,
@BitmapAnimationBackend.FrameType int frameType) {
...
//改變像素信息鹿寻,
if (!mBitmapFrameRenderer.renderFrame(frameNumber, bitmapReference.get())) {
return false;
}
//緩存
synchronized (mPendingFrameDecodeJobs) {
mBitmapFrameCache.onFramePrepared(mFrameNumber, bitmapReference, frameType);
}
return true;
}
}
}
提前準備幀的邏輯也清楚了,到這里Fresco中動圖的加載邏輯也分析清楚了
BitmapFrameRenderer::
...
private AnimatedImageCompositor mAnimatedImageCompositor;
...
@Override
public boolean renderFrame(int frameNumber, Bitmap targetBitmap) {
try {
mAnimatedImageCompositor.renderFrame(frameNumber, targetBitmap);
} catch (IllegalStateException exception) {
return false;
}
return true;
}
...
AnimatedImageCompositor::
public void renderFrame(int frameNumber, Bitmap bitmap) {
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
// If blending is required, prepare the canvas with the nearest cached frame.
int nextIndex;
if (!isKeyFrame(frameNumber)) {
// Blending is required. nextIndex points to the next index to render onto the canvas.
nextIndex = prepareCanvasWithClosestCachedFrame(frameNumber - 1, canvas);
} else {
// Blending isn't required. Start at the frame we're trying to render.
nextIndex = frameNumber;
}
// 這里主要涉及到透明度的混合問題
for (int index = nextIndex; index < frameNumber; index++) {
AnimatedDrawableFrameInfo frameInfo = mAnimatedDrawableBackend.getFrameInfo(index);
DisposalMethod disposalMethod = frameInfo.disposalMethod;
if (disposalMethod == DisposalMethod.DISPOSE_TO_PREVIOUS) {
continue;
}
if (frameInfo.blendOperation == BlendOperation.NO_BLEND) {
disposeToBackground(canvas, frameInfo);
}
mAnimatedDrawableBackend.renderFrame(index, canvas);
mCallback.onIntermediateResult(index, bitmap);
if (disposalMethod == DisposalMethod.DISPOSE_TO_BACKGROUND) {
disposeToBackground(canvas, frameInfo);
}
}
AnimatedDrawableFrameInfo frameInfo = mAnimatedDrawableBackend.getFrameInfo(frameNumber);
if (frameInfo.blendOperation == BlendOperation.NO_BLEND) {
disposeToBackground(canvas, frameInfo);
}
//最終诽凌,繪制當(dāng)前幀
mAnimatedDrawableBackend.renderFrame(frameNumber, canvas);
}