13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

Vsync信号机制和UI刷新流程

[[439732]]

 

前言

屏幕刷新帧率不稳定,掉帧严重,无法保证每秒60帧,导致屏幕画面撕裂;

今天我们来讲解下Vsync机制和UI刷新流程

一、 Vsync信号详解

1、屏幕刷新相关知识点

2、VSYNC机制

VSync机制:Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,VSync是Vertical Synchronization(垂直同步)的缩写,是一种在PC上很早就广泛使用的技术,可以简单的把它认为是一种定时中断。而在Android 4.1(JB)中已经开始引入VSync机制;

VSync机制下的绘制过程;CPU/GPU接收vsync信号,Vsync每16ms一次,那么在每次发出Vsync命令时,CPU都会进行刷新的操作。也就是在每个16ms的第一时间,CPU就会响应Vsync的命令,来进行数据刷新的动作。CPU和GPU的刷新时间,和Display的FPS是一致的。因为只有到发出Vsync命令的时候,CPU和GPU才会进行刷新或显示的动作。CPU/GPU接收vsync信号提前准备下一帧要显示的内容,所以能够及时准备好每一帧的数据,保证画面的流畅;

可见vsync信号没有提醒CPU/GPU工作的情况下,在第一个16ms之内,一切正常。然而在第二个16ms之内,几乎是在时间段的最后CPU才计算出了数据,交给了Graphics Driver,导致GPU也是在第二段的末尾时间才进行了绘制,整个动作延后到了第三段内。从而影响了下一个画面的绘制。这时会出现Jank(闪烁,可以理解为卡顿或者停顿)。这时候CPU和GPU可能被其他操作占用了,这就是卡顿出现的原因;

二、UI刷新原理流程

1、VSYNC流程示意

当我们通过setText改变TextView内容后,UI界面不会立刻改变,APP端会先向VSYNC服务请求,等到下一次VSYNC信号触发后,APP端的UI才真的开始刷新,基本流程如下:

setText最终调用invalidate申请重绘,最后会通过ViewParent递归到ViewRootImpl的invalidate,请求VSYNC,在请求VSYNC的时候,会添加一个同步栅栏,防止UI线程中同步消息执行,这样做为了加快VSYNC的响应速度,如果不设置,VSYNC到来的时候,正在执行一个同步消息;

2、view的invalidate

View会递归的调用父容器的invalidateChild,逐级回溯,最终走到ViewRootImpl的invalidate

 
 
 
 
  1. View.java 
  2.  void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, 
  3.             boolean fullInvalidate) { 
  4.             // Propagate the damage rectangle to the parent view. 
  5.             final AttachInfo ai = mAttachInfo; 
  6.             final ViewParent p = mParent; 
  7.             if (p != null && ai != null && l < r && t < b) { 
  8.                 final Rect damage = ai.mTmpInvalRect; 
  9.                 damage.set(l, t, r, b); 
  10.                 p.invalidateChild(this, damage); 
  11.             } 
  12. ViewRootImpl.java 
  13. void invalidate() { 
  14.     mDirty.set(0, 0, mWidth, mHeight); 
  15.     if (!mWillDrawSoon) { 
  16.         scheduleTraversals(); 
  17.     } 

ViewRootImpl会调用scheduleTraversals准备重绘,但是,重绘一般不会立即执行,而是往Choreographer的Choreographer.CALLBACK_TRAVERSAL队列中添加了一个mTraversalRunnable,同时申请VSYNC,这个mTraversalRunnable要一直等到申请的VSYNC到来后才会被执行;

3、scheduleTraversals

 
 
 
 
  1. ViewRootImpl.java 
  2.  // 将UI绘制的mTraversalRunnable加入到下次垂直同步信号到来的等待callback中去 
  3.  // mTraversalScheduled用来保证本次Traversals未执行前,不会要求遍历两边,浪费16ms内,不需要绘制两次 
  4. void scheduleTraversals() { 
  5.     if (!mTraversalScheduled) { 
  6.         mTraversalScheduled = true; 
  7.         // 防止同步栅栏,同步栅栏的意思就是拦截同步消息 
  8.         mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); 
  9.         // postCallback的时候,顺便请求vnsc垂直同步信号scheduleVsyncLocked 
  10.         mChoreographer.postCallback( 
  11.                 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 
  12.           
  13.         if (!mUnbufferedInputDispatch) { 
  14.             scheduleConsumeBatchedInput(); 
  15.         } 
  16.         notifyRendererOfFramePending(); 
  17.         pokeDrawLockIfNeeded(); 
  18.     } 

4、申请VSYNC同步信号

 
 
 
 
  1. Choreographer.java 
  2. private void postCallbackDelayedInternal(int callbackType, 
  3.         Object action, Object token, long delayMillis) { 
  4.     synchronized (mLock) { 
  5.         final long now = SystemClock.uptimeMillis(); 
  6.         final long dueTime = now + delayMillis; 
  7.         mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); 
  8.         if (dueTime <= now) { 
  9.          
  10.             scheduleFrameLocked(now); 
  11.         }  
  12.     } 

5、scheduleFrameLocked

 
 
 
 
  1. // mFrameScheduled保证16ms内,只会申请一次垂直同步信号 
  2. // scheduleFrameLocked可以被调用多次,但是mFrameScheduled保证下一个vsync到来之前,不会有新的请求发出 
  3. // 多余的scheduleFrameLocked调用被无效化 
  4. private void scheduleFrameLocked(long now) { 
  5.     if (!mFrameScheduled) { 
  6.         mFrameScheduled = true; 
  7.         if (USE_VSYNC) { 
  8.             if (isRunningOnLooperThreadLocked()) { 
  9.                 scheduleVsyncLocked(); 
  10.             } else { 
  11.                 // 因为invalid已经有了同步栅栏,所以必须mFrameScheduled,消息才能被UI线程执行 
  12.                 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); 
  13.                 msg.setAsynchronous(true); 
  14.                 mHandler.sendMessageAtFrontOfQueue(msg); 
  15.             } 
  16.         }   
  17.     } 

6、FrameDisplayEventReceiver

 
 
 
 
  1. private final class FrameDisplayEventReceiver extends DisplayEventReceiver 
  2.             implements Runnable { 
  3.         private boolean mHavePendingVsync; 
  4.         private long mTimestampNanos; 
  5.         private int mFrame; 
  6.         public FrameDisplayEventReceiver(Looper looper) { 
  7.             super(looper); 
  8.         } 
  9.         @Override 
  10.         public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { 
  11.             long now = System.nanoTime(); 
  12.             if (timestampNanos > now) { 
  13.              
  14.                 timestampNanos = now; 
  15.             } 
  16.              
  17.               if (mHavePendingVsync) { 
  18.                 Log.w(TAG, "Already have a pending vsync event.  There should only be " 
  19.                         + "one at a time."); 
  20.             } else { 
  21.                 mHavePendingVsync = true; 
  22.             } 
  23.              
  24.             mTimestampNanos = timestampNanos; 
  25.             mFrame = frame; 
  26.             Message msg = Message.obtain(mHandler, this); 
  27.              
  28.             msg.setAsynchronous(true); 
  29.             mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); 
  30.         } 
  31.         @Override 
  32.         public void run() { 
  33.             mHavePendingVsync = false; 
  34.              
  35.             doFrame(mTimestampNanos, mFrame); 
  36.         } 
  37.     } 

7、doFrame

 
 
 
 
  1. void doFrame(long frameTimeNanos, int frame) { 
  2.     final long startNanos; 
  3.     synchronized (mLock) { 
  4.      
  5.         if (!mFrameScheduled) { 
  6.             return; // no work to do 
  7.         } 
  8.        long intendedFrameTimeNanos = frameTimeNanos; 
  9.         startNanos = System.nanoTime(); 
  10.         final long jitterNanos = startNanos - frameTimeNanos; 
  11.          
  12.         if (jitterNanos >= mFrameIntervalNanos) { 
  13.             final long skippedFrames = jitterNanos / mFrameIntervalNanos; 
  14.              
  15.             if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { 
  16.              
  17.                 Log.i(TAG, "Skipped " + skippedFrames + " frames!  " 
  18.                         + "The application may be doing too much work on its main thread."); 
  19.             } 
  20.             final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; 
  21.                  
  22.             frameTimeNanos = startNanos - lastFrameOffset; 
  23.         } 
  24.         if (frameTimeNanos < mLastFrameTimeNanos) { 
  25.              
  26.             scheduleVsyncLocked(); 
  27.             return; 
  28.         } 
  29.            
  30.         mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); 
  31.          
  32.         mFrameScheduled = false; 
  33.          
  34.         mLastFrameTimeNanos = frameTimeNanos; 
  35.     } 
  36.     try { 
  37.           
  38.         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); 
  39.          
  40.         mFrameInfo.markInputHandlingStart(); 
  41.         doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); 
  42.          
  43.         mFrameInfo.markAnimationsStart(); 
  44.         doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); 
  45.          
  46.         mFrameInfo.markPerformTraversalsStart(); 
  47.         doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); 
  48.          
  49.         } else { 
  50.          
  51.             mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; 
  52.             mPrivateFlags &= ~PFLAG_DIRTY_MASK; 
  53.         } 
  54.         return renderNode; 
  55.     } 

10、绘制总结

总结

 

关于绘制还有很多知识点,后面会总结陆续发出来的;


当前文章:Vsync信号机制和UI刷新流程
转载源于:http://cdbrznjsb.com/article/djipdig.html

其他资讯

让你的专属顾问为你服务