Fresco Gif加载解析
普通Gif加载
val controller = Fresco.newDraweeControllerBuilder()
.setAutoPlayAnimations(true)//就是添加了这一句代码,就可以播放动图
.setImageRequest(request)
.setOldController(sivBanner?.controller)
.build()
sivBanner?.controller = controller
先看性能表现效果图:
从上面三张图可以看的出来普通的加载会频繁GC,这种情况比较严重,并且CPU使用率比较高,50%左右,并且通过Dump内存的分布可以看出来,Fresco的缓存了太多的图片,并且占用的是BitmapMemory,这3种表现在加载Gif的时候是无法接受的。我们希望达到的效果是CPU的使用率能够降下来,并且尽量少的占用Fresco的内存缓存,如果想达到目标,只能去看看Fresco的Gif加载的代码。
这里再提一点,为什么会想到去优化Fresco的Gif加载。因为看到android-gif-drawable的表现后,发现android-gif-drawable其实是依赖于GifLib来做的底层支撑,而Fresco也是基于GifLib。因为两个框架底层用的是一样的,那么从理论上来说,Fresco就应该有优化的空间。
Gif加载流程分析
先从头分析下
AbstractDraweeController::onNewResultInternal()会把生成的Drawable设置给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);
//设置帧缓存
BitmapFrameCache bitmapFrameCache = createBitmapFrameCache(animatedImageResult);
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);
}
最终设置给DraweeHierachy的是AnimatedDrawable2,
先看下AnmatedDrawable2
AnimatedDrawable2
@Override
public void draw(Canvas canvas) {
.....
// 绘制当前帧,把绘制的操作交给了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) {
...
//绘制当前帧
boolean drawn = drawFrameOrFallback(canvas, frameNumber, FRAME_TYPE_CACHED);
...
//准备下面的几帧
if (mBitmapFramePreparationStrategy != null && mBitmapFramePreparer != null) {
mBitmapFramePreparationStrategy.prepareFrames(
mBitmapFramePreparer,
mBitmapFrameCache,
this,
frameNumber);
}
return drawn;
}
//drawFrameOrFallback,绘制当前帧或者后备帧
//1. FRAME_TYPE_CACHED 首先尝试从帧缓存里面取当前帧的bitmap,如果取到就绘制和缓存当前帧
//2. FRAME_TYPE_REUSED 尝试获取可以重用的bitmap,获取到就绘制和缓存当前帧
//3. FRAME_TYPE_CREATED 创建Bitmap,然后绘制和缓存当前帧
//4. FRAME_TYPE_FALLBACK 获取给定帧编号的后备帧。 如果绘制帧的所有其他尝试都失败,则调用此方法。 返回的Bitmap可以是最后绘制的帧(如果有的话)
private boolean drawFrameOrFallback(Canvas canvas, int frameNumber, @FrameType int frameType) {
CloseableReference 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:
//获取给定帧编号的后备帧。 如果绘制帧的所有其他尝试都失败,则调用此方法。 返回的位图例如可以是最后绘制的帧(如果有的话)。
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);
}
}
...
//绘制和缓存当前帧
private boolean drawBitmapAndCache(
int frameNumber,
@Nullable CloseableReference bitmapReference,
Canvas canvas,
@FrameType int frameType) {
if (!CloseableReference.isValid(bitmapReference)) {
return false;
}
//这里的bitmap是经过native层修改像素信息后的下一帧bitmap
if (mBounds == null) {
canvas.drawBitmap(bitmapReference.get(), 0f, 0f, mPaint);
} else {
canvas.drawBitmap(bitmapReference.get(), null, mBounds, mPaint);
}
// 这里时告诉BitmapFrameCache,当前帧绘制好了,可以做响应的缓存操作
if (frameType != FRAME_TYPE_FALLBACK) {
mBitmapFrameCache.onFrameRendered(
frameNumber,
bitmapReference,
frameType);
}
if (mFrameListener != null) {
mFrameListener.onFrameDrawn(this, frameNumber, frameType);
}
return true;
}
/**
* 尝试在给定的bitmap上绘制当前帧,如果绘制失败,bitmap引用会关闭并且返回false,如果绘制成功,这个bitmap就会被draw,并且在绘制完成后会自动关闭
*/
private boolean renderFrameInBitmap(
int frameNumber,
@Nullable CloseableReference targetBitmap) {
...
//继续向下传递,渲染当前帧
boolean frameRendered =
mBitmapFrameRenderer.renderFrame(frameNumber, targetBitmap.get());
if (!frameRendered) {
CloseableReference.closeSafely(targetBitmap);
}
return frameRendered;
}
renderFrameInBitmap() 在Bitmap上渲染当前帧,这个方法就跟android-gif-drawable中的renderFrame()是一样的作用,就是在native层修改掉bitmap的像素信息,然后调用drawBitmapAndCache()
BitmapAnimationBackend::renderFrameInBitmap()---->
AnimatedDrawableBackendFrameRenderer::renderFrame()---->
AnimatedImageCompositor::renderFrame()---->
AnimatedDrawableBackendImpl::renderFrame()---->
AnimatedDrawableBackendImpl::renderImageSupportsScaling()---->
GifFrame::renderFrame()---->
GifFrame::nativeRenderFrame()---->
下面分析下最终渲染帧的地方,最终传到了AnimatedDrawableBackend 实现类是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);
}
根据上面的代码可以看出来,最终也是先锁定像素信息,然后修改,最终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 应用给定的函数到范围并存储结果于始于 d_first 的另一范围。
// 1) 应用一元函数 [=] (uint8_t color) 到 [pSource, pSource + width) 所定义的范围。
/**
*对于一元操作,将op应用于[first1, last1]范围内的每个元素,并将每个操作返回的值存储在以result开头的范围内。给定的op将被连续调用last1-first1+1次。op可以是函数指针或函数对象或lambda表达式。
*/
std::transform(pSource, pSource + width, pDest, [=] (uint8_t color) {
if (color == transparentIndex) {
return TRANSPARENT;
}
return getColorFromTable(color, pColorMap);
});
}
这是最终绘制当前帧的地方,在AnimatedDrawableBackendImpl类中,有个属性 mTempBitmap,其实这个bitmap就是传给native层,修改像素信息为下一帧的像素后,响应到Java层的。最后把MtempBitmap的像素信息渲染到canvas上的Bitmap,也就是在drawFrameOrFallback传入进来的bitmap
然后再回到BitmapAnimationBackend::drawFrameOrFallback(),renderFrameInBitmap方法走完了,还需要继续走drawBitmapAndCache()
private boolean drawBitmapAndCache(
int frameNumber,
@Nullable CloseableReference bitmapReference,
Canvas canvas,
@FrameType int frameType) {
if (!CloseableReference.isValid(bitmapReference)) {
return false;
}
//这里bitmap是承载了经过native层修改像素信息后的bitmap
if (mBounds == null) {
canvas.drawBitmap(bitmapReference.get(), 0f, 0f, mPaint);
} else {
canvas.drawBitmap(bitmapReference.get(), null, mBounds, mPaint);
}
// 这里传给了BitmapFrameCache,当前帧绘制好了,可以做响应的缓存操作
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,用于复用
NoOpCache: 无操作缓存,就是没缓存
BitmapFramCahce的创建
BitmapFrameCache创建的地方在:
AnimatedFactoryV2Impl
public class AnimatedFactoryV2Impl implements AnimatedFactory {
private static final int NUMBER_OF_FRAMES_TO_PREPARE = 3;
...
private DrawableFactory createDrawableFactory() {
//传递给ExperimentalBitmapAnimationDrawableFactory,设置BitmapFrameCache的实现类
Supplier cachingStrategySupplier = new Supplier() {
@Override
public Integer get() {
return ExperimentalBitmapAnimationDrawableFactory.CACHING_STRATEGY_NO_CACHE;
}
};
final SerialExecutorService serialExecutorServiceForFramePreparing =
new DefaultSerialExecutorService(mExecutorSupplier.forDecode());
//设置动图的预先加载的帧数,默认是3帧
Supplier numberOfFramesToPrepareSupplier = new Supplier() {
@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);//创建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:
//创建FrescoFrameCache,并且设置可复用,即mEnableBitmapReusing为true
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), true); case CACHING_STRATEGY_FRESCO_CACHE_NO_REUSING:
//创建FrescoFrameCache,并且设置不可复用,即mEnableBitmapReusing为false
return new FrescoFrameCache(createAnimatedFrameCache(animatedImageResult), false);
case CACHING_STRATEGY_KEEP_LAST_CACHE:
return new KeepLastFrameCache();//保存上一帧的bitmap以复用
case CACHING_STRATEGY_NO_CACHE:
default:
return new NoOpCache();//无缓存操作
}
}
...
}
FrescoFrameCache
在Fresco中的BitmapFrameCache默认的是的FrescoFrameCache,并且在KeepLastFrameCache,NoOpCache和FrescoFrameCache中,最复杂的就是FrescoFrameCache,把FrescoFrameCache分析清楚了,剩下的两个就会都明白了
public class FrescoFrameCache implements BitmapFrameCache {
//执行帧缓存的类
private final AnimatedFrameCache mAnimatedFrameCache;
private final boolean mEnableBitmapReusing;
@GuardedBy("this")
private final SparseArray> mPreparedPendingFrames;//存储准备的后面的帧
@GuardedBy("this")
@Nullable
private CloseableReference mLastRenderedItem;//上一帧的图片
public FrescoFrameCache(AnimatedFrameCache animatedFrameCache, boolean enableBitmapReusing) {
mAnimatedFrameCache = animatedFrameCache;
mEnableBitmapReusing = enableBitmapReusing;
mPreparedPendingFrames = new SparseArray<>();
}
//根据frameNumber获取对应帧的Bitmap
@Nullable
@Override
public synchronized CloseableReference getCachedFrame(int frameNumber) {
return convertToBitmapReferenceAndClose(mAnimatedFrameCache.get(frameNumber));
}
//根据frameNumber获取对应后备帧
@Nullable
@Override
public synchronized CloseableReference getFallbackFrame(int frameNumber) {
return convertToBitmapReferenceAndClose(CloseableReference.cloneOrNull(mLastRenderedItem));
}
//根据frameNumber获取对应帧的复用bitmap
@Nullable
@Override
public synchronized CloseableReference getBitmapToReuseForFrame(
int frameNumber,
int width,
int height) {
if (!mEnableBitmapReusing) {//是否可复用,允许复用的话才会往下走
return null;
}
return convertToBitmapReferenceAndClose(mAnimatedFrameCache.getForReuse());
}
//内存缓存是否含有对应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();
}
//清除缓存,把对应的引用关闭,特别是准备的帧
@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();
}
//当对应帧在绘制后,执行响应的缓存操作
@Override
public synchronized void onFrameRendered(
int frameNumber,
CloseableReference bitmapReference,
@BitmapAnimationBackend.FrameType int frameType) {
Preconditions.checkNotNull(bitmapReference);
//关闭存储在mPreparedPendingFrames的当前帧图片的引用,便于内存缓存可以回收
removePreparedReference(frameNumber);
//缓存对应帧
CloseableReference closableReference = null;
try {
closableReference = createImageReference(bitmapReference);
if (closableReference != null) {
CloseableReference.closeSafely(mLastRenderedItem);
mLastRenderedItem = mAnimatedFrameCache.cache(frameNumber, closableReference);
}
} finally {
CloseableReference.closeSafely(closableReference);
}
}
//在提前准备帧的时候,把前面的几帧缓存起来,便于渲染时直接从内存中获取,提升性能
@Override
public synchronized void onFramePrepared(
int frameNumber,
CloseableReference bitmapReference,
@BitmapAnimationBackend.FrameType int frameType) {
Preconditions.checkNotNull(bitmapReference);
CloseableReference closableReference = null;
try {
closableReference = createImageReference(bitmapReference);
if (closableReference == null) {
return;
}
CloseableReference newReference =
mAnimatedFrameCache.cache(frameNumber, closableReference);
if (CloseableReference.isValid(newReference)) {
CloseableReference oldReference = mPreparedPendingFrames.get(frameNumber);
CloseableReference.closeSafely(oldReference);
// For performance reasons, we don't clone the reference and close the original one
// 为了提升性能,防止对应的帧从内存中移除,所以存储在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 existingPendingReference =
mPreparedPendingFrames.get(frameNumber);
if (existingPendingReference != null) {
mPreparedPendingFrames.delete(frameNumber);
CloseableReference.closeSafely(existingPendingReference);
...
}
}
//把对应的CloseableReference 转换成CloseableReference
@VisibleForTesting
@Nullable
static CloseableReference convertToBitmapReferenceAndClose(
final @Nullable CloseableReference 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);
}
}
...
根据上面的代码,主要的工作是在AnimatedFrameCache中完成响应的缓存的操作,继续分析AnimatedFrameCache
public class AnimatedFrameCache {
//动图中对应帧在内存缓存中的CacheKey,根据帧索引和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 mBackingCache;//Fresco默认的内存缓存,就是Bitmapmemory,从外面传进来的
private final CountingMemoryCache.EntryStateObserver mEntryStateObserver;////监听缓存的Entry的状态变化,Entry是CountingMemoryCache对CloseableReference 缓存value的包装
@GuardedBy("this")
private final LinkedHashSet mFreeItemsPool;//在内存缓存中已经没有对象引用的值,存储可以复用的帧的CacheKey
public AnimatedFrameCache(
CacheKey imageCacheKey,
final CountingMemoryCache backingCache) {
mImageCacheKey = imageCacheKey;
mBackingCache = backingCache;
mFreeItemsPool = new LinkedHashSet<>();
mEntryStateObserver = new CountingMemoryCache.EntryStateObserver() {
@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);
}
}
//根据FrameKey往内存缓存中存储
@Nullable
public CloseableReference cache(
int frameIndex,
CloseableReference imageRef) {
return mBackingCache.cache(keyFor(frameIndex), imageRef, mEntryStateObserver);
}
//获取对应帧的缓存
@Nullable
public CloseableReference get(int frameIndex) {
return mBackingCache.get(keyFor(frameIndex));
}
//查询是否含有对应帧
public boolean contains(int frameIndex) {
return mBackingCache.contains(keyFor(frameIndex));
}
//获取可以复用的Bitmap,提高性能
@Nullable
public CloseableReference getForReuse() {
while (true) {
CacheKey key = popFirstFreeItemKey();
if (key == null) {
return null;
}
CloseableReference imageRef = mBackingCache.reuse(key);
if (imageRef != null) {
return imageRef;
}
}
}
//找到可以复用的CacheKey,然后从内存缓存中取出对应帧的图片
@Nullable
private synchronized CacheKey popFirstFreeItemKey() {
CacheKey cacheKey = null;
Iterator iterator = mFreeItemsPool.iterator();
if (iterator.hasNext()) {
cacheKey = iterator.next();
iterator.remove();
}
return cacheKey;
}
//根据索引生成FrameKey
private FrameKey keyFor(int frameIndex) {
return new FrameKey(mImageCacheKey, frameIndex);
}
}
根据上面的分析,缓存这一块的逻辑已经比较清晰了,就是在绘制的对应帧的时候,会传递给FrescoFrameCache,FrescoFrameCache会传递给AnimatedFrameCache,并且执行相应的缓存,最终的存储位置就是Fresco的bitmap缓存。还有一个很重要的逻辑时复用Bitmap,这块的逻辑是会在BitmapAnimationBackend::drawFrameOrFallback()中,复用的Bitmap其实就是在内存缓存中符合条件的没有其他对象引用的bitmap。
BitmapFramePreparer
在动图的渲染的过程中,绘制和缓存的操作说完了,下面还有一个比较重要的步骤,就是准备接下来的几帧,下面先找到准备的入口,就是在BitmapAnimationBackend::drawFrame()中,在drawFrameOrFallback 代码就不贴了,自己找一下
首先看一下BitmapFramePreparer创建的地方,在ExperimentalBitmapAnimationDrawableFactory
ExperimentalBitmapAnimationDrawableFactory
private AnimationBackend createAnimationBackend(AnimatedImageResult animatedImageResult) {
...
int numberOfFramesToPrefetch = mNumberOfFramesToPrepareSupplier.get();//外面传进来的,默认是3帧
BitmapFramePreparationStrategy bitmapFramePreparationStrategy = null;
BitmapFramePreparer bitmapFramePreparer = null;
if (numberOfFramesToPrefetch > 0) {//如果提前准备的帧数大于0,才会创建bitmapFramePreparationStrategy和BitmapFramePreparer
bitmapFramePreparationStrategy =
new FixedNumberBitmapFramePreparationStrategy(numberOfFramesToPrefetch);
bitmapFramePreparer = createBitmapFramePreparer(bitmapFrameRenderer);
}
...
}
private BitmapFramePreparer createBitmapFramePreparer(BitmapFrameRenderer bitmapFrameRenderer) {
return new DefaultBitmapFramePreparer(
mPlatformBitmapFactory,
bitmapFrameRenderer,
Bitmap.Config.ARGB_8888,
mExecutorServiceForFramePreparing);
}
上面设计到两个类,BitmapFramePreparationStrategy和BitmapFramePreparer,一个是策略,一个是具体操作,下面继续分析响应的类
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();
//调用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);
//执行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 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对应的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 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);
}
//最终,绘制当前帧
mAnimatedDrawableBackend.renderFrame(frameNumber, canvas);
}