
屏幕刷新帧率不稳定,掉帧严重,无法保证每秒60帧,导致屏幕画面撕裂;
今天我们来讲解下Vsync机制和UI刷新流程
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可能被其他操作占用了,这就是卡顿出现的原因;
当我们通过setText改变TextView内容后,UI界面不会立刻改变,APP端会先向VSYNC服务请求,等到下一次VSYNC信号触发后,APP端的UI才真的开始刷新,基本流程如下:
setText最终调用invalidate申请重绘,最后会通过ViewParent递归到ViewRootImpl的invalidate,请求VSYNC,在请求VSYNC的时候,会添加一个同步栅栏,防止UI线程中同步消息执行,这样做为了加快VSYNC的响应速度,如果不设置,VSYNC到来的时候,正在执行一个同步消息;
View会递归的调用父容器的invalidateChild,逐级回溯,最终走到ViewRootImpl的invalidate
- View.java
 - void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
 - boolean fullInvalidate) {
 - // Propagate the damage rectangle to the parent view.
 - 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);
 - p.invalidateChild(this, damage);
 - }
 - ViewRootImpl.java
 - void invalidate() {
 - mDirty.set(0, 0, mWidth, mHeight);
 - if (!mWillDrawSoon) {
 - scheduleTraversals();
 - }
 - }
 
ViewRootImpl会调用scheduleTraversals准备重绘,但是,重绘一般不会立即执行,而是往Choreographer的Choreographer.CALLBACK_TRAVERSAL队列中添加了一个mTraversalRunnable,同时申请VSYNC,这个mTraversalRunnable要一直等到申请的VSYNC到来后才会被执行;
- ViewRootImpl.java
 - // 将UI绘制的mTraversalRunnable加入到下次垂直同步信号到来的等待callback中去
 - // mTraversalScheduled用来保证本次Traversals未执行前,不会要求遍历两边,浪费16ms内,不需要绘制两次
 - void scheduleTraversals() {
 - if (!mTraversalScheduled) {
 - mTraversalScheduled = true;
 - // 防止同步栅栏,同步栅栏的意思就是拦截同步消息
 - mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
 - // postCallback的时候,顺便请求vnsc垂直同步信号scheduleVsyncLocked
 - mChoreographer.postCallback(
 - Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
 - if (!mUnbufferedInputDispatch) {
 - scheduleConsumeBatchedInput();
 - }
 - notifyRendererOfFramePending();
 - pokeDrawLockIfNeeded();
 - }
 - }
 
- Choreographer.java
 - private void postCallbackDelayedInternal(int callbackType,
 - Object action, Object token, long delayMillis) {
 - synchronized (mLock) {
 - final long now = SystemClock.uptimeMillis();
 - final long dueTime = now + delayMillis;
 - mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
 - if (dueTime <= now) {
 - scheduleFrameLocked(now);
 - }
 - }
 - }
 
- // mFrameScheduled保证16ms内,只会申请一次垂直同步信号
 - // scheduleFrameLocked可以被调用多次,但是mFrameScheduled保证下一个vsync到来之前,不会有新的请求发出
 - // 多余的scheduleFrameLocked调用被无效化
 - private void scheduleFrameLocked(long now) {
 - if (!mFrameScheduled) {
 - mFrameScheduled = true;
 - if (USE_VSYNC) {
 - if (isRunningOnLooperThreadLocked()) {
 - scheduleVsyncLocked();
 - } else {
 - // 因为invalid已经有了同步栅栏,所以必须mFrameScheduled,消息才能被UI线程执行
 - Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
 - msg.setAsynchronous(true);
 - mHandler.sendMessageAtFrontOfQueue(msg);
 - }
 - }
 - }
 - }
 
- private final class FrameDisplayEventReceiver extends DisplayEventReceiver
 - implements Runnable {
 - private boolean mHavePendingVsync;
 - private long mTimestampNanos;
 - private int mFrame;
 - public FrameDisplayEventReceiver(Looper looper) {
 - super(looper);
 - }
 - @Override
 - public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
 - long now = System.nanoTime();
 - if (timestampNanos > now) {
 - timestampNanos = now;
 - }
 - if (mHavePendingVsync) {
 - Log.w(TAG, "Already have a pending vsync event. There should only be "
 - + "one at a time.");
 - } else {
 - mHavePendingVsync = true;
 - }
 - mTimestampNanos = timestampNanos;
 - mFrame = frame;
 - 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);
 - }
 - }
 
- void doFrame(long frameTimeNanos, int frame) {
 - final long startNanos;
 - synchronized (mLock) {
 - if (!mFrameScheduled) {
 - return; // no work to do
 - }
 - long intendedFrameTimeNanos = frameTimeNanos;
 - startNanos = System.nanoTime();
 - final long jitterNanos = startNanos - frameTimeNanos;
 - if (jitterNanos >= mFrameIntervalNanos) {
 - final long skippedFrames = jitterNanos / mFrameIntervalNanos;
 - if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
 - Log.i(TAG, "Skipped " + skippedFrames + " frames! "
 - + "The application may be doing too much work on its main thread.");
 - }
 - final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
 - frameTimeNanos = startNanos - lastFrameOffset;
 - }
 - if (frameTimeNanos < mLastFrameTimeNanos) {
 - scheduleVsyncLocked();
 - return;
 - }
 - mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
 - mFrameScheduled = false;
 - mLastFrameTimeNanos = frameTimeNanos;
 - }
 - try {
 - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
 - mFrameInfo.markInputHandlingStart();
 - doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
 - mFrameInfo.markAnimationsStart();
 - doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
 - mFrameInfo.markPerformTraversalsStart();
 - doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
 - } else {
 - mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
 - mPrivateFlags &= ~PFLAG_DIRTY_MASK;
 - }
 - return renderNode;
 - }
 
关于绘制还有很多知识点,后面会总结陆续发出来的;
                网页名称:Vsync信号机制和UI刷新流程
                
                URL地址:http://www.csdahua.cn/qtweb/news15/465365.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网