zoukankan      html  css  js  c++  java
  • Fresco 源码分析(一) DraweeView-DraweeHierarchy-DraweeController(MVC) DraweeHierachy+DraweeController的分析

    4.1.5.2 模型层DraweeHierachy继承体系以及各个类的作用

    DraweeHierachy (I)
    --| SettableDraweeHierarchy (I)
    ------| GenericDraweeHierarchy

    DraweeHierachy:

    1. 用于获取顶层的drawable
      SettableDraweeHierachy:
    2. 图像可以被重置
    3. 图像可以设置进度
    4. 设置失败
    5. 设置重试
    6. 设置controllerOverlay

    在理解获取顶层的Drawable时,需要首先理解Drawable的继承结构

    先来看看DraweeHierachy的源码,发现其为接口,并且只有一个方法,就是用于获取顶层的Drawable

    DraweeHierachy的源码

    public interface DraweeHierarchy {
    
      /**
       * Returns the top level drawable in the corresponding hierarchy. Hierarchy should always have
       * the same instance of its top level drawable.
       * @return top level drawable
       */
      public Drawable getTopLevelDrawable();
    }
    

    再来看看继承其的接口,SettableDraweeHierachy,如上所述

    1. 图像可以被重置

    2. 图像可以设置进度

    3. 设置失败

    4. 设置重试

    5. 设置controllerOverlay

       public interface SettableDraweeHierarchy extends DraweeHierarchy {
         public void reset();
         public void setImage(Drawable drawable, float progress, boolean immediate);
         public void setProgress(float progress, boolean immediate);
         public void setFailure(Throwable throwable);
         public void setRetry(Throwable throwable);
         public void setControllerOverlay(Drawable drawable);
       }
      

    4.1.5.3 控制层DraweeController继承体系以及个各类的作用

    DraweeController
    --| AbstractDraweeController
    ----| PipelineDraweeController

    DraweeController:

    1. 获取和设置Hieraychy
    2. view的各种事件通知过来,controller来控制这些逻辑的操作(onAttach/onDetach/onTouchEvent/getAnimatable)

    AbstractDraweeController:

    1. 最关键的功能: 实现了客户端向服务端的提交请求,即向DataSource中注册观察者,在有结果返回的时候,在主线程通知客户端更新即可,即设置Hierarychy的drawable即可

    2. 参照之前的分析方式,仍然采用先构造,然后具体方法的顺序
      2.1 构造方法,设置了UI线程池,重试,以及手势相关的信息

      public AbstractDraweeController(
      DeferredReleaser deferredReleaser,
      Executor uiThreadImmediateExecutor,
      String id,
      Object callerContext) {
      mDeferredReleaser = deferredReleaser;
      mUiThreadImmediateExecutor = uiThreadImmediateExecutor;
      init(id, callerContext);
      }

      /**
      * Initializes this controller with the new id and caller context.
      * This allows for reusing of the existing controller instead of instantiating a new one.
      * This method should be called when the controller is in detached state.
      * @param id unique id for this controller
      * @param callerContext tag and context for this controller
      */
      protected void initialize(String id, Object callerContext) {
      init(id, callerContext);
      }

      private void init(String id, Object callerContext) {
      mEventTracker.recordEvent(Event.ON_INIT_CONTROLLER);
      // cancel deferred release
      if (mDeferredReleaser != null) {
      mDeferredReleaser.cancelDeferredRelease(this);
      }
      // reinitialize mutable state (fetch state)
      mIsAttached = false;
      releaseFetch();
      // reinitialize optional components
      if (mRetryManager != null) {
      mRetryManager.init();
      }
      if (mGestureDetector != null) {
      mGestureDetector.init();
      mGestureDetector.setClickListener(this);
      }
      if (mControllerListener instanceof InternalForwardingListener) {
      ((InternalForwardingListener) mControllerListener).clearListeners();
      } else {
      mControllerListener = null;
      }
      // clear hierarchy and controller overlay
      if (mSettableDraweeHierarchy != null) {
      mSettableDraweeHierarchy.reset();
      mSettableDraweeHierarchy.setControllerOverlay(null);
      mSettableDraweeHierarchy = null;
      }
      mControllerOverlay = null;
      // reinitialize constant state
      if (FLog.isLoggable(FLog.VERBOSE)) {
      FLog.v(TAG, "controller %x %s -> %s: initialize", System.identityHashCode(this), mId, id);
      }
      mId = id;
      mCallerContext = callerContext;
      }

      2.2 具体方法,在这里做分析时,我们重点关注图片如何获取,因而我们关注的核心方法是onAttach(),在这里实现了图片请求的机制,以及图片获取到如何回调,如何显示到UI层的控制,在下面的程序中,看到核心的设置的方法是submitRequest()

      @Override
      public void onAttach() {
      // 记录log
      if (FLog.isLoggable(FLog.VERBOSE)) {
      FLog.v(
      TAG,
      "controller %x %s: onAttach: %s",
      System.identityHashCode(this),mId,
      mIsRequestSubmitted ? "request already submitted" : "request needs submit");
      }
      //-------------事件跟踪
      mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
      Preconditions.checkNotNull(mSettableDraweeHierarchy);
      mDeferredReleaser.cancelDeferredRelease(this);
      mIsAttached = true;
      // --------如果是为请求的状态,发送请求!!!!!!!!!!!!!!
      if (!mIsRequestSubmitted) {
      submitRequest();
      }
      }

    此处以第一次请求为例,这样分析比较简单,查看下面的方法,在请求时,设置请求的进度为0,获取到数据源(DataSource),然后给数据源注册观察者(DataSubscriber),先查看下面的SubmitRequest方法

    protected void submitRequest() {
        mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
        getControllerListener().onSubmit(mId, mCallerContext);
        mSettableDraweeHierarchy.setProgress(0, true);
        mIsRequestSubmitted = true;
        mHasFetchFailed = false;
        mDataSource = getDataSource();
        if (FLog.isLoggable(FLog.VERBOSE)) {
          FLog.v(
              TAG,
              "controller %x %s: submitRequest: dataSource: %x",
              System.identityHashCode(this),
              mId,
              System.identityHashCode(mDataSource));
        }
        final String id = mId;
    	//------------此处以第一次请求为例,所以wasImmediate为false
        final boolean wasImmediate = mDataSource.hasResult();
    	//------------创建dataSubscriber的匿名内部类,交由AbstractDraweeController处理
    	//------------回调的结果
        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();
                float progress = dataSource.getProgress();
                T image = dataSource.getResult();
                if (image != null) {
                  onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
                } 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);
      }
    

    到了这里,一次请求已经完成了,请求的结果会在回调中执行,但是请求是如何生成的呢?我们并没有看到具体发送请求的逻辑,这个疑问我们先记录下来(暂且标记为Q1)。先来看看对于请求结果是如何处理的,以新的一次请求结果为例,onNewResultImpl()方法,而onNewResultImpl方法,以image不为空为例,最终会调用AbstractDraweeController.onNewResultInternal()方法。下面我们来看看,是如何处理这次新的请求的结果。

    1. 判断是否是想要的数据源,即查看数据信息是否是当前请求的信息,如果不是,直接释放了资源

    2. 如果是想要的数据源,创建对应的drawable,设置当前显示的drawable,释放之前缓存的drawable对象和Image对象

      private void onNewResultInternal(
      String id,
      DataSource dataSource,
      @Nullable T image,
      float progress,
      boolean isFinished,
      boolean wasImmediate) {
      // ignore late callbacks (data source that returned the new result is not the one we expected)
      if (!isExpectedDataSource(id, dataSource)) {
      logMessageAndImage("ignore_old_datasource @ onNewResult", image);
      releaseImage(image);
      dataSource.close();
      return;
      }
      mEventTracker.recordEvent(
      isFinished ? Event.ON_DATASOURCE_RESULT : Event.ON_DATASOURCE_RESULT_INT);
      // create drawable
      Drawable drawable;
      try {
      drawable = createDrawable(image);
      } catch (Exception exception) {
      logMessageAndImage("drawable_failed @ onNewResult", image);
      releaseImage(image);
      onFailureInternal(id, dataSource, exception, isFinished);
      return;
      }
      T previousImage = mFetchedImage;
      Drawable previousDrawable = mDrawable;
      mFetchedImage = image;
      mDrawable = drawable;
      try {
      // set the new image
      if (isFinished) {
      logMessageAndImage("set_final_result @ onNewResult", image);
      mDataSource = null;
      mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
      getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
      // IMPORTANT: do not execute any instance-specific code after this point
      } else {
      logMessageAndImage("set_intermediate_result @ onNewResult", image);
      mSettableDraweeHierarchy.setImage(drawable, progress, wasImmediate);
      getControllerListener().onIntermediateImageSet(id, getImageInfo(image));
      // IMPORTANT: do not execute any instance-specific code after this point
      }
      } finally {
      if (previousDrawable != null && previousDrawable != drawable) {
      releaseDrawable(previousDrawable);
      }
      if (previousImage != null && previousImage != image) {
      logMessageAndImage("release_previous_result @ onNewResult", previousImage);
      releaseImage(previousImage);
      }
      }
      }

    好了,就是获取到图像后续的操作,这个其实就是我们UI的操作,分析到此即可,其他的情况,我们参照这个分析的方式分析即可。下面我们来解决一下之前的Q1问题,数据源的请求是如何发送出去的,这个问题就比较复杂了,我们需要通过至少四篇的博客来分析这个请求的过程。
    下篇博客:Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题 :http://www.cnblogs.com/pandapan/p/4646786.html

    安卓源码分析群: Android源码分析QQ1群号:164812238

  • 相关阅读:
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    linux中的cd ..和cd -命令有什么区别?
    GCC使用
  • 原文地址:https://www.cnblogs.com/pandapan/p/4644195.html
Copyright © 2011-2022 走看看