View#invalidate流程分析

280 阅读3分钟

一、View#invalidate开始分析

1.1、View.invalidate()会发起一次View绘制

  • (1)分析View.invalidate()源码如下:

      public void invalidate() {
          invalidate(true);
      }
    
      public void invalidate(boolean invalidateCache) {
          invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
      }
    
      void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {
          ...
          final AttachInfo ai = mAttachInfo;
          final ViewParent p = mParent;
          if (p != null && ai != null && l < r && t < b) {
              final Rect damage = ai.mTmpInvalRect;
              damage.set(l, t, r, b);
    
              //调用了ViewGroup的invalidateChild方法
              p.invalidateChild(this, damage);
          }
          ...
      }
    
  • (2)这里调用到了ViewGroup的方法,ViewGroup#invalidateChild()

      public final void invalidateChild(View child, final Rect dirty) {
          final AttachInfo attachInfo = mAttachInfo;
          if (attachInfo != null && attachInfo.mHardwareAccelerated) {
              // HW accelerated fast path
              onDescendantInvalidated(child, child);
              return;
          }
    
          ViewParent parent = this;
          if (attachInfo != null) {
              ...
    
              do {
                  View view = null;
                  if (parent instanceof View) {
                      view = (View) parent;
                  }
    
                  ...
                  // 通过do-while
                  // 递归调用父的invalidateChildInParent,直到最顶层的View为止
                  // 也就是通过递归的方式找父
                  // 因为只有找到父,才可以测量、布局、绘制整个view
                  parent = parent.invalidateChildInParent(location, dirty);
                  ...
    
              } while (parent != null);
          }
      }
    
  • 其实,从View层面最终肯定会找到DecorView,但是顺着DecorView的创建以及添加到Window的时机,可以发现在->ViewRootImpl#setView()存在view.assignParent(this)->View#assignParent()这个调用链儿,

  • 所以在ViewGroup找的这个parent就是DecorView的父,就是ViewRootImpl

1.2、分析ViewRootImpl#invalidateChildInParent()

  • (1)分析ViewRootImpl#invalidateChildInParent()

      public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
          checkThread();
    
          if (dirty == null) {
              //1.这个地方会调用invalidate(),里边会调用scheduleTraversals()
              invalidate();
              return null;
          } else if (dirty.isEmpty() && !mIsAnimating) {
              return null;
          }
    
          ...
          
          // 2.这个地方最终也会调用scheduleTraversals()
          invalidateRectOnScreen(dirty);
    
          return null;
      }
    
  • 所以不管走到1还是2,最红都会执行scheduleTraversals(),这个流程与View.requestLayout不谋而合。

  • (2)分析ViewRootImpl#scheduleTraversals

          void scheduleTraversals() {
              //1.防止多次调用,一个vsync时期内,没必须要多次调用
              if (!mTraversalScheduled) {
                  mTraversalScheduled = true;
                  
                  //2.发送一个同步屏障,保证handler先处理异步消息
                  mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                  
                  //3.通过编舞者发送一个runnable,
                  mChoreographer.postCallback(
                          Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                  ...
              }
          }
    

二、Choreographer编舞者

2.1、mChoreographer什么时候初始化

  • 在ViewRootImpl的构造方法中初始化好了,mChoreographer = Choreographer.getInstance();

  • mChoreographer是保存在ThreadLocal中的线程私有的变量,所以这个mChoreographer是跟随线程的

  • Choreographer源代码分析:

      /**
       * Gets the choreographer for the calling thread.  Must be called from
       * a thread that already has a {@link android.os.Looper} associated with it.
       * @return The choreographer for this thread.
       */
        public static Choreographer getInstance() {
           return sThreadInstance.get();
        }
        
        // 1.sThreadInstance是一个ThreadLocal<Choreographer>对象,线程私有的
    
        private Choreographer(Looper looper, int vsyncSource) {
            mLooper = looper;
            mHandler = new FrameHandler(looper);
    
            // FrameDisplayEventReceiver是vsync事件接收器,是Choreographer的内部类
            // 它的父类是DisplayEventReceiver
            mDisplayEventReceiver = USE_VSYNC
                    ? new FrameDisplayEventReceiver(looper, vsyncSource)
                    : null;
            mLastFrameTimeNanos = Long.MIN_VALUE;
    
            // 一帧的事件,60fts就是1s刷新60帧,也就是一帧需要16.7ms
            mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    
            //回调队列
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
        }
    

2.2、mTraversalRunnable是什么

  • mTraversalRunnable就是一个runnable,将来要执行的run方法内部的方法

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    

2.3、mChoreographer是如何发送任务,以及任务是如何被调度执行的

  • vsync是一个由硬件发出来的信号,Choreographer主要负责监听这个信号,每当信号来临时,统一开始绘制流程

  • 继续分析mChoreographer.postCallback

    // 从ViewRootImpl的scheduleTraversals中开始
    mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }
    
    public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
        ...
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }
    
    private void postCallbackDelayedInternal(int callbackType,bject action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
    
            // 将mTraversalRunnable 加入队列
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
    
            if (dueTime <= now) { // 因为delayMillis=0,这里会走进去
                scheduleFrameLocked(now);
            } else {
                //延迟执行
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
    
                // 发送一个异步消息
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }
    
  • 整个postCallback的调用链就是这样的 Choreographer#postCallback ->postCallbackDelayed ->postCallbackDelayedInternal ->scheduleFrameLocked ->scheduleVsyncLocked ->DisplayEventReceiver.scheduleVsync(); 

  • 最终调到DisplayEventReceiver中

2.4、收到底层信号后做了什么

(1)从DisplayEventReceiver#dispatchVsync开始分析

       // Called from native code.
        private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
        }
  • Choreographer.postCallback() 方法通过 DisplayEventReceiver.nativeScheduleVsync() 方法向系统底层注册了下一次 vsync 信号的监听。

  • 当下一次 vsync 来临时,系统会回调其 dispatchVsync() 方法,因为父类从DisplayEventReceiver中onVsync是个空方法,最终回调 FrameDisplayEventReceiver.onVsync() 方法。

  • FrameDisplayEventReceiver.onVsync() 方法中取出之前提交的 mTraversalRunnable 并执行。 (2)看下FrameDisplayEventReceiver#onVsync()做了什么

      @Override
      public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
       // 发送 vsync event to the Handler.
    
       // 如果这个时候,消息队列中没有比当前帧更早的消息,这个帧消息会立刻执行
    
       // 否则早已当前帧消息的消息会先执行
       long now = System.nanoTime();
       if (timestampNanos > now) {
           Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                   + " ms in the future!  Check that graphics HAL is generating vsync "
                   + "timestamps using the correct timebase.");
           timestampNanos = now; // 1.timestampNanos是vsync回调的时间,单位是纳秒,不能比now大
       }
    
       if (mHavePendingVsync) {
           Log.w(TAG, "Already have a pending vsync event.  There should only be "
                   + "one at a time.");
       } else {
           mHavePendingVsync = true;
       }
    
       //2. 代码执行到这里的vsync信号已经发生,而且上面1处到这行代码也需要耗时,所以此时timestampNanos一定是<now的,这就确保了这一帧的消息一定会插入到MQ的队首,从而优先执行
       mTimestampNanos = timestampNanos;
       mFrame = frame;
       
       // 此处设置callback是this,所以会回调本类的run方法
       Message msg = Message.obtain(mHandler, this);
       msg.setAsynchronous(true);
       mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);//纳秒转为毫秒
    

    }

    @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }

  • 一定要搞清楚为什么当前帧的消息会被插入到MQ的队首,看注释2 (3)分析onFrame()

    void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { ...

        //1.frameTimeNanos是vsync信号回调时间
        long intendedFrameTimeNanos = frameTimeNanos;
    
        //2. startNanos是当前时间
        startNanos = System.nanoTime();
    
        //3. 2和1相减,就是主线程的耗时时间
        final long jitterNanos = startNanos - frameTimeNanos;
    
        //mFrameIntervalNanos是一帧的时间
        if (jitterNanos >= mFrameIntervalNanos) {
            final long skippedFrames = jitterNanos / mFrameIntervalNanos;
            if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
    
                //3.掉帧30帧,就会出现常见的log,在这个地方
                Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                        + "The application may be doing too much work on its main thread.");
            }
        }
    }
    
    try {
    
        // doCallbacks开始执行回调,根据对应的CallbackType,找到对应的CallbackRecord对象
        // CallbackType只有四种,CALLBACK_INPUT,CALLBACK_ANIMATION,CALLBACK_TRAVERSAL,CALLBACK_COMMIT
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
        AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
        mFrameInfo.markInputHandlingStart();
        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
        mFrameInfo.markAnimationsStart();
        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
        doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    
        mFrameInfo.markPerformTraversalsStart();
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
        doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
    } finally {
        AnimationUtils.unlockAnimationClock();
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    

    }