记得看文章三部曲,点赞,评论,转发。
微信搜索【程序员小安】关注还在移动开发领域苟活的大龄程序员,“面试系列”文章将在公众号同步发布。

1.前言

最近看到几个技术群里都在吐槽目前面试八股文泛滥,作为技术担当的天才少年_决定要去各个大厂踢馆,用我扎实的八股文功底来教育一下他们。

2.正文

一路披荆斩棘,天才少年终于来到一个大厂的面试会议室。
是心动啊,糟糕眼神躲不掉,对你莫名的心跳,竟然停不了对你的迷恋~~

小伙子,我是今天的面试官,你不要对我抱有幻想,准备好了的话,咱们直接开始面试吧。

什么鬼,我的内心他怎么知道!

在Activity的oncreate方法中如何获取view的宽度和高度?


很简单,直接用view.post方法就可以获取到,代码如下所示:

view.post(new Runnable() {@Overridepublic void run() {//获取宽度和高度}});

嗯,为什么一定要调用post方法呢?如果直接调用getHeight取到的值是多少呢?

嘿嘿,绕来绕去,不就是view.post源码的执行流程嘛,天才少年岂是浪得虚名,为了熟读framework源码,我抛弃了小琴,那是个风高夜黑的晚上,小琴拉着我的手问我愿不愿意做孩子的父亲,我反手就是一巴掌,怎么能让小孩来打破我的单身生活。思绪飘得有点远,我推了推眼镜,试图掩盖留下的泪水,清了清嗓子,准备开始讲(装)解(逼)。

先看View.post的源码:

public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {return attachInfo.mHandler.post(action);}// Assume that post will succeed laterViewRootImpl.getRunQueue().post(action);return true;}

代码量不大,可以很明显的看出来,attchInfo对象起到决定性作用,他是否为空,决定下面流程的运转,我先站在上帝的视角跟你说一下,attchInfo是在ViewRootImpl的构造函数中初始化,ViewRootImpl是在onResume之后才会创建,具体的源码,后面我会单独写一篇文章来讲解。
这边你只需要知道,在onCrate方法里面,attchInfo为空,则会走到ViewRootImpl.getRunQueue().post(action);
RunQueue的源码如下所示:

static final class RunQueue {private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();//post发送的action(Runnable)放入HandlerAction对象中,然后存入mActions集合中void post(Runnable action) {postDelayed(action, 0);}void postDelayed(Runnable action, long delayMillis) {HandlerAction handlerAction = new HandlerAction();//post发送的action(Runnable)放入HandlerAction对象中handlerAction.action = action;handlerAction.delay = delayMillis;synchronized (mActions) {mActions.add(handlerAction);//存入mActions集合中}}void removeCallbacks(Runnable action) {final HandlerAction handlerAction = new HandlerAction();handlerAction.action = action;synchronized (mActions) {final ArrayList<HandlerAction> actions = mActions;while (actions.remove(handlerAction)) {// Keep going}}}void executeActions(Handler handler) {synchronized (mActions) {final ArrayList<HandlerAction> actions = mActions;final int count = actions.size();for (int i = 0; i < count; i++) {final HandlerAction handlerAction = actions.get(i);handler.postDelayed(handlerAction.action, handlerAction.delay);//循环遍历mActions中的HandlerAction对象,把Runnable发送到该handler的messageQueue中。}actions.clear();}}private static class HandlerAction {Runnable action;long delay;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;HandlerAction that = (HandlerAction) o;return !(action != null ? !action.equals(that.action) : that.action != null);}@Overridepublic int hashCode() {int result = action != null ? action.hashCode() : 0;result = 31 * result + (int) (delay ^ (delay >>> 32));return result;}}}

这代码有点多,可以稍微解释一下吗?

别急啊,装逼不需要思考的吗?
核心代码我已经注释过了,相信很好理解:当调用ViewRootImpl.getRunQueue().post(action)时,该action被存储在RunQueue类中的集合
mActions中,等外部调用executeActions方法时,才会真正的执行action的回调。再一次借用上帝视角,这个handler其实就是UI线程的,所以这个时候才是真正的把action放到UI线程对应的MessageQueue中。

那是哪个外部调用executeActions方法的呢?可以讲一讲吗?

肯定可以啊,我来就是面试的,难不成来跟你相亲的。

我们知道View的渲染离不开ViewRootImpl类,页面绘制从requestLayout开始,那我们今天就从requestLayout方法往下讲,如果你想听requestLayout前面的东西,二面咱们细聊,又是一个勾引面试欲望的小技巧。

    @Overridepublic void requestLayout() {checkThread();mLayoutRequested = true;scheduleTraversals();}

checkThread前面分析过,就是检查更新线程跟新建view的线程是否是用一个,跟今天要讲的不相关,暂时忽略,我们看scheduleTraversals()

    void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().postSyncBarrier();//设置同步屏障mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//通过mChoreographer垂直脉冲刷新页面,调用mTraversalRunnablescheduleConsumeBatchedInput();}}

可以看到这边设置了同步屏障,同步屏障和异步消息这个咱们后面单独用一篇文章来讲,这边你只需要知道,用同步屏障后,优先执行异步消息,而mChoreographer内部必然是异步消息,见下面的代码:

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);//设置异步消息
mHandler.sendMessageAtTime(msg, dueTime);

继续看mTraversalRunnable的代码,如下所示:

final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}}
void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");try {performTraversals();} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}}

继续看performTraversals();

private void performTraversals() {// cache mView since it is used so much below...final View host = mView;//省略一些代码host.dispatchAttachedToWindow(attachInfo, 0);//**注意performMeasure,performLayout,performDraw在这个方法后面哦**mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);host.fitSystemWindows(mFitSystemWindowsInsets);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//测量performLayout(lp, mWidth, mHeight); // 布局performDraw();//绘制

可以看到在performTraversals方法中,调用了host.dispatchAttachedToWindow(attachInfo, 0),host其实View,那我们看下View的源码:

void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info; // 这个时候attachinfo才被赋值... 省略部分代码// Transfer all pending runnables.if (mRunQueue != null) {mRunQueue.executeActions(info.mHandler); // 终于看到他了//executeActions方法会循环遍历mActions中的HandlerAction对象,把Runnable发送到该handler的messageQueue中。mRunQueue = null; }
}

到这里基本真想大白了,调用view.post方法并不是实时调用,而是被存储在RunQueue类中的集合
mActions中,等外部调用executeActions方法,而executeActions是在View的dispatchAttachedToWindow方法中被调用。
时序图如下所示:

看你的分析,executeActions方法是在页面刷新方法(performMeasure,performLayout,performDraw)后面,明明页面刷新在调用之后,为什么view.post还能拿到view的宽和高呢?

可以啊,这都能看出来,不过我既然来了,就打算得到你,不是,就是回答你。
还记得我前面讲到同步屏障吗,怕你不记得,代码再贴给你看下

    void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().postSyncBarrier();//设置同步屏障mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//通过mChoreographer垂直脉冲刷新页面,调用mTraversalRunnablescheduleConsumeBatchedInput();}}

mChoreographer.postCallback其实也是一个Handler消息,并且是异步消息,上面已经讲过了,所以他会优先执行,而通过view.post发送的消息是同步消息,当设置了同步屏障后,会优先执行异步消息,所以必须等performMeasure,performLayout,performDraw流程走完后,view.post发送的消息才能真正的执行,自然也就可以拿到view的宽和高了。

可以了,你对handler,view,ViewRootImpl了解很深刻,今天有点晚了,消息屏障,view的创建流程,mChoreographer发送消息的流程,咱们后面再聊。


微信搜索【程序员小安】“面试系列(java&andriod)”文章将在公众号同步发布。

面试官问我:View.post为什么能够获取View的宽高相关推荐

  1. 【059期】面试官问:序列化是什么,为什么要序列化,如何实现?

    >>号外:关注"Java精选"公众号,回复"面试资料",免费领取资料!"Java精选面试题"小程序,3000+ 道面试题在线刷, ...

  2. 后处理程序文件大小的变量_【每日一题】(17题)面试官问:JS中事件流,事件处理程序,事件对象的理解?...

    关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 2020,实「鼠」不易 2021,「牛」转乾坤 风劲潮涌当扬帆,任重道远须奋蹄! 一.前言 2020.12.23 立 ...

  3. 已经成功拿到了几个offer的我来告诉你,Android面试官问的一些问题,看完这一篇就没有拿不到的offer

    前言 我是2020年毕业于中南大学的计算机学院的,大家可以叫我小吴,我嘞毕业之后在华为实习了差不多一年多,一直都从事着Android开发. 然后2021年的时候因为我自己的一些原因打算离职到外面看看, ...

  4. 面试官问你想找什么工作_找工作时如何面试面试官

    面试官问你想找什么工作 在技​​术面试中要问的十二个问题 (Twelve questions to ask at tech interviews) I've just come off six wee ...

  5. 面试官问:select......for update会锁表还是锁行?

    欢迎关注方志朋的博客,回复"666"获面试宝典 select查询语句是不会加锁的,但是select .......for update除了有查询的作用外,还会加锁呢,而且它是悲观锁 ...

  6. 面试官问:数据库 delete 表数据,磁盘空间还是被一直占用,为什么?

    以下文章来源方志朋的博客,回复"666"获面试宝典 最近有个上位机获取下位机上报数据的项目,由于上报频率比较频繁且数据量大,导致数据增长过快,磁盘占用多. 为了节约成本,定期进行数 ...

  7. 面试官问:Kafka 会不会丢消息?怎么处理的?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! Kafka存在丢消息的问题,消息丢失会发生在Broker, ...

  8. 面试官问:Integer 如何实现节约内存和提升性能的?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Byte_Liu 来源:https://urlify.cn/ ...

  9. 面试官问我:一个 TCP 连接可以发多少个 HTTP 请求?我竟然回答不上来...

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 作者 | 松若章 来源 | https://zhuanlan.zhihu.com/p/6142 ...

  10. 面试官问:请拿出一段体现你水平的代码,我该如何回答?

    程序员面试,免不了被问代码问题.如果面试官问你,最能代表你的当下水平的代码是什么?你该怎么回答呢?知乎的几位作者给出了优秀答案. 每天下班前半小时都会运行这段. #include <stdlib ...

最新文章

  1. PAT1123 Is It a Complete AVL Tree(AVL树完全二叉树)
  2. mysql里的ibdata1文件
  3. 谈谈CListCtrl如何调整行高
  4. Java,AXIS,webService 调用 完整实例 xml 入参出参
  5. Linux下快速设定ip bond
  6. OSSemPost()--发出一个信号量
  7. jquery获取前一个月日期
  8. SQL Server 2016 新功能之综述
  9. RAX,eax,ax,ah,al 关系
  10. 自动驾驶——标注工具(js+electron)的开发笔记(基于Create-React-App)
  11. java 8-6 抽象的练习
  12. deepinu盘制作工具_U盘引导盘制作工具Rufus 3.11.1678 正式版
  13. 3.Chrome开发者工具不完全指南(二、进阶篇)
  14. 七日杀a17服务器修改,七日杀a17作弊指令
  15. opencv图像分析与处理(6)- 二维取样定理与二维傅里叶变换
  16. 无状态编程, lambda 表达式中传入的局部变量,为什么需要是不可变的(final)
  17. 微软服务器上市时间,微软Office 2010全球发布会时间确定
  18. 编写strcpy函数
  19. SAP 物料可用性检查
  20. python操作数据库工具类

热门文章

  1. Linux 安装宝塔
  2. C++实现服务器发送图片
  3. #711 – 在拖拽的过程中改变鼠标样式(Changing the Mouse Cursor While Dragging)
  4. 用mobilenet模型跑tensorflow CNN的样例:image_retrain.py和label_image.py
  5. ArcGIS Server 报错 set the request‘s mode to ‘no-cors‘ to fetch the resource with CORS disabled.
  6. Python - 寻找最大矩形 数字版矩阵版
  7. 单调栈求最大面积(模板)
  8. 怎么做好团队代码review
  9. 大数据与人工智能都分不清楚,还想走向人生巅峰?
  10. oracle vm virtualbox安装mac os,虚拟机VirtualBox安装MAC OS 10.12图文教程