网站建设高端定制,wordpress topnews,做网站用什么技术,百度云资源搜索阅读本文前#xff0c;请您先点击上面的蓝色字体“Android扫地僧”#xff0c;“关注”后再点击置顶公众号#xff0c;优质干货#xff0c;重磅资源第一时间送达。散人丶https://juejin.im/post/5ce686a46fb9a07ec754f470前言之前在整理知识的时候#xff0c;看到android屏…阅读本文前请您先点击上面的蓝色字体“Android扫地僧”“关注”后再点击置顶公众号优质干货重磅资源第一时间送达。散人丶https://juejin.im/post/5ce686a46fb9a07ec754f470前言之前在整理知识的时候看到android屏幕刷新机制这一块以前一直只是知道Android每16.6ms会去刷新一次屏幕也就是我们常说的60fpx那么问题也来了16.6ms刷新一次是什么一次是以这个固定的频率去重新绘制吗但是请求绘制的代码时机调用是不同的如果操作是在16.6ms快结束的时候去绘制的那么岂不是就是时间少于16.6ms也会产生丢帧的问题再者熟悉绘制的朋友都知道请求绘制是一个Message对象那这个Message是会放进主线程Looper的队列中吗那怎么能保证在16.6ms之内会执行到这个Message呢文章较长请耐心观看水平不足如果错误还望指出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); p.invalidateChild(this, damage); } ..... }}主要关注这个p最终调用的是它的invalidateChild()方法那么这个p到底是个啥ViewParent是一个接口那很明显p是一个实现类答案是ViewRootImpl我们知道View树的根节点是DecorView那DecorView的Parent是不是ViewRootImpl呢熟悉Activity启动流程的朋友都知道Activity 的启动是在 ActivityThread 里完成的handleLaunchActivity() 会依次间接的执行到 Activity 的 onCreate(), onStart(), onResume()。在执行完这些后 ActivityThread 会调用 WindowManager#addView()而这个 addView()最终其实是调用了 WindowManagerGlobal 的 addView() 方法我们就从这里开始看因为是隐藏类所以这里借助Source Insight查看WindowManagerGlobalpublic void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { synchronized (mLock) { ..... root new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { synchronized (mLock) { final int index findViewLocked(view, false); if (index 0) { removeViewLocked(index, true); } } throw e; } } public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { .... view.assignParent(this); ... } }void assignParent(ViewParent parent) { if (mParent null) { mParent parent; } else if (parent null) { mParent null; } }参数是ViewParent所以在这里就直接将DecorView和ViewRootImpl给绑定起来了所以也验证了上述的结论子View里执行invalidate()之类的操作最后都会走到ViewRootImpl里来ViewRootImpl##scheduleTraversals根据上面的链路最终是会执行到scheduleTraversals方法void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled true; mTraversalBarrier mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }方法不长首先如果mTraversalScheduled为false进入判断同时将此标志位置位true第二句暂时不管后续会讲到主要看postCallback方法传递进去了一个mTraversalRunnable对象可以看到这里是一个请求绘制的Runnable对象final class TraversalRunnable implements Runnable { Override public void run() { doTraversal(); } }void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing(ViewAncestor); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile false; } } }doTraversal方法里面又将mTraversalScheduled置位了false对应上面的scheduleTraversals方法可以看到一个是postSyncBarrier()而在这里又是removeSyncBarrier()这里其实涉及到一个很有意思的东西叫同步屏障等会会拉出来单独讲解然后调用了performTraversals()这个方法应该都知道了View 的测量、布局、绘制都是在这个方法里面发起的代码逻辑太多了就不贴出来了暂时只需要知道这个方法是发起测量的开始。这里我们暂时总结一下当子View调用invalidate的时候最终是调用到ViewRootImpl的performTraversals()方法的performTraversals()方法又是在doTraversal里面调用的doTraversal又是封装在mTraversalRunnable之中的那么这个Runnable的执行时机又在哪呢Choreographer##postCallback回到上面的scheduleTraversals方法中mTraversalRunnable是传递进了Choreographer的postCallback方法之中private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { synchronized (mLock) { final long now SystemClock.uptimeMillis(); final long dueTime now delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime now) { scheduleFrameLocked(now); } else { Message msg mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } }}可以看到内部好像有一个类似MessageQueue的东西将Runnable通过delay时间给存储起来的因为我们这里传递进来的delay是0所以执行scheduleFrameLocked(now)方法private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled true; if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else { Message msg mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } }}private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() mLooper;}这里有一个判断isRunningOnLooperThreadLocked看着像是判断当前线程是否是主线程如果是的话调用scheduleVsyncLocked()方法不是的话会发送一个MSG_DO_SCHEDULE_VSYNC消息但是最终都会调用这个方法public void scheduleVsync() { if (mReceiverPtr 0) { Log.w(TAG, Attempted to schedule a vertical sync pulse but the display event receiver has already been disposed.); } else { nativeScheduleVsync(mReceiverPtr); }}如果mReceiverPtr不等于0的话会去调用nativeScheduleVsync(mReceiverPtr)这是个native方法暂不跟踪到C里面去了看着英文方法像是一个安排信号的意思之前是把CallBack存储在一个Queue之中了那么必然有执行的方法void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c callbacks; c ! null; c c.next) { if (DEBUG_FRAMES) { Log.d(TAG, RunCallback: type callbackType