阅读本文前,请您先点击上面的蓝色字体“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查看WindowManagerGlobal

public 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 + 

android token机制_你真的了解16.6ms刷新机制吗?相关推荐

  1. java垃圾回收机制_乐字节Java|GC垃圾回收机制、package和import语句

    本文接上一篇:乐字节Java|this关键字.static关键字.block块.本文是接着讲述JavaGC垃圾回收机制.package 和 import语句. 一.GC垃圾回收机制 GC全名:Garb ...

  2. jvm垃圾回收机制_深入理解JVM的垃圾回收机制

    ​如何判断对象已"死" Java堆中存放着几乎所有的对象实例,垃圾回收器在堆进行垃圾回收前,首先要判断这些对象那些还存活,那些已经"死去".判断对象是否已&qu ...

  3. java 委托机制_通过反射实现Java下的委托机制代码详解

    简述 一直对Java没有现成的委托机制耿耿于怀,所幸最近有点时间,用反射写了一个简单的委托模块,以供参考. 模块API public Class Delegater()//空参构造,该类管理委托实例并 ...

  4. java 反射机制_详解Java中的反射机制的优缺点

    一.什么是反射? 对于程序员来说,应该很少需要直接使用反射工具:之所以在语言中提供它们,是为了支持其他Java特性,比如对象序列化.Java Beans以及RMI.还有就是在很多框架中,也是应用到了反 ...

  5. python内存的回收机制_简单介绍python的垃圾回收机制

    这篇文章主要介绍了python垃圾回收机制的相关资料,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下 什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回 ...

  6. python的内存回收机制_关于python的变量使用回收机制

    a=3 print type(a) #a为整型 a=3L print type(a) #a为长整型 a=2.3 print type(a) #float a=2.3e10 print type(a) ...

  7. android rom结构_如何将新的ROM刷新到您的Android手机

    android rom结构 When you bought your phone it was cutting edge, had the latest version of Android, and ...

  8. python反爬虫机制_盘点一些网站的反爬虫机制

    因为 Python 语法简介以及强大的第三方库,所以我们使用它来制作网络爬虫程序.网络爬虫的用途是进行数据采集,也就是将互联网中的数据采集过来. 网络爬虫的难点其实并不在于爬虫本身.而是网站方为了避免 ...

  9. android 卡顿、ANR优化(1)屏幕刷新机制

    前言: 本文通过阅读各种文章和源码总结出来的,如有不对,还望指出 目录 正文 基础概念 视觉暂留 逐行扫描 帧 CPU/GPU/Surface: 帧率.刷新率.画面撕裂 画面撕裂 Android屏幕刷 ...

最新文章

  1. iOS 10道细节面试题
  2. IO异常 java.net.SocketException: Connection reset
  3. InfiniBand简介
  4. 后端:死磕18个Java8日期处理,工作必用!
  5. 网页设置页数/总页数_图书分配问题(分配最小页数)
  6. C语言课后习题(24)
  7. Matlab中错误使用mex
  8. 什么是技术档案(Technical Archives)?
  9. service数据保存_精通IPFS:IPFS 保存内容之下篇
  10. 青年歌手大奖赛_评委会打分 (一道比较简单的题目)
  11. 15.Object Manager
  12. 数学专业英语--2.5(几何)
  13. 【SPSS】SPSS学习笔记
  14. win10,对于VNR无法使用OCR光学识别的方法,仿照大佬们所讲,图解
  15. 手机(摩托罗拉、索爱、西门子、LG)大部分机型的cpu型号
  16. H3C-云计算技术专题培训(分享七)
  17. vue上线后图片不显示_vue页面中图片不显示解决
  18. smbd服务器-网络磁盘映射
  19. 在线考试系统的时间控制(倒计时)
  20. 持续集成在Hexo自动化部署上的实践

热门文章

  1. java 运算优先级_Java-运算符优先级
  2. cursor.execute(sql) 执行结果集是有记录的 但是num=cursor.rownumber 返回值为0
  3. 好文推荐系列--------(3)GruntJS 在线重载 提升生产率至新境界
  4. OSGI(面向Java的动态模型系统)
  5. C语言中字符串如何转换为二进制、八进制、十进制、十六进制
  6. C++总结笔记(二)面向对象
  7. 马云卸任CEO演讲全文:明天起生活将是我的工作
  8. ContentType的类型
  9. 用js将form表单同时提交到两个不同页面的方法
  10. excel亮灯怎么设置_Excel表格技巧—怎么给表格设置密码