背景

全民短视频,mcn机构的遍地开花,mcn的的生存、发展就要想各种各样的办法。在一个数据导向的社会,大家都希望通过数据获取更多、更准确的数据,于是有朋友希望统计下进入抖音直播间的人数和人物的基础信息。 想到之前写的一个自动翻页的小工具,想来这个事情还不是手拿把掐的小事嘛?于是就一口答应了。 于是就遇到了一系列的问题。

正常的流程

1、通过工具定位具体的view

2、根据业务,对view进行操作

遇到的问题

怎么查找希望的view? 赶时间的同学可以直接看最后的捷径整理

常用工具

2.1.1、ddms中的 dump view hierarchy for ui automator 新版本Android studio中已经不提供 可以尝试在Android sdk路径下,查看tools/monitor 是否存在,存在即还有可能能用。

2.1.2、上面提供的工具如果可用,还有一个简化版本的,可以直接在tools/bin下查找 uiautomatorviewer 功能和上面的一样

正常是这样的效果

java 统计直播成交量 统计直播数据_java 统计直播成交量

还有就是利用adb命令,adb命令的方法看起来比较头疼,就不举例了,具体的可以自己搜索下。

出师不利

上面的工具已经用的比较多了,就以为解决这个问题就是分分钟的事情。还是自己年轻了。 尝试了2.1.1方法,然后一直失败

java 统计直播成交量 统计直播数据_自动化_02

最开始以为是工具不对,内存导致的 就换了2.1.2的工具。还是不行。但是发现了一个重要的现象 如果页面上面的变化内容比较少的话,就能抓取到,多了就不行了 ,结合保存怀疑是因为在生成view hierarchy的时候,直播页面已经发生了变化,导致生成失败。

要不尝试反编译下apk?

说干就干,使用jadx对apk进行反编译,8g内存直接被爆了,重新配置为16g,正常跑起来了。 看了下代码,觉得头疼,要不还是想下其他方案吧。不得不说,直播页真的是有点头疼 1、activity竟然还要使用代理来处理 2、使用了万恶的event bus 3、对资源进行了混淆和代理 整体来说,如果去看这个代码的话,就比较头疼,要花比较多的精力。

要不先xp hook下?

这个好像成本也比较大。作为兜底方案。

柳暗花明又一村

本来以为没有其他的更好方案了,后来一想,不对,我们既然已经使用了辅助功能,为啥不考虑用api来做呢? 说干就干。 1、我们的service直接继承自 AccessibilityService 我们之前的业务也都在这个里面处理。

public abstract void onAccessibilityEvent(AccessibilityEvent event);

关注到 AccessibilityEvent 下有

public @EventType int getEventType() {
        return mEventType;
    }

查看EventType:

@IntDef(flag = true, prefix = { "TYPE_" }, value = {
            TYPE_VIEW_CLICKED,
            TYPE_VIEW_LONG_CLICKED,
            TYPE_VIEW_SELECTED,
            TYPE_VIEW_FOCUSED,
            TYPE_VIEW_TEXT_CHANGED,
            TYPE_WINDOW_STATE_CHANGED,
            TYPE_NOTIFICATION_STATE_CHANGED,
            TYPE_VIEW_HOVER_ENTER,
            TYPE_VIEW_HOVER_EXIT,
            TYPE_TOUCH_EXPLORATION_GESTURE_START,
            TYPE_TOUCH_EXPLORATION_GESTURE_END,
            TYPE_WINDOW_CONTENT_CHANGED,
            TYPE_VIEW_SCROLLED,
            TYPE_VIEW_TEXT_SELECTION_CHANGED,
            TYPE_ANNOUNCEMENT,
            TYPE_VIEW_ACCESSIBILITY_FOCUSED,
            TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
            TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
            TYPE_GESTURE_DETECTION_START,
            TYPE_GESTURE_DETECTION_END,
            TYPE_TOUCH_INTERACTION_START,
            TYPE_TOUCH_INTERACTION_END,
            TYPE_WINDOWS_CHANGED,
            TYPE_VIEW_CONTEXT_CLICKED,
            TYPE_ASSIST_READING_CONTEXT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface EventType {}

看到了心心念念的 TYPE_VIEW_CLICKED 我觉得看到了光明。

2、就干,代码安排上。 开启辅助,触发对应view的点击事件。激动人心的事情发生了

真的输出了对应的view id和text!!!

3、使用

findAccessibilityNodeInfosByViewId(String viewId)

获取对应的nodeInfo,然后对其进行点击操作。

原本平淡无奇的操作,竟然再起波澜。

波澜再起

发现对前面找到的view进行点击操作的时候,无法触发效果。 但是我刚才点击的时候抓到的明明是这个view啊,text内容也是对的,怎么回事呢? 先看下这个view是不是clickable吧,万一没有这个功能呢?

List<AccessibilityAction> getActionList()

nodeInfo提供了查看当前节点所有的action的列表,也有对应的含义。其中我们比较关注的是这个

public static final int ACTION_CLICK = 0x00000010;

遗憾的是前面找到的view并没有这个action。

完美结局

回想Android的事件传递机制,如果一个view没有拦截点击事件,但是点击事件还被响应了,那就只有一个情况--当前的view的父布局或者子布局把事件给处理了。 兵分两路 通过:

getParent()
getChild(i)

去查看对应的父view和子view,并检查他们对应的action是否有click。 功夫不负有心人。最终确认了,view的父布局中有click的action。对父view进行操作,也获得了期望的效果。至此这个问题被解决。

捷径整理

1、利用AccessibilityService中onAccessibilityEvent的event 的clicktype,缩小view的查找范围。

2、校验view是否是想要确认的view

3、如果是需要view,但是无法响应事件,可以查找他的父布局或者子布局上能够响应click的view

4、确定最终要确定的view

5、不要被之前的经验所束缚,要多使用系统给的api,要多思考系统原理。