做家装图接单网站,怎么做网站结构拓扑图,做网站用什么配置的笔记本,网站建设发言材料1 问题描述
为系统和锁屏分别设置两张不同的壁纸#xff0c;然后在锁屏界面长按Power调起google语音助手后#xff0c;有时候会出现壁纸不可见的情况#xff0c;如以下截图所示#xff1a; 有的时候又是正常的#xff0c;但显示的也是系统壁纸#xff0c;并非是锁屏壁纸…
1 问题描述
为系统和锁屏分别设置两张不同的壁纸然后在锁屏界面长按Power调起google语音助手后有时候会出现壁纸不可见的情况如以下截图所示 有的时候又是正常的但显示的也是系统壁纸并非是锁屏壁纸。
后面我本地多次尝试发现了一些规律
1、同时设置系统和锁屏壁纸为壁纸A此时不会有问题。
2、在第1步的基础上单独将锁屏壁纸设置为壁纸B此时也不会有问题。
3、在第2步的基础上单独将系统壁纸设置为壁纸C出现问题。
添加了log后大概知道问题出现的原因了不过这里先分析一下WallpaperController中关于计算壁纸可见性的一些代码逻辑吧之前零零散散看过一点希望这次能够趁分析这个问题的机会做一些总结。
2 WallpaperController代码分析
2.1 WallpaperController.adjustWallpaperWindows
更新壁纸的起点在WallpaperController.adjustWallpaperWindows 1、WallpaperController.findWallpaperTarget用来寻找壁纸的目标窗口将寻找的结果放到成员变量WallpaperController.mFindResults中。
2、WallpaperController.updateWallpaperWindowsTarget根据成员变量WallpaperController.mFindResults更新成员变量WallpaperController.mWallpaperTarget。
3、最后如果WallpaperController.mWallpaperTarget不为空那么认为壁纸可见再调用WallpaperController.updateWallpaperTokens更新壁纸的可见性。
这里有两个成员变量mFindResults和mWallpaperTarget要先介绍一下。
首先是mWallpaperTarget定义为 白话点说就是壁纸的目标窗口比如一个Activity的窗口在显示的时候被设置为支持壁纸显示比如Launcher的窗口那么这个窗口就可以作为壁纸的目标窗口。如果我们遍历所有的窗口后找不到一个窗口可以作为壁纸的目标窗口那么就说明所有的窗口都不支持壁纸显示那壁纸也就会被设置为不可见。如果可以找到一个壁纸的目标窗口那么这个目标窗口就会被保存到成员变量WallpaperController.mWallpaperTarget中。
接着是成员变量mFindResults定义为 它是一个FindWallpaperTargetResult类型的成员变量这里则需要知道FindWallpaperTargetResult这个类的作用它定义在WallpaperController里 从类的注释上以及类名来看这个类是用来保存寻找壁纸目标窗口操作的结果。
再看它的成员变量首先是TopWallpaper类型的mTopWallpaper它又是定义在FindWallpaperTargetResult中的内部类只有两个成员变量mTopHideWhenLockedWallpaper和mTopShowWhenLockedWallpaper结合这里的注释以及我看了代码后的理解
mTopHideWhenLockedWallpaper和mTopShowWhenLockedWallpaper都可以设置为壁纸窗口即“Window{d837259 u0 com.android.systemui.wallpapers.ImageWallpaper}”并且同一时间它们两个中间只能有一个被设置
1、如果mTopHideWhenLockedWallpaper被设置即不为空说明此时壁纸只能在Home界面可见锁屏界面不可见。
2、如果mTopShowWhenLockedWallpaper被设置即不为空说明此时壁纸在Home界面以及锁屏界面均可见。
后面分析到相关代码的时候就能了解上面的意义了。
然后再解释几个后续会分析到的成员变量
1、mNeedsShowWhenLockedWallpaper如果在一个Activity界面可以在锁屏界面上显示比如通话界面如果这个Activity或者窗口不是全屏的那么就会把mNeedsShowWhenLockedWallpaper的值设置为true。在这种场景下即使我们没有为锁屏壁纸找到一个目标窗口那么我们可能也是需要将锁屏壁纸显示出来的。
2、useTopWallpaperAsTarget结合上面第一点来说如果后续经过我们遍历所有窗口后我们找不到任何一个窗口可以作为壁纸的目标窗口但是在一些特殊场景下我们又是需要将壁纸显示出来的那么我们就将这个值设置为true表示我们将TopWallpaper中的mTopHideWhenLockedWallpaper或者mTopShowWhenLockedWallpaper保存的壁纸WindowState本身作为壁纸的目标窗口至于从这两者中的哪个里面取则是看当前是否处于锁屏。
3、wallpaperTarget这个没什么好说的在寻找壁纸的目标窗口阶段我们将寻找的结果保存在FindWallpaperTargetResult.wallpaperTarget中后续在WallpaperController.updateWallpaperWindowsTarget中我们就从FindWallpaperTargetResult.wallpaperTarget中拿寻找的结果。
接下来看寻找壁纸的目标窗口的代码WallpaperController.findWallpaperTarget。
2.2 WallpaperController.findWallpaperTarget
这个方法用来寻找壁纸的目标窗口是我们本篇文章的分析重点。 2.2.1 FindWallpaperTargetResult.reset
调用FindWallpaperTargetResult.reset重置FindWallpaperTargetResult的保存的所有信息 2.2.2 Freeform的情况
如果当前有WINDOWING_MODE_FREEFORM类型的App显示 那么就设置FindWallpaperTargetResult.useTopWallpaperAsTarget为true 即在Freeform的场景下直接将壁纸进行显示不需要再额外找一个目标窗口了这算是一种对需要显示壁纸的特殊场景的处理。
2.2.3 WallpaperController.mFindWallpapers
接着对所有的窗口进行第一次遍历 如果这个窗口是壁纸类型的那么继续判断如果WallpaperWindowToken.canShowWhenLocked返回true说明此时壁纸是允许在锁屏界面显示的那么就将这个壁纸窗口保存在FindWallpaperTargetResult.mTopShowWhenLockedWallpaper中后续如果我们检测到FindWallpaperTargetResult.mTopShowWhenLockedWallpaper不为空那么就说明当前壁纸是允许在锁屏界面显示的。否则就将这个壁纸窗口保存在FindWallpaperTargetResult.mTopHideWhenLockedWallpaper中后续如果我们检测到FindWallpaperTargetResult.mTopHideWhenLockedWallpaper不为空那么就说明当前壁纸是只能在Home界面显示的。
另外根据我本地的测试当同时设置系统壁纸和锁屏壁纸时WallpaperWindowToken.setShowWhenLocked这个方法会被调用设置WallpaperWindowToken.mShowWhenLocked为true调用堆栈为 2.2.4 WallpaperController.mFindWallpaperTargetFunction
对所有的窗口进行第二次遍历 这里的逻辑也比较复杂省略不太重要的部分首先看一种特殊的情况
如果在锁屏状态并且此时正在遍历的这个窗口盖在锁屏界面那么继续判断
1、如果现在锁屏的状态为“occluded”。
或者
2、当前该在锁屏界面上的那个窗口处于Transition那一般就是open或者close。
该窗口或者该窗口对应的ActivityRecord是否是全屏的如果不是那么将mNeedsShowWhenLockedWallpaper设置为true。很好理解如果是一个非全屏的窗口盖在锁屏界面上如果不显示锁屏壁纸那么屏幕上没有被这个非全屏窗口覆盖的部分就会由于没有内容显示从而黑屏。
这里稍微提一下这个对窗口是否处于Transitiond的判断如果只是判断锁屏的状态为“occluded”那么可能会出现锁屏状态切换为“occluded”不够及时从而出现短暂黑屏的现象就比如我这里长按Power键唤起google语音助手的情况 所以我们需要加上对窗口是否处于Transition的判断确保壁纸在Transition早期阶段就显示。
判断过这种特殊场景后接着对这个正在遍历的窗口进行判断如果这个窗口在屏幕上并且已经绘制完成了那么调用WindowState.hasWallpaper方法去判断该窗口是否支持显示壁纸这里就不分析动画过程中显示壁纸的情况了 这是更一般的情况。
涉及LetterBox的情况比较少见最常见的还是通过检查窗口是否配置了FLAG_SHOW_WALLPAPER这个窗口标志位来判断这个窗口是否支持显示壁纸就比如Launcher。
2.2.5 FindWallpaperTargetResult.setUseTopWallpaperAsTarget
回到WallpaperController.findWallpaperTarget方法 这里承接第4步如果在第4步我们发现一个非全屏的窗口盖在了锁屏界面上那么就会将FindWallpaperTargetResult.mNeedsShowWhenLockedWallpaper设置为true。
接着在这里就调用FindWallpaperTargetResult.setUseTopWallpaperAsTarget设置FindWallpaperTargetResult.useTopWallpaperAsTarget为true 来保证后续壁纸可以显示这个场景和Freeform出现的场景处理方式一致即对需要显示壁纸的特殊场景的一种处理。
2.2.6 FindWallpaperTargetResult.setWallpaperTarget
来看最后的一点内容 如果FindWallpaperTargetResult.wallpaperTarget为空说明通过两次遍历我们没有找到壁纸的目标窗口但是FindWallpaperTargetResult.useTopWallpaperAsTarget为true又说明我们的确想显示壁纸那么就调用FindWallpaperTargetResult.getTopWallpaper看看能不能返回一个壁纸窗口如果可以那么就调用FindWallpaperTargetResult.setWallpaperTarget将壁纸的目标窗口设置为壁纸本身。但是也可能会返回null看下FindWallpaperTargetResult.getTopWallpaper的内容 前面说过了如果将壁纸窗口保存在mTopHideWhenLockedWallpaper中说明当前壁纸只能在Home界面显示不能在锁屏界面显示。如果将壁纸窗口保存在mTopShowWhenLockedWallpaper中说明当前壁纸可以在Home界面以及锁屏界面显示。这里的代码大概就是这种逻辑比较简单不再赘述。
需要注意的是这里的返回值可能为空我们分析的这个问题就是因为这里返回空所以出现了壁纸不可见的情况导致了黑屏我们后续分析问题产生的原因。
2.3 WallpaperController.updateWallpaperWindowsTarget WallpaperController.updateWallpaperWindowsTarget这个方法我看了下好像没有太多可以说的就是把上一步寻找壁纸的目标窗口的结果保存到WallpaperController的成员变量mWallpaperTarget中。
2.4 WallpaperController.updateWallpaperTokens
回到WallpaperController.adjustWallpaperWindows中如果WallpaperController.mWallpaperTarget不为空那么调用WallpaperController.updateWallpaperTokens设置壁纸的可见性 这里需要注意的一点就是及时这里的传参visibility是true后续可能也无法将壁纸变为可见因为这里还有额外的判断。
首先这里的成员变量mWallpaperTokens定义为 是一个WallpaperWindowToken的队列在WallpaperWindowToken创建的时候会把它加入到mWallpaperTokens中。
接着这里会调用FindWallpaperTargetResult.getTopWallpaper来获取当前的壁纸窗口只有这个壁纸窗口不为空并且在mWallpaperTokens中我们才能将壁纸的可见性设置为true。
后续的WallpaperWindowToken.updateWallpaperWindows就不分析了。
3 问题分析
代码分析完了现在分析问题。
根据我之前本地操作的结果
1、同时设置系统和锁屏壁纸为壁纸A此时不会有问题。
2、在第1步的基础上单独将锁屏壁纸设置为壁纸B此时也不会有问题。
3、在第2步的基础上单独将系统壁纸设置为壁纸C出现问题。
3.1 同时设置系统和锁屏壁纸为壁纸A
这个路径下会WallpaperWindowToken.setShowWhenLocked这个方法会被调用设置WallpaperWindowToken.mShowWhenLocked为true调用堆栈为 对所有窗口进行mFindWallpapers遍历时由于WallpaperWindowToken.mShowWhenLocked为true因此会设置mTopShowWhenLockedWallpaper为壁纸窗口。
对所有窗口进行mFindWallpaperTargetFunction遍历时由于google语音助手这个显示在锁屏界面之上的界面对应的ActivityRecord是非全屏的因此会设置FindWallpaperTargetResult.mNeedsShowWhenLockedWallpaper为true 并且由于所有窗口都不满足作为壁纸的目标窗口的条件因此这一步没有找到目标窗口。
后续再回到WallpaperController.findWallpaperTarget 现在FindWallpaperTargetResult.mNeedsShowWhenLockedWallpaper为true所以这里会调用FindWallpaperTargetResult.setUseTopWallpaperAsTarget来将FindWallpaperTargetResult.useTopWallpaperAsTarget设置为true那么接着就会调用FindWallpaperTargetResult.getTopWallpaper尝试获取壁纸窗口并且将返回的结果作为目标窗口 这里由于我们处于锁屏因此会返回TopWallpaper.mTopShowWhenLockedWallpaper并且根据我们的分析因为WallpaperWindowToken.mShowWhenLocked为true因此之前我们的确是将壁纸窗口保存在了TopWallpaper.mTopShowWhenLockedWallpaper中的因此这里就可以返回壁纸窗口并且将其设置为壁纸的目标窗口。
这种情况最终是会找到一个壁纸的目标窗口的因此壁纸是可见的。
3.2 在第1步的基础上单独将锁屏壁纸设置为壁纸B
这种情况下和3.1节的分析内容不会有区别所以也不会有什么问题。
唯一有点奇怪的是此时显示的是系统壁纸而非锁屏壁纸。也不能说奇怪毕竟系统壁纸才是真正的壁纸是有一个专门的壁纸窗口对应而锁屏壁纸应该只是锁屏界面为自己设置的一张背景图。
3.3 在第2步的基础上单独将系统壁纸设置为壁纸C
这种情况下就会出现问题原因出在哪儿呢
看了下log发现此时WallpaperWindowToken.mShowWhenLocked变成了false那么对所有窗口进行mFindWallpapers遍历时由于WallpaperWindowToken.mShowWhenLocked为false因此会设置TopWallpaper.mTopHideWhenLockedWallpaper为壁纸窗口。
而在后续调用FindWallpaperTargetResult.getTopWallpaper尝试获取壁纸窗口时由于此时处于锁屏因此返回的仍然是TopWallpaper.mTopShowWhenLockedWallpaper。这就是问题的原因所在了我们将壁纸窗口保存在了TopWallpaper.mTopHideWhenLockedWallpaper中那么TopWallpaper.mTopShowWhenLockedWallpaper就是空的因此调用FindWallpaperTargetResult.getTopWallpaper返回的就是空的最终结果就是没有为壁纸找到一个目标窗口壁纸在锁屏状态下变为不可见。
那么为什么WallpaperWindowToken.mShowWhenLocked变成了false呢我也没看到WallpaperWindowToken.setShowWhenLocked方法有调用将WallpaperWindowToken.mShowWhenLocked设置为false啊原来是设置系统壁纸的时候直接重新创建了一个新的WallpaperWindowToken对象 WallpaperWindowToken.mShowWhenLocked默认是false并且后续没有再调用WallpaperWindowToken.setShowWhenLocked将WallpaperWindowToken.mShowWhenLocked设置为true所以就出现了问题。
最后再看下同时设置系统壁纸和锁屏壁纸的情况吧同时设置了壁纸后会重新创建一个WallpaperWindowToken对象接着就是调用WallpaperWindowToken.setShowWhenLocked设置WallpaperWindowToken.mShowWhenLocked 看调用堆栈都在WallpaperManagerService$DisplayConnector.connectLocked方法中 看到这里需要更正上面的一个说法就是单独设置了系统壁纸的时候其实也是调用了WallpaperWindowToken.setShowWhenLocked了但是设置的是false因为只是针对系统壁纸生效而本来WallpaperWindowToken.mShowWhenLocked默认的就是false所以我之前添加的log没有打印…
用白话总结一下这个问题给我个人的感觉就是
1、setShowWhenLocked这个属性表示的壁纸自己支持不支持在锁屏界面显示是壁纸自己决定的或者说是Launcher在设置壁纸的时候决定的。
2、WallpaperController用来决策壁纸是否需要在锁屏界面上显示。
这个问题很明显就是这两者冲突了WallpaperController的逻辑觉得这个时候壁纸应该在锁屏界面显示但是还是需要看看在Launcher设置壁纸的时候设定壁纸是否可以在锁屏界面显示。如果壁纸不支持在锁屏界面显示那么WallpaperController也不能强行让壁纸在锁屏界面上显示。
4 解决方案
分析到这里感觉这应该是google的原生问题但是pixel却没有问题不过发现了一个区别就是将系统壁纸和锁屏壁纸分别设置为不同的壁纸图片后在锁屏界面长按Power唤起语音助手时发现pixel显示的是壁纸是锁屏壁纸而我们的机器要么显示的是系统壁纸要么就不显示。
dump信息看了下原来pixel的机器有两个WallpaperWindowToken分别管理系统壁纸和锁屏壁纸 而我们的机器只有一个是系统壁纸 锁屏壁纸只是NotificationShade为自己设置的一张背景。
跟SystemUI的同事沟通了一下得知pixel用的似乎不是aosp里的SystemUI而是自己另外一套的SystemUI并且将aosp的SystemUI推到手机里也有问题那这个问题无法参考pixel进行修改了。
回顾一下问题发生的原因即WallpaperController判断出锁屏界面需要显示壁纸壁纸却又说我的出厂设定就是只能在Home界面显示我就不显示WallpaperController拗不过壁纸所以出现了黑屏。
如果要解决这个黑屏问题我目前能想到的就是围绕FindWallpaperTargetResult.mNeedsShowWhenLockedWallpaper这个变量做文章 既然这个变量被设置为true了就说明当下的确需要壁纸去显示了不管壁纸它支持不支持在锁屏界面上显示它都得直楞起来先给我显示了再说。