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

  • 相关阅读:
    git安装和使用
    GitHub入门
    jmeter入门
    this关键字
    ES6函数
    代码雨
    this指向练习题
    a标签阻止默认跳转行为事件
    模板引擎的应用
    面向对象
  • 原文地址:https://www.cnblogs.com/pandapan/p/4644195.html
Copyright © 2011-2022 走看看