美文网首页
Fresco之webp源码解析

Fresco之webp源码解析

作者: sylviaMo | 来源:发表于2019-06-24 10:55 被阅读0次

    前言

    主流的Android的网络图片加载框架,各有利弊,目前公司项目都转用glide,并且把fresco从项目中移除。然而宝宝我还没有去把fresco的源码去撸一遍啊,最近总算有空可以瞻仰一下这个传说很牛逼的库了。上一家公司项目中是使用过fresco的,但是没有使用到webp的图片加载,但是这个功能在我看来是其与其他图片加载框架的一个很大的区别项(差异化功能亮点)。所以我就先从webp加载去看咯。

    图片请求

    代码后放,先放图,因为fresco是MVC的设计,我们就先看我绘制的UI调用的时序图,主要流程我都是画进去咯:


    fresco_UI_调用时序图.png
    • SimpleDraweeView
      继承DraweeView,继承关系如下
    Object (java.lang)
        View (android.view)
            ImageView (android.widget)
                DraweeView (com.facebook.drawee.view)  根部DraweeView
                    GenericDraweeView (com.facebook.drawee.view)  通用的DraweeView
                        SimpleDraweeView (com.facebook.drawee.view) 最简单的DraweeView
    

    两个主要函数

     public void setImageRequest(ImageRequest request) {
        AbstractDraweeControllerBuilder controllerBuilder = mControllerBuilder;
        DraweeController controller =
            controllerBuilder.setImageRequest(request).setOldController(getController()).build();
        setController(controller);
      }
    
      public void setImageURI(Uri uri, @Nullable Object callerContext) {
        DraweeController controller =  1.创建PipelineDraweeController
            mControllerBuilder         2.创建PipelineDraweeControllerBuilder
                .setCallerContext(callerContext)
                .setUri(uri)
                .setOldController(getController())
                .build();
        setController(controller);      3.设置PipelineDraweeController
      }
    
    • GenericDraweeView
      在这里类里面主要做了下面几件事情

      1. 解析XML属性,根据属性值构建 GenericDraweeHierarchy
      2. 将Hierarchy设置给DraweeView体系
    protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
        ...
         解析xml里面的属性值
        GenericDraweeHierarchyBuilder builder =
            GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
        setAspectRatio(builder.getDesiredAspectRatio());
        setHierarchy(builder.build());
        ...
      }
    

    这里面包含了初始化的操作,有一点可以看到,Hierarchy 是在构造方法里面初始化的,所以在使用SimpleDraweeView的getHierarchy 方法时,可以不用判断是否为空
    可以得出结论Hierarchy 是 GenericDraweeHierarchy

    • DraweeView
      继承ImageView
    public void setHierarchy(DH hierarchy) {
        mDraweeHolder.setHierarchy(hierarchy);
        super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
      }
    
      public void setController(@Nullable DraweeController draweeController) {
        mDraweeHolder.setController(draweeController);     1.设置PipelineDraweeController
        super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());    2.给image view设置图片
      }
    
    protected void doAttach() {
        mDraweeHolder.onAttach();
      }
      protected void doDetach() {
        mDraweeHolder.onDetach();
      }
    

    SimpleDraweeView这个类是我们在业务中最直接接触的类,主要通过setImageURI()设置URI给图片赋值,赋值的过程是SimpleDraweeView.setImageURI() --> PipelineDraweeControllerBuilder.build() --> PipelineDraweeControllerBuilder.buildController() --> PipelineDraweeControllerBuilder.obtainController() --> PipelineDraweeController.initialize() --> 赋值完成
    具体图片的加载和转换成ImageView可以类型的实现细节都放在了GenericDraweeHierarchy和PipelineDraweeController这两个类里,同时由DraweeHolder去管理,完美的使用了MVC设计模式。

    • DraweeHolder
      public void onAttach() {
        mEventTracker.recordEvent(Event.ON_HOLDER_ATTACH);
        mIsHolderAttached = true;
        attachOrDetachController();
      }
    
     public void onDetach() {
        mEventTracker.recordEvent(Event.ON_HOLDER_DETACH);
        mIsHolderAttached = false;
        attachOrDetachController();
      }
    
    private void attachOrDetachController() {
        if (mIsHolderAttached && mIsVisible) {
          attachController();
        } else {
          detachController();
        }
      }
    private void attachController() {
        if (mIsControllerAttached) {
          return;
        }
        mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
        mIsControllerAttached = true;
        if (mController != null &&
            mController.getHierarchy() != null) {
          mController.onAttach();
        }
      }
    
      private void detachController() {
        if (!mIsControllerAttached) {
          return;
        }
        mEventTracker.recordEvent(Event.ON_DETACH_CONTROLLER);
        mIsControllerAttached = false;
        if (isControllerValid()) {
          mController.onDetach();
        }
      }
    

    从上述代码可以看到最终都是调用到了PipelineDraweeControlleronAttach()onDetach()函数。

    • AbstractDraweeController
      是实现主要功能的方法,主要函数是onAttach()和onDetach()和submitRequest() ,上面看的DraweeHolder调用的函数最终都是调用到了AbstractDraweeControlleronAttach()onDetach()函数哦
     @Override
      public void onAttach() {
       ...
        mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
        Preconditions.checkNotNull(mSettableDraweeHierarchy);
        mDeferredReleaser.cancelDeferredRelease(this);
        mIsAttached = true;
        if (!mIsRequestSubmitted) {
          submitRequest();
        }
       ...
      }
      @Override
      public void onDetach() {
       ...
        mEventTracker.recordEvent(Event.ON_DETACH_CONTROLLER);
        mIsAttached = false;
        mDeferredReleaser.scheduleDeferredRelease(this);
       ...
      }
    
    protected void submitRequest() {
        ...
        final T closeableImage = getCachedImage();
        if (closeableImage != null) {
          ...
          mDataSource = null;
          mIsRequestSubmitted = true;
          mHasFetchFailed = false;
          mEventTracker.recordEvent(Event.ON_SUBMIT_CACHE_HIT);
          getControllerListener().onSubmit(mId, mCallerContext);
          onImageLoadedFromCacheImmediately(mId, closeableImage);
          onNewResultInternal(mId, mDataSource, closeableImage, 1.0f, true, true, true);
          ...
          return;
        }
        mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
        getControllerListener().onSubmit(mId, mCallerContext);
        mSettableDraweeHierarchy.setProgress(0, true);
        mIsRequestSubmitted = true;
        mHasFetchFailed = false;
        mDataSource = getDataSource();
        ...
        final String id = mId;
        final boolean wasImmediate = mDataSource.hasResult();
        final DataSubscriber<T> dataSubscriber =
            new BaseDataSubscriber<T>() {
              @Override
              public void onNewResultImpl(DataSource<T> dataSource) {
                // isFinished must be obtained before image, otherwise we might set intermediate result
                // as final image.
                boolean isFinished = dataSource.isFinished();
                boolean hasMultipleResults = dataSource.hasMultipleResults();
                float progress = dataSource.getProgress();
                T image = dataSource.getResult();
                if (image != null) {
                  onNewResultInternal(
                      id, dataSource, image, progress, isFinished, wasImmediate, hasMultipleResults);
                } else if (isFinished) {
                  onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
                }
              }
    
              @Override
              public void onFailureImpl(DataSource<T> dataSource) {
                onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
              }
    
              @Override
              public void onProgressUpdate(DataSource<T> dataSource) {
                boolean isFinished = dataSource.isFinished();
                float progress = dataSource.getProgress();
                onProgressUpdateInternal(id, dataSource, progress, isFinished);
              }
            };
        mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
       ...
      }
    

    整体的调用过程 onAttach() --> submitRequest() --> onNewResultInternal()

    • PipelineDraweeController
      这个类继承了抽象类AbstractDraweeController,抽象类里是主要实现类,这个类主要方法是
      getDataSource()createDrawable(CloseableReference<CloseableImage> image)
    
     @Override
      protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
        ...
        DataSource<CloseableReference<CloseableImage>> result = mDataSourceSupplier.get();
        ...
        return result;
      }
    
     @Override
      protected Drawable createDrawable(CloseableReference<CloseableImage> image) {
        try {
          ...
          Preconditions.checkState(CloseableReference.isValid(image));
          CloseableImage closeableImage = image.get();
    
          maybeUpdateDebugOverlay(closeableImage);
    
          Drawable drawable =
              maybeCreateDrawableFromFactories(mCustomDrawableFactories, closeableImage);
          if (drawable != null) {
            return drawable;
          }
    
          drawable = maybeCreateDrawableFromFactories(mGlobalDrawableFactories, closeableImage);
          if (drawable != null) {
            return drawable;
          }
    
          drawable = mDefaultDrawableFactory.createDrawable(closeableImage);
          if (drawable != null) {
            return drawable;
          }
          throw new UnsupportedOperationException("Unrecognized image class: " + closeableImage);
        } finally {
          if (FrescoSystrace.isTracing()) {
            FrescoSystrace.endSection();
          }
        }
      }
    

    数据源的获取和解析

    看完上面的分析后,发现我们现在主要方向在getDataSource(),那么我们从这里开始继续往下看咯~

    fresco_数据源获取和解析_调用时序图1.png fresco_数据源获取和解析_调用时序图2.png
    是不是看到这么多类绕来绕去,已经蒙圈,不要害怕,因为我现在就是蒙圈本尊~~在这两个图里可以看到之前创建的produceSequence是干什么的了,这里使用到了责任链设计模式,通过调用produceResults()->各个producer类wrap的consumer类的onNewResultImpl(),一层层向上传递。但是看了这么多,我们只看到了ImagePipeline.fetchDecodedImage()的各个producerSequence,其实还有很多producer哦。
    • ImagePipelineFactory
      public ImagePipeline getImagePipeline() {
        if (mImagePipeline == null) {
          mImagePipeline =
              new ImagePipeline(
                  getProducerSequenceFactory(),这里创建各路producer大神
                  mConfig.getRequestListeners(),
                  mConfig.getIsPrefetchEnabledSupplier(),
                  getBitmapMemoryCache(),
                  getEncodedMemoryCache(),
                  getMainBufferedDiskCache(),
                  getSmallImageBufferedDiskCache(),
                  mConfig.getCacheKeyFactory(),
                  mThreadHandoffProducerQueue,
                  Suppliers.of(false),
                  mConfig.getExperiments().isLazyDataSource(),
                  mConfig.getCallerContextVerifier());
        }
        return mImagePipeline;
      }
      private ProducerSequenceFactory getProducerSequenceFactory() {
        // before Android N the Bitmap#prepareToDraw method is no-op so do not need this
        final boolean useBitmapPrepareToDraw = Build.VERSION.SDK_INT >= 24 //Build.VERSION_CODES.NOUGAT
            && mConfig.getExperiments().getUseBitmapPrepareToDraw();
    
        if (mProducerSequenceFactory == null) {
          mProducerSequenceFactory =
              new ProducerSequenceFactory(
                  mConfig.getContext().getApplicationContext().getContentResolver(),
                  getProducerFactory(),
                  mConfig.getNetworkFetcher(),
                  mConfig.isResizeAndRotateEnabledForNetwork(),
                  mConfig.getExperiments().isWebpSupportEnabled(),
                  mThreadHandoffProducerQueue,
                  mConfig.isDownsampleEnabled(),
                  useBitmapPrepareToDraw,
                  mConfig.getExperiments().isPartialImageCachingEnabled(),
                  mConfig.isDiskCacheEnabled(),
                  getImageTranscoderFactory());
        }
        return mProducerSequenceFactory;
      }
    

    看看这里,是不是发现了特别多的producer呢,哈哈哈,我们就先看看我们最关心的网络获取图片的producer吧,这里就看okhttp的了,因为支持很多种,这里就不分析其他的了。

    • OkHttpNetworkFetcher
    @Override
      public void fetch(
          final OkHttpNetworkFetchState fetchState, final NetworkFetcher.Callback callback) {
        fetchState.submitTime = SystemClock.elapsedRealtime();
        final Uri uri = fetchState.getUri();
    
        try {
          final Request.Builder requestBuilder = new Request.Builder()
              .url(uri.toString())
              .get();
    
          if (mCacheControl != null) {
            requestBuilder.cacheControl(mCacheControl);
          }
    
          final BytesRange bytesRange = fetchState.getContext().getImageRequest().getBytesRange();
          if (bytesRange != null) {
            requestBuilder.addHeader("Range", bytesRange.toHttpRangeHeaderValue());
          }
    
          fetchWithRequest(fetchState, callback, requestBuilder.build());
        } catch (Exception e) {
          // handle error while creating the request
          callback.onFailure(e);
        }
      }
    
    protected void fetchWithRequest(
          final OkHttpNetworkFetchState fetchState,
          final NetworkFetcher.Callback callback,
          final Request request) {
        final Call call = mCallFactory.newCall(request);
    
        fetchState
            .getContext()
            .addCallbacks(
                new BaseProducerContextCallbacks() {
                  @Override
                  public void onCancellationRequested() {
                    if (Looper.myLooper() != Looper.getMainLooper()) {
                      call.cancel();
                    } else {
                      mCancellationExecutor.execute(
                          new Runnable() {
                            @Override
                            public void run() {
                              call.cancel();
                            }
                          });
                    }
                  }
                });
    
        call.enqueue(
            new okhttp3.Callback() {
              @Override
              public void onResponse(Call call, Response response) throws IOException {
                fetchState.responseTime = SystemClock.elapsedRealtime();
                final ResponseBody body = response.body();
                try {
                  if (!response.isSuccessful()) {
                    handleException(
                        call, new IOException("Unexpected HTTP code " + response), callback);
                    return;
                  }
    
                  BytesRange responseRange =
                      BytesRange.fromContentRangeHeader(response.header("Content-Range"));
                  if (responseRange != null
                      && !(responseRange.from == 0
                          && responseRange.to == BytesRange.TO_END_OF_CONTENT)) {
                    // Only treat as a partial image if the range is not all of the content
                    fetchState.setResponseBytesRange(responseRange);
                    fetchState.setOnNewResultStatusFlags(Consumer.IS_PARTIAL_RESULT);
                  }
    
                  long contentLength = body.contentLength();
                  if (contentLength < 0) {
                    contentLength = 0;
                  }
                  callback.onResponse(body.byteStream(), (int) contentLength);
                } catch (Exception e) {
                  handleException(call, e, callback);
                } finally {
                  body.close();
                }
              }
    
              @Override
              public void onFailure(Call call, IOException e) {
                handleException(call, e, callback);
              }
            });
      }
    
    

    callback.onResponse(body.byteStream(), (int) contentLength);这里就会把请求到的body传递上去。
    到这里基本就能看清fresco是如何请求数据和解析数据的了。下面我们就看看如果是webp的图片,是如何处理的。

    • ProducerSequenceFactory
      在上述图fresco_数据源获取和解析_调用时序图1中看到网络图片的producerSequence的创建是在ProducerSequenceFactory.getNetworkFetchSequence()方法中实现的,我们来具体看一下这个源码
     /**
       * swallow result if prefetch -> bitmap cache get -> background thread hand-off -> multiplex ->
       * bitmap cache -> decode -> multiplex -> encoded cache -> disk cache -> (webp transcode) ->
       * network fetch.
       */
      private synchronized Producer<CloseableReference<CloseableImage>> getNetworkFetchSequence() {
         ...
        if (mNetworkFetchSequence == null) {
         ...
          mNetworkFetchSequence =
              newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence());
          ...
        }
        ...
        return mNetworkFetchSequence;
      }
    
      /** multiplex -> encoded cache -> disk cache -> (webp transcode) -> network fetch. */
      private synchronized Producer<EncodedImage> getCommonNetworkFetchToEncodedMemorySequence() {
      
        ...
        if (mCommonNetworkFetchToEncodedMemorySequence == null) {
         ...
          Producer<EncodedImage> inputProducer =
              newEncodedCacheMultiplexToTranscodeSequence(
                  mProducerFactory.newNetworkFetchProducer(mNetworkFetcher));
          mCommonNetworkFetchToEncodedMemorySequence =
              ProducerFactory.newAddImageTransformMetaDataProducer(inputProducer);
    
          mCommonNetworkFetchToEncodedMemorySequence =
              mProducerFactory.newResizeAndRotateProducer(
                  mCommonNetworkFetchToEncodedMemorySequence,
                  mResizeAndRotateEnabledForNetwork && !mDownsampleEnabled,
                  mImageTranscoderFactory);
      ...
        return mCommonNetworkFetchToEncodedMemorySequence;
      }
    
      private Producer<EncodedImage> newEncodedCacheMultiplexToTranscodeSequence(
          Producer<EncodedImage> inputProducer) {
        if (WebpSupportStatus.sIsWebpSupportRequired &&
            (!mWebpSupportEnabled || WebpSupportStatus.sWebpBitmapFactory == null)) {
          inputProducer = mProducerFactory.newWebpTranscodeProducer(inputProducer);
        }
        if (mDiskCacheEnabled) {
          inputProducer = newDiskCacheSequence(inputProducer);
        }
        EncodedMemoryCacheProducer encodedMemoryCacheProducer =
            mProducerFactory.newEncodedMemoryCacheProducer(inputProducer);
        return mProducerFactory.newEncodedCacheKeyMultiplexProducer(encodedMemoryCacheProducer);
      }
    

    最后是在函数newEncodedCacheMultiplexToTranscodeSequence()里创建了WebpTranscodeProducer实例。

    • WebpTranscodeProducer
      webp格式的图片的处理就是在这个类里完成的,这个类的主要函数是doTranscode(),看一下源码
      private static void doTranscode(
          final EncodedImage encodedImage,
          final PooledByteBufferOutputStream outputStream) throws Exception {
        InputStream imageInputStream = encodedImage.getInputStream();
        ImageFormat imageFormat = ImageFormatChecker.getImageFormat_WrapIOException(imageInputStream);
        if (imageFormat == DefaultImageFormats.WEBP_SIMPLE ||
            imageFormat == DefaultImageFormats.WEBP_EXTENDED) {
            WebpTranscoderFactory.getWebpTranscoder().transcodeWebpToJpeg(
                imageInputStream,
                outputStream,
                DEFAULT_JPEG_QUALITY);
          encodedImage.setImageFormat(DefaultImageFormats.JPEG);
        } else if (imageFormat == DefaultImageFormats.WEBP_LOSSLESS ||
            imageFormat == DefaultImageFormats.WEBP_EXTENDED_WITH_ALPHA) {
          // In this case we always transcode to PNG
          WebpTranscoderFactory.getWebpTranscoder()
              .transcodeWebpToPng(imageInputStream, outputStream);
          encodedImage.setImageFormat(DefaultImageFormats.PNG);
        } else {
          throw new IllegalArgumentException("Wrong image format");
        }
      }
    

    看到这个函数是根据webp的ImageFormat去决定转换成JPEG还是PNG。转换格式的实现是放在了WebpTranscoderImpl这个类里,实现细节是native的,通过JNI实现,到这里基本就知道加载一个webp格式的网络图片的整个加载的过程了。

    • webp转换成JPEG和PNG的实现
      WebpTranscoderImpl类里的nativeTranscodeWebpToJpeg() 和 nativeTranscodeWebpToPng()的对应的native的实现是在WebpTranscoder.cpp类。
      JNI的使用规则这里不做详细赘述,看一下为什么我说native的实现是在这个类,看一下WebpTranscoder.cpp的register方法
    static JNINativeMethod gWebpTranscoderMethods[] = {
      { "nativeTranscodeWebpToJpeg",
        "(Ljava/io/InputStream;Ljava/io/OutputStream;I)V",
        (void*) WebpTranscoder_transcodeToJpeg },
      { "nativeTranscodeWebpToPng",
        "(Ljava/io/InputStream;Ljava/io/OutputStream;)V",
        (void*) WebpTranscoder_transcodeToPng },
    };
    
    bool registerWebpTranscoderMethods(JNIEnv* env){
      auto webPTranscoderClass = env->FindClass(
          "com/facebook/imagepipeline/nativecode/WebpTranscoderImpl");
      if (webPTranscoderClass == nullptr) {
        LOGE("could not find WebpTranscoderImpl class");
        return false;
      }
    
      auto result = env->RegisterNatives(
          webPTranscoderClass,
          gWebpTranscoderMethods,
          std::extent<decltype(gWebpTranscoderMethods)>::value);
    
      if (result != 0) {
        LOGE("could not register WebpTranscoder methods");
        return false;
      }
    
      return true;
    }
    

    这里声明了Java类WebpTranscoderImpl的两个方法和C++类对应的native实现方法的对应关系,然后注册。这里以转换成JPEG格式的实现为例,看一下源码

    static void WebpTranscoder_transcodeToJpeg(
        JNIEnv* env,
        jclass clzz,
        jobject is,
        jobject os,
        jint quality) {
      auto decodedImagePtr = decodeWebpFromInputStream(env, is, PixelFormat::RGB);
      RETURN_IF_EXCEPTION_PENDING;
      encodeJpegIntoOutputStream(env, *decodedImagePtr, os, quality);
    }
    

    后面的C++的实现感兴趣的盆友可以看看。纯手动输入,万一有什么误导性的错误,还望能指出,谢谢。

    相关文章

      网友评论

          本文标题:Fresco之webp源码解析

          本文链接:https://www.haomeiwen.com/subject/jzsjqctx.html