Android 重学系列 WMS在Activity启动中的职责 计算窗体的大小(四)
通过启动窗口为例子,大致上明白了WMS是如何添加,更新,移除窗口的工作原理。本文将会重点聊一聊窗口的大小计算逻辑。
下面的源码都是来自Android 9.0
计算窗口的大小和Android 4.4相比变化很大。花了一点心思去重新学习了。在Android 4.4中,窗体的计算在onResume中调用了ViewRootImpl调用relayoutWindow对整个Window重新测量窗口大小以及边距。
relayoutWindow这个方法是做什么的呢?当我们在Activity的生命周期到达了onResume的阶段,此时ViewRootImpl的setView,开始走渲染的View的流程,并且调用requestLayout开始测量渲染。其中有一个核心的逻辑就是调用WMS的relayoutWindow,重新测量Window。
在Android 9.0中把这个流程和DisplayContent绑定起来。让我们稍微解剖一下这个方法。
relayout大致上要做了以下的事情:
relayout的方法有点长,本次我们将关注这一部分核心的逻辑。分别是两个方法:
能看到在这里面对performSurfacePlacementLoop做最多为6次的循环,这六次循环做什么呢?
能看到这里面的核心逻辑,首先会检查WMS下mForceRemoves集合中是否还有对象。有则调用removeImmediately清空WindowState的中SurfaceControl和WindowContainer之间的绑定和Surface对象,以及销毁WindowAnimator中的Surface。
做这个得到目的很简单,因为下一个步骤将会申请一个Surface对象,而此时如果Android系统内存过大了(OOM),mForceRemoves就存在对象,就可以销毁不需要的Surface。这一点的设计和Davlik虚拟机申请对象时候的思路倒是一致的。
销毁需要一点时间,因此就需要做一个250毫秒的的等待。接着会调用RootWindowContainer的performSurfacePlacement做真正的执行。最后会通过handler通过ViewServer通知事件给DebugBridge调试类中。
每一次loop的最后,如果发现RootWindowContainer需要重新测量,就会把当前这个方法,放入Handler中,等待下次的调用,也是调用6次。这样就能最大限度的保证在这段时间内Window能够测量每一次的窗体参数。
下面这个方法十分长,我们只看核心;
我在上面划分了9个部分:
这里只给总览,之后有机会再进去里面抓细节。
我们能够看到无论是在哪里,如果窗口发生了变化,都会调用updateFocusedWindowLocked方法。实际上这个方法才是真正的核心测量窗口大小逻辑。
这里注意一下isWindowChange是判断输入法焦点是否一致,而窗体焦点则是通过不同的WindowState来判断。
实际上核心测量的真正动作是DisplayContent.performLayout。我们仔细一想也就知道,在Android 9.0的时候,DisplayContent象征着逻辑屏幕,我们讨论无分屏的情况,实际上就是指我们当前窗体铺满逻辑显示屏各个边距的大小。
在正式开始聊窗体大小的测量之前,实际上,在Android系统中,为了把Window各个边界标记出来,实际上随着时代和审美潮流的演进,诞生越来越多的边距类型,我们往往可以通过这些边距来测定窗体的大小。
在DisplayFrame中有了大致的分区,如下:
可以看到,这些窗体的边距实际上是跟着这些年潮流走的。如Android 7.0的自由窗体模式,嵌套窗体模式,刘海屏等等,这些边距的共同作用,才会诞生一个真正的Window大小。有了这些基础知识之后,我们去看看测量大小的逻辑。
我们这里把这个方法拆成如下几个部分:
能看到,此时会设置当前显示屏幕的大小,以及获取过扫描区域,还会判断当前手机屏幕是否支持刘海屏。这一切实际上都是由硬件回馈到DisplayService,我们再从中获取的信息。
实际上如果有读者注意到我写的WMS第一篇就会看到实际上WMS初始化的时候,我们能够看到WMS会初始化一个WindowManagerPolicy的策略,而这个策略就是PhoneWindowManager。实际上这也支持了系统开发自定义策略,从而办到自己想要的窗体计算结果。
首先初始化几个参数,父窗体,屏幕,过扫描,可见区域,输入法区域为当前逻辑显示屏的大小,等到后面做裁剪。
能看到所有的事情实际上是关注的是系统UI上的判断,检测NavBar,StatusBar大小。最后再判断当前刘海屏的不允许交叉的区域顶部和显示屏顶部哪个大。如果mDisplayCutoutSafe的top大于mUnrestricted的top,说明mDisplayCutoutSafe在mUnrestricted下面,也就是我上面那个包含一段黑色的区域。此时会拿稳定的应用区域和刘海区域顶部的最大值,作为刘海屏幕的区域。这样就能保证刘海屏的顶部就是状态栏。
提一句如果NavigationBar隐藏,则会创建一个虚假的区域把输入事件都捕捉起来。
里面有四个关键函数:
可以看到所有的所有的间距将会设置为mUnrestricted的初始宽高,也就是不包含OverScan区域。如果是遇到刘海屏,则会根据设置的SafeInset区域来设置mDisplayCutoutSafe的安全区域。也就是我上面那种情况。比如设置了LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT这种情况,显示区域将不会超过刘海屏的底部。
我们关注到mTmpNavigationFrame这个对象的赋值,在正常的情况下的范围是如下:
此时mStable和mStableFullscreen区域的底部都是对应着top,也就是对应着Navigation顶部。System系统元素的底部也是Navigation顶部。
最后经过computeFrameLw重新计算这个区域的值。这个方法稍后会聊到,但是在正常手机开发中,其实是没有变化的。也就说,实际上对于mNavigationBar来说:
同理对于statusBar来说:
注意,此时如果statusBar可见,则做如下计算:
这种情况挺常见的,我们从一个隐藏状态栏的页面跳转到有状态栏的页面,国有有个PopupWindow,你能看到这个popwindow会明显向下移动。
在这个方法中mScreenDecorWindows这个集合实际上是在adjustWindowParamsLw以及prepareAddWindowLw这两个方法中加入。加入的条件是,每当有新的Window加入(WMS的addView)或者Window需要重新调整(WMS的relayoutWindow),当前新增得到Window或者需要重新relayout的Window有StatusBar有权限,且显示则会添加到mScreenDecorWindows集合。
mScreenDecorWindows从上面的描述,能得知实际上这个步骤还没有根据层级作区分。但是没关系,此时仅仅只是初步的测量。
明白了mScreenDecorWindows之后,我们阅读上面这个方法就很简单了。
layoutScreenDecorWindows做的事情就如名字一样,要测量Window上装饰部分,如StatusBar,如输入法。此时经过循环,自尾部往头部调用所有的WindowState的computeFrameLw计算每一个WindowState的对应Window的窗体大小。
当计算出每一个窗体大小之后,将会把事件分成两个情况,当计算出来的当前的Window的left和top都小于等于0,也就是说,当前的Window的顶部边缘并且左边缘超过了当前的屏幕。
说明了有什么东西在右下侧把整个Window定上去了。因此dockFrame的计算就很简单了:
如果计算出来的bottom大于等于屏幕高度且right大于等于屏幕宽度。说明有什么东西在左上方把整个Window顶下去了。
最后再设置这个把displayFrames的可见等区域都设置为dockFrame。联合上下文,实际上这里就是把整个区域的顶部移动到了statusBar之下。
杭供知爱: 一、刷recovery和无痛ROOT.(刷recovery和无痛ROOT都是同时完成的,所以这两个就放到一起讲了,想降低或者升级recovery的版本都可以用这个方法). 关于recovery.通俗点讲,android手机上所说的recovery相当于电脑上的DOS.在电...
山阳区19337541857: 安卓智能网络电视盒一直重启怎么办? - ?
杭供知爱: 安卓电视盒子无限重启的主要一般出现在系统方面,也不排除设备故障:1、安装的软件出现问题,多是第三方,和系统之间运行冲突,所以第三方的软件一定要到渠道安全的第三方应用市场或者官网下载. 2、如果盒子上带有复位键,可以尝...
山阳区19337541857: 请问我重刷安卓系统,是否需要刷回WM再刷安卓,有没有更简单的方法??
杭供知爱: 如果是magldr线刷的只要在开机时长按关机键 然后出来的菜单中选择 usb flash .. 然后用数据线连接电脑,即可刷
山阳区19337541857: 如何自学 Android 编程 - ?
杭供知爱: 首先,自学的话,书是必不可少的,我推荐几本学习编程的书,作为过来人,这些书非常适合初学者.关于java的学习,个人非常推荐《java编程艺术》和《thinking in java》.《java编程艺术》这本书好像有七百多页,我也不是很记得,这本书...
山阳区19337541857: 自学的Android的学习路线 - ?
杭供知爱: 第一阶段:Java面向对象编程 第二阶段:Java Web开发 第三阶段:android UI编程 第四阶段:android网络编程与数据存储 第五阶段:android手机硬件管理 书籍: Android编程入门教程andbook Google Android SDK 开发范例大全
山阳区19337541857: 刷完安卓重启又回到WM了,怎么回事啊?
杭供知爱: 我晕,刷完重启后当然回到wm了,又不是直刷,需要再次启动的,如果想开机启动就去安卓系统,去论坛里找个软件吧,可以实现的
山阳区19337541857: 你好.我想应聘的是一家打制造印机中的硒鼓的公司.我想学习WMS.要怎么学习WMS - ?
杭供知爱: WMS就是仓库管理:平时负责出入库报表,库存管理,确保库存精确.库存低时提示进行采购.得仓库的一系列管理.如果企业没上系统,则需熟悉EXCEL表格的制作报表.上系统就最好了,系统化的管理仓库.
山阳区19337541857: 学习android开发的前提是什么? java要学到什么程度? - ?
杭供知爱: core java 必须学会 也就是基本语法. J2SE部分 会了这之后在学起来相对轻松点
山阳区19337541857: 学安卓软件开发需要多长时间? - ?
杭供知爱: 首先你要学习 1、java编程语言 2、android布局,框架,生命周期,各种组件,广播等 3、做几个demo因人而异了,如果0基础,java估计就要2~3个月. android的话也要2~3个月,准备半年时间吧.天天学
山阳区19337541857: 请教Android学习路线 - ?
杭供知爱: Android开发现在占据移动端的半壁江山,越来越多的企业需要大量的Android开发人员.第一阶段:Java核心技术0基础学Android开发,从Java发展史开始,本阶段课程结合开发环境讲解基础语法,熟悉程序结构,再深入细致的介绍Java中面...