测试基础 检测弹窗与蒙层的方法 (Android )

yeshen · 2017年12月08日 · 最后由 sunny0330 回复于 2019年12月18日 · 7099 次阅读
本帖已被设为精华帖!

背景

弹窗的分为系统级别的弹窗和应用内的。
系统弹窗 一般有几种,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 上可以看出变化

Activity 当作弹窗

这个是蛮蛋疼的一种实现,目的应该是为了随时随地弹窗。想到两种解决办法:

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

完整代码放在

https://github.com/wuyisheng/ATX

共收到 7 条回复 时间 点赞
思寒_seveniruby 将本帖设为了精华贴 12月08日 15:00

这是与官方库的比对 https://github.com/NetEaseGame/ATX/compare/master...wuyisheng:master
除了 dumpsys,一般的自动化工具也是可以识别出弹框的。pageSource 里面的数据跟 dumpsys window 类似吧。appium 里面加个算法就可以识别了吧。

嗯,dumpsys window windows 里面的信息可以很好的处理弹窗。
可惜蒙层的分析不能很通用。在考虑过度绘制方法中找一点突破口。

除了 ATX 之外,别的测试框架我不是很熟悉。可以指点一下,appium 或者其他测试框架是怎么处理这个问题的吗?

用 UI 2.0 可以直接注册 UIWatcher 解决这个问题。

依兰听风 回复

看到了,你说的是这个吧?, 谢谢。

我研究下

为什么你的源码里面根本没找到你上面的两个方法

cheng 回复

写了之后只是提交到公司内部的 git。回头我提交到 github 吧~
但是两个方法的代码都在文章中直接贴了出来的哦~

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 14:44
simple [精彩盘点] TesterHome 社区 2018 年 度精华帖 中提及了此贴 01月07日 12:08
sunny0330 如何检测弹窗、并关闭相应的安卓弹窗 中提及了此贴 12月18日 20:51

dumpsys window 这个返回的 window 有 10 几个,

 Window #0 Window{5ed78e1 u0 PointerLocation - display 0}:
  Window #1 Window{5f94251 u0 Framework_SearchPanel}:
  Window #2 Window{5f5a609 u0 NavigationBar0}:
  Window #3 Window{5f89729 u0 android}:
  Window #4 Window{5f5a651 u0 StatusBar}:
  Window #5 Window{5f89609 u0 AssistPreviewPanel}:
  Window #6 Window{5ece209 u0 DockedStackDivider}:
  Window #7 Window{6052371 u0 com.sankuai.meituan/com.sankuai.meituan.activity.P
reloadedWelcome}:
  Window #8 Window{6046f29 u0 com.sankuai.meituan/com.sankuai.meituan.activity.P
reloadedWelcome}:
  Window #9 Window{600bd29 u0 com.huawei.intelligent.Workspace}:
  Window #10 Window{5f896e1 u0 com.huawei.android.launcher/com.huawei.android.la
uncher.unihome.UniHomeLauncher}:
  Window #11 Window{5f94209 u0 com.sina.weibo/com.sina.weibo.SplashActivity}:
  Window #12 Window{5ed79b9 u0 com.baidu.searchbox/com.baidu.searchbox.MainActiv
ity}:
  Window #13 Window{600bd71 u0 com.huawei.camera/com.huawei.camera}:
  Window #14 Window{5fbf8e1 u0 com.huawei.wallet/com.huawei.wallet.ui.setting.In
troductionActivity}:
  Window #15 Window{6054bb9 u0 com.android.mms/com.android.mms.ui.ConversationLi
st}:
  Window #16 Window{600bc09 u0 com.android.gallery3d/com.huawei.gallery.app.Gall
eryMain}:
  Window #17 Window{5f4efb9 u0 com.android.settings/com.android.settings.HWSetti
ngs}:
  Window #18 Window{5e6d4e1 u0 com.android.systemui.HwImageWallpaper}:

哪里表明当前界面有弹窗尼?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册