在activity中获取view的宽和高的一些探寻

首先抛出几个问题
1.如何在activity中获取view的宽和高?
2.下面引申出来了一个新的问题, onResume() 是什么时候调用的?还有 activity 的各个生命周期都是在哪被调用的?当然这就不是本文的内容了,这里不做过多的深入.
3.既然在 onCreate(); onStart(); onResume();中都无法直接获取 view 的宽高,所以确定 view 的测绘应该是在 onResume() 之后,那么 view 的测绘流程是重哪里开始的?

*********************我是分割线*************************
1.如何在activity中获取view的宽和高?(这里使用了 kotlin 就不改了)
@1.直接获取 view.width --- view.height 当然这是没什么用的
@2. view.post {
WLog.e("view:post {view.width} ---{view.height}")
}
@3. //多次回调,页面上view树每次发生变化都会进行回调
view.viewTreeObserver.addOnGlobalLayoutListener {
//不想多次回调可以移除
// view.viewTreeObserver.removeOnGlobalLayoutListener { this }
WLog.e("onResume():viewTree {view.width} ---{view.height}")
}

2.onResume() 是什么时候调用的?
首先来看切入点 ActivityThread.class.handleResumeActivity() (这里就不要纠结这个方法是在哪调用的了,内容又变多了)

Dingtalk_20220208132342_副本.jpg

可以看到这里执行完就已经切回到了 Activity 类里面了,然后我们在看看 Activity 类的情况
Dingtalk_20220208132342_副本1.jpg

最后可以看到终于找到了心心念念的 onResume() 方法,执行的流程就是这个流程,多了不会说

3.view 的测绘流程是重哪里开始的?
继续回到 ActivityThread.class.handleResumeActivity() 方法中,来看看在 performResumeActivity(token, finalStateRequest, reason);之后都干了什么.

微信截图_20220211104256.png

View decor = r.window.getDcorView(); 这个 decor 就是 PhoneWindow 里面的 decor ,这里装载了 activity 里面的 veiw

wm.addView(decor,l); 正式开始进入了view的测绘流程
decor.setVisibility(View.INVISIBNLE); 隐藏视图,防止软键盘弹出
去看 addView(); 方法 vm 就是 WindowManager , 实现了 ViewManager 所以也就有了 添加,更新,移除的能力


微信截图_20220211110050.png

先找到WindowMananger的实现类WindowManangerImpl 里面可以看到实现了上面的三个方法,并且用 mGlobal 做了一层转发,


微信截图_20220211110309.png

为什么这么做呢,一个页面就有一个 WindowManager 对象,一个应用有很多个 WindowManager ,这层转发就是把所有的 WindowManager 做了一个收拢,全部收拢到了 WindowManagerGlobal 类中,
收拢了入口和出口,对外暴露的只是一个方法的定义而已

好收一收,回到handleResumeActivity()方法,往下看实现了一个 ViewRootImpl 然后调用 vm.addView() 方法,从上面看,这个vm 就是WindowMananger,把decor添加到了windowMananger中,把传入进来的 Veiw(就是decor) 和 viewRoot 相关联了,重点来了,ViewRootImpl 这里关联之后就开始了 view 的 测量布局绘制流程

微信截图_20220211111008.png

因为到了ViewRootImpl ,介绍一下 ViewRootImpl
1.WindowSession 将 Window 添加到 WindowManagerService 中 IWindowSession mWindowSession 是一个 Ibinder 接口,通过它把当前窗口注册到 WindowMananger 中
2.页面 Viwe 树的顶层节点,但并不是一个View,只是实现了ViewParent ,关联 Window 和 View ,所有页面的跟布局都是 decorView,decorView 的 parent 就是 viewRootImpl
3.choreographer 接收 Vsync 同步信号触发 View 的三大流程 mChoreographer 接收系统发出的 Vsync垂直同步信号
,(垂直同步信号:系统会每隔一段时间发出一个信号,这个时间间隔是根据手机屏幕的刷新率确定的,目的60hz = 一秒60次)
4.WindowInputEventReceiver 接收屏幕输入事件,分发手势
5.接收 Vsync 同步信号,触发 View 的动画重绘
上面提到的属性构造方法里都有,太长了不截图了

好了,回到 vm.addView(); 上面说了 vm 就是 WindowManager ,走你,来到 WindowManager 的实现类 WindowManagerImpl,我们看到了 addView()方法


微信截图_20220211115050.png

继续跟进,来到了 WindowManagerGlobal 中,找到 addView() 方法.好长,来看最后的 setView()方法


微信截图_20220211115313.png

点进去往下看往下看找到 requestLayout() 方法,翻一下:把我们的窗口注册到 WindowService 之前,先来安排一次页面view树的布局,测绘工作,在这个方法里面就是真正开始了view的三大流程

微信截图_20220211130756.png

跟进 ,继续点进去


微信截图_20220211131222.png

可以看到有一个 checkThread](); 方法,再点进去可以看到这个著名的异常,Only the original thread that created a view hierarchy can touch its views.


微信截图_20220211131414.png

翻译:只有创建视图层次结构的原始线程才能触及其视图。(意思就是在异步线程中不能更新主线程UI)
这里面就有一个问题,在子线程中真的不能更新view么?
我们可以新建一个activity试一下, Thread({txText.setText("我是来自子线程")}).start() 结果是可以成功更新的,但是我们知道在子线程中更新view并不是所有场景都生效的,
如果对view的更新涉及到了 view.requestLayout() ,view.post,就不会成功

收一收,回到 requestLayout() ,我们看第二个方法,scheduleTraversals();根据方法名字就是安排一次任务的执行,

微信截图_20220211131827.png

这里面首先 mTraversalScheduled 进行同一帧重复多次请求的过滤,然后通过mHandler 发送了一条屏障消息,开始布局测量绘制工作,
接着调用了 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
使用 mChoreographer 去注册了一个垂直同步信号的监听的回调,每一次垂直信号的到达都会执行 mTraversalRunnable,

微信截图_20220211132143.png

好,来到 TraversalRunnable,可以看到调用了 doTraversal(); 接着跟进,首先重置标记为,然后移除了消息队列中的消息屏障,因为一旦调用了 doTraversal() 就说明了 UI 测绘这条任务已经开始执行了,使同步消息也可以执行,


微信截图_20220211132253.png

接着就开始了巨长的 performTraversals();这个方法的作用就是根据当前页面的view树,判断有没有view发生了变化,比如大小,位置,属性等等,来计算要不要对我们当前页面的view树进行重新测绘重新布局,以及重新绘制的操作,
关键的代码有
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);里面调用 view.measure();
performLayout(); 里面调用了 host.layout(); 这个host 就是 mView 也就是 decorView;
performDraw(); 里面先调用了内部方法 draw(); 然后到了 drawSoftware(); 哎?在里面发现 mView.draw(canvas);
这里就可以看出 activity 的布局测量绘制工作确实发生在了 onResume() 后面,真正的开始是在 VeiwRootImpl中
完结撒花

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容