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

阅读本文前,请您先点击上面的蓝色字体“Android扫地僧”“关注”后再点击置顶公众号,优质干货,重磅资源第一时间送达。

散人丶

https://juejin.im/post/5ce686a46fb9a07ec754f470

f2d44b63d54ea8f7ea429ffa8b95ec06.png

前言

之前在整理知识的时候,看到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 + 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

dfa2.java 原理_DFA编程练习2

题目: 请设计DFA, 使其接受全部含有奇数个1的串, 假定 ∑ {0, 1}.解:DFA可能出现两个个状态:qeven: 读入了偶数个1的串.qodd: 读入了奇数个1的串, 该状态也是终结状态(accept state).它们的状态转移图如下:编写程序, 运行效果如下:测试用例说明:0000不被上图的DFA接受1111不被…

Asp.net的HTTP请求处理过程

说明&#xff1a; &#xff08;1&#xff09;、客户端浏览器向服务器发出一个Http请求&#xff0c;此请求会被inetinfo.exe进程截获&#xff0c;然后转交给 aspnet_isapi.dll进程&#xff0c;接着它又通过Http Pipeline的管道&#xff0c;传送给aspnet_wp.exe这个进程&#xff…

ubuntu13.10 编译时 关于链接xlib 库阶段出错的问题解决

/usr/bin/ld: fmouse_main.o: undefined reference to symbol XFlush /usr/lib/i386-linux-gnu/libX11.so.6: error adding symbols: DSO missing from command line collect2: error: ld returned 1 exit status make: *** [freepen_drv] 错误 1 以上为错误提示&#xff0c…

fread读取整个文件_qt如何实现大文件的加载和显示

最近研究了下如何用qt的原生控件来加载和显示大文件&#xff08;>1G&#xff09;&#xff0c;分享下一些摸索经验。下文源码&#xff1a;compilelife/loginsight​github.com文件的内存映射在开始qt部分之前&#xff0c;我们先了解一个概念——文件的内存映射。我们知道一般…

[转]listview中设置背景图片后 拉动变黑

经本人亲测有效...在Android中&#xff0c;ListView是最常用的一个控件&#xff0c;在做UI设计的时候&#xff0c;很多人希望能够改变一下它的背景&#xff0c;使他能够符合整体的UI设计&#xff0c;改变背景背很简单只需要准备一张图片然后指定属性 android:background"d…

编译pjsip2.1.0 vidgui程序时,xlib保错问题

/usr/lib/i386-linux-gnu/libX11.so.6: error adding symbols: DSO missing from command line 以上为错误提示&#xff0c;为链接阶段未指定正确的库文件导致&#xff1b; 解决办法&#xff1a; 修改pro文件&#xff0c;增加下面这一行 LIB -lX11 -L/usr/lib/i386-linux-g…

linux内核编译及添加系统调用(hdu)_浅谈关于Linux内核write系统调用操作的原子性

Linux系统的write调用到底是不是原子的。网上能搜出一大堆文章&#xff0c;基本上要么是翻译一些文献&#xff0c;要么就是胡扯&#xff0c;本文中我来结合实例来试着做一个稍微好一点的回答。先摆出结论吧。结论包含两点&#xff0c;即write调用不能保证什么以及write调用能保…

java 判断对象为控制_Java流程控制

Java流程控制1、Scanner对象①java.util.Scanner是Java5的新特性&#xff0c;可以通过Scanner类来获取用户的输入。②基本语法&#xff1a;1 Scanner snew Scanner(System.in);③通过next()和nextLine()方法接受用户输入&#xff0c;通过hasNext()和hasNextLine()方法来判断用户…

lsb_release -a 查询系统版本

LSB是Linux Standard Base的缩写&#xff0c;lsb_release命令用来显示LSB和特定版本的相关信息。如果使用该命令时不带参数&#xff0c;则默认加上-v参数。 -v, --version 显示版本信息 -i, --id 显示发行版的ID -d, --description 显示该发行版的描述信息 -r, --release 显示当…

directx最终用户运行时_运维定位服务故障时,前5分钟都在忙啥?

遇到服务器故障&#xff0c;问题出现的原因很少可以一下就想到。我们基本上都会从以下步骤入手&#xff0c;这些也是绝大多数运维工程师在定位故障时前几分钟的主要排查点&#xff1a;一、尽可能搞清楚问题的前因后果不要一下子就扎到服务器前面&#xff0c;你需要先搞明白对这…

Hadoop源代码分析(二)

下面给出了Hadoop的包的功能分析。 Package Dependences tool 提供一些命令行工具&#xff0c;如DistCp&#xff0c;archive mapreduce Hadoop的Map/Reduce实现 filecache 提供HDFS文件的本地缓存&#xff0c;用于加快Map/Reduce的数据访问速度 fs 文件系统的抽象&…

java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势

关于线程安全&#xff0c;线程锁我们经常会用到&#xff0c;但你的使用姿势正确不&#xff0c;反正我用错了好长一段时间而不自知。所以有了这篇博客总结下线程锁的正确打开姿势废话不说看例子一&#xff0c;对整个方法进行加锁1&#xff0c;对整个方法进行加锁&#xff0c;不同…

IDE--ubuntu下安装 Source insight

2013-06-03 09:05 74人阅读 评论(0) 收藏 举报 习惯了在source insight下编辑阅读源码&#xff0c;在linux下用vi总是用不好 &#xff0c;还是在ubuntu上用回熟悉的source insight。 在ubuntu中&#xff0c;安装windows程序用wine&#xff0c;然后用wine安装windows软件即可。…

python中什么是数据驱动_利用Python如何实现数据驱动的接口自动化测试

前言 大家在接口测试的过程中&#xff0c;很多时候会用到对CSV的读取操作&#xff0c;本文主要说明Python3对CSV的写入和读取。下面话不多说了&#xff0c;来一起看看详细的介绍吧。 1、需求 某API&#xff0c;GET方法&#xff0c;token,mobile,email三个参数 token为必填项 mo…

密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制

写这篇文章之前&#xff0c;我去百度了一下啥叫锁&#xff0c;百度百科上写道&#xff1a;置于可启闭的器物上&#xff0c;以钥匙或暗码开启。确实我们一般理解的锁就是门锁&#xff0c;密码锁&#xff0c;但是在计算机科学中&#xff0c;锁又是啥&#xff0c;说实话&#xff0…

对cookie和子cookie操作的封装

1 /**2 * 封装cookie的操作3 * type {Object}4 */5 var CookieUtil {6 /**7 * 根据cookie的名字获取相应的值8 * param name cookie名字9 * return {*}10 */11 get:function (name) {12 //对name进行URL编码13 var cookieName…

解决Ubuntu下切换到root用户后没有声音问题

Ubuntu在root用户下&#xff0c;为了安全考虑默认是关闭了声音系统的。 如果要开很简单&#xff0c;因为root登录后pulseaudio没有启动。所以要先启动它 将root加到pulse和pulse-access组&#xff1a; sudo usermod -a -G pulse-access root gpasswd -a root pulse gpasswd -…

lua 给userdata设置元表_lua学习之复习汇总篇

第六日笔记1. 基础概念程序块定义在 lua 中任何一个源代码文件或在交互模式中输入的一行代码程序块可以是任意大小的程序块可以是一连串语句或一条命令也可由函数定义构成&#xff0c;一般将函数定义写在文件中&#xff0c;然后用解释器执行这个文件换行在代码中不起任何作用&a…

java代码请求2次_Android基于OkHttpUtils网络请求的二次封装

OkHttpUtils网络请求为什么进行二次封装&#xff1f;1、减少代码量2、后期换网络处理框架方便二次封装的实现原理1、将网络请求提取在一个方法中2、对里面的可变参数,可以通过参数传递过去,也可以提供一个set方法传递过去3、对于请求失败和成功,我们可以使用接口回调,让调用该方…

集群服务负载均衡------LVS

个人的理解&#xff0c;以一种通俗易懂的方式讲述出来&#xff0c;如果有哪些地方说的不正确的话&#xff0c;希望大家留言指出来。笔者会非是常的感谢&#xff01; Cluster服务器集群&#xff0c;直接理解为一些单一的服务器的集合通过某种方式组合起来&#xff0c;为客户端提…