弹窗的分为系统级别的弹窗和应用内的。
系统弹窗 一般有几种,ANR 的弹窗,APP crash 的弹窗,各种权限弹窗。用户弹窗比较多的是 dialog,或者是 fragment dialog,最麻烦的就是各种新手引导,新手引导一般是半透明的,图像匹配和处理都有点麻烦。
adb shell 检测弹窗或者蒙层,考虑这个问题有两种思路
考虑从 View Hierarchy,需要分析
dumpsys activity top
View Hierarchy 的日志中,越往下,就是越顶层。
i,蒙层的显示与关闭就一个一个 visibile 和 gone 的区别 :(
举个例子
org.yeshen.test.RelativeLayout{330c237 V.E...C. ... 0,0-1080,1776 #240f05a4 org.yeshen.test.main:id/tip_container}
org.yeshen.test.View{479a3a4 V.ED.... ... 540,888-540,888 #240f05a5 org.yeshen.test.main:id/text}
org.yeshen.test.ImageView{c7ed50d V.ED.... ... 216,512-864,1007 #240f05a6 org.yeshen.test.main:id/image1}
org.yeshen.test.ImageView{bf8dcc2 V.ED..C. ... 240,1104-840,1236 #240f05a7 org.yeshen.test.main:id/image2}
org.yeshen.test.RelativeLayout{330c237 G.E...C. ... 0,0-1080,1776 #240f05a4 org.yeshen.test.main:id/tip_container}
org.yeshen.test.View{479a3a4 V.ED.... ... 540,888-540,888 #240f05a5 org.yeshen.test.main:id/text}
org.yeshen.test.ImageView{c7ed50d V.ED.... ... 216,512-864,1007 #240f05a6 org.yeshen.test.main:id/image1}
org.yeshen.test.ImageView{bf8dcc2 V.ED..C. ... 240,1104-840,1236 #240f05a7 org.yeshen.test.main:id/image2}
ii,通过检测有没有对应的控件挂载,这个通过 diff 就可以看得出来
dumpsys window $(activityName)
dumpsys SurfaceFlinger
i,有弹窗的话,必然会增加 window,在 WindowManagerService 中必然有备案
dumpsys window
在我的测试中是最有效的,一般一个 activity 中有子弹窗
如果是权限弹窗(permission)的话,需要检测 permission 的的 packageIntall 的 window。当然各种系统的弹窗都可以抓。
ii,surfaceflinger 渲染过程中必然会增加一层或多层 layer
dumpsys SurfaceFlinger
可以抓叠加层,如果是系统级别的 crash 的话,那么 layer 上可以看出变化
这个是蛮蛋疼的一种实现,目的应该是为了随时随地弹窗。想到两种解决办法:
i,检查下最顶层的 activity 是什么,在脚本中做处理。
adb shell dumpsys activity top | grep pid
ii,查看 window 中的大小,没有铺满全屏的话,就是 Activity 当 Dialog 使用了
i,每个 tap,click 执行前都注入一下检查,提供 callback
ii,提供关注关键词的方法,让开发者在某个页面关注某个 view,或者某个 window 的叠加
iii,在 gui 部分提供更便利的检测方法,分析这些dumpsys
出来的东西有点工作量的
# count is 0 : May be covered by a system error pop-up
# count is 1 : normal
# count is 2 : if package is None or this a
def top_window_count(self, filters=None):
filters = filters if filters is not None else self.package
windows_str, summary, = adb.dumpsys_window(serial=self.serial,
port=self.port,
filters=filters).split("\n\n")
windows = windows_str.split(" Window #")
found = re.compile(r'mFocusedApp=AppWindowToken{(?P<token>[^/\s]+)').search(summary)
count = 0
if found:
token = "mRootToken=AppWindowToken{%s" % found.group('token')
for w in windows:
if token in w:
count += 1
return count
def __check_id_exists(self, identity=None):
# org.yeshen.CustomView{a434138 V.E...C. ... 0,0-1080,1776 #240f05a4 org.yeshen:id/tip_container}
top = adb.dumpsys_top_activity(serial=self.serial, port=self.port).split("\n")
match = ":id/%s" % identity
for s in top:
if match in s:
return " V." in s
return False
完整代码放在