Fresco Gif加載解析

Fresco Gif加載解析

普通Gif加載

val controller = Fresco.newDraweeControllerBuilder()
            .setAutoPlayAnimations(true)//就是添加了這一句代碼触趴,就可以播放動圖
            .setImageRequest(request)
            .setOldController(sivBanner?.controller)
            .build()

sivBanner?.controller = controller

先看性能表現(xiàn)效果圖:


FrescoGif_GC.jpeg

FrescoGif_CPU.jpeg

FrescoGif_Memory.jpeg

從上面三張圖可以看的出來普通的加載會頻繁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

AnimationDrawableBackend.jpg
AnimationDrawable.jpg

  @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

BitmapFrescoFrameCache.jpg

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);
  }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毡熏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子侣诵,更是在濱河造成了極大的恐慌痢法,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杜顺,死亡現(xiàn)場離奇詭異财搁,居然都是意外死亡,警方通過查閱死者的電腦和手機哑舒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門妇拯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幻馁,“玉大人洗鸵,你說我怎么就攤上這事越锈。” “怎么了膘滨?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵甘凭,是天一觀的道長。 經(jīng)常有香客問我火邓,道長丹弱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任铲咨,我火速辦了婚禮躲胳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纤勒。我一直安慰自己坯苹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布摇天。 她就那樣靜靜地躺著粹湃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泉坐。 梳的紋絲不亂的頭發(fā)上为鳄,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音腕让,去河邊找鬼孤钦。 笑死,一個胖子當(dāng)著我的面吹牛纯丸,可吹牛的內(nèi)容都是我干的司训。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼液南,長吁一口氣:“原來是場噩夢啊……” “哼壳猜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滑凉,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤统扳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畅姊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咒钟,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年若未,在試婚紗的時候發(fā)現(xiàn)自己被綠了朱嘴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖萍嬉,靈堂內(nèi)的尸體忽然破棺而出乌昔,到底是詐尸還是另有隱情,我是刑警寧澤壤追,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布磕道,位于F島的核電站,受9級特大地震影響行冰,放射性物質(zhì)發(fā)生泄漏溺蕉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一悼做、第九天 我趴在偏房一處隱蔽的房頂上張望疯特。 院中可真熱鬧,春花似錦肛走、人聲如沸辙芍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽故硅。三九已至,卻和暖如春纵搁,著一層夾襖步出監(jiān)牢的瞬間吃衅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工腾誉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徘层,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓利职,卻偏偏與公主長得像趣效,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猪贪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容