又是一年618,电商平台又是常规的 价格歧视 策略:蹲点抢券、各种凑单、签到、关注店铺、加购商品、看直播等。满打满算,可能最终付款的价格比618前还贵,2333,毕竟都是 活动前先涨价。
比如,前些天杰哥想给家里物色个小冰箱,标的1799,领了各种券,就差个plus的1500-50,价格可以做到1550左右,想着能省一点是一点,先加购物车,明早定个9:59的闹钟抢下券,抢到直接下单一波带走。然后我发现我想多了,当我抢到券,再打开商品页,我整个人傻了:
6啊,直接涨价到1899,而且还要预约,得过两天晚上8点才能抢,你猜我最后多少钱下单的?1650,所以整这些有的没的,最后我还多花了100块钱…
不过说回来,阿狗的活动奖励,比阿猫良心多了,属于多劳多得类型,当年阿猫搞组队PK互抢红包的恶心玩法还历历在目。关于阿狗活动的玩法,其实都是一样的,就是换个主题,活动任务的自动化方案,之前就写过了几篇文章了:
- 《节约”阳寿”——某电商春节炸年兽自动化》 → Airtest自动化测试工具 + 第三方OCR
- 《节约”阳寿”——某电商618活动自动化》 → 图片处理 + 相似度匹配 + 第三方OCR + Chrome Inspect
- 《破大防!这个开源库,竟能让APP日常任务自动化变得如此简单》 → 中文OCR库chineseocr_lite
上述方案,都需要手机插着电脑跑脚本,上上周不是整活封装了一个AccessibilityService库么 → 《简单封装AccessibilityService写个库,助力Android自动化》,本节就用这个库来轻松实现自动化。
0x1、打开APP进入活动页
这一步的常规实现思路:先获得 目标APP的包名 和 入口Activity,然后调用 startActivity() 实现跳转。
① 获得 APP的包名和入口Activity 的3种技巧
- 从APK文件下手 → 包名和入口Activity都会在 AndroidManifest.xml 配置文件中声明,直接找到这个文件就好~
直接解压apk文件 是 不行的哈!!!你打开只会看到这个:
最简单快捷的方法:把APK拖到Android Studio里双击打开,搜 package=
就可以找到包名了:
接着搜 android.intent.category.LAUNCHER
就可以找到入口Activity啦:
没有安装Android Studio的话,也可以用其它反编译工具获取此文件(如:apktool)
- 使用adb命令获取:
手机接电脑,打开APP的瞬间,执行下述命令:
adb shell dumpsys activity top | grep ACTIVITY
运行结果如下:
圈着的部分:应用包名 / 应用入口Activity,而全限定类命(完整的Activity)就是前后两部分拼接而已~
- 使用CpFastAccessibility获得
自定义无障碍服务类时 (需继承FastAccessibilityService),重写 noAnalyzeCallBack()
直接打印 EventWrapper
参数:
运行授予无障碍后,打开电商APP:
不难看出这个 MainActivity
就是APP的入口Activity啦。是不是超简单?那这里是怎么实现的呢?简述下原理:
- 1、无障碍配置文件设置 监听所有应用 → android:packageNames=”@null”
- 2、重写
onAccessibilityEvent()
,取出event中的关键信息丢EventWrapper实例,然后通过回调往外丢:
上面重写了 noAnalyzeCallBack()
,自然能收到~
② 打开目标APP
通过上面获得的包名和入口Activity就可以精确的打开目标App了,但我想告诉你,其实 只需要一个包名,Android为我们提供了一个API:PackageManager.getLaunchIntentForPackage(packageName),语法就不展开讲了,感兴趣的自己搜关键字,直接给出封装的调用代码,除了常规判空,还加入了异常兜底:
调用方式很简单,而且会返回一个跳转成功与否的值~
Tips:因为 Android 11 的软件包可见性影响,有安装APP,但是调 getLaunchIntentForPackage()还是会返回null,需要修改下 AndroidManifest.xml,下述方法2选1:
<!-- 方法1:添加查询所有的应用的权限 -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<!-- 方法2:添加queries标签,声明与哪些包名的应用交互 -->
<queries>
<package android:name="com.facebook.katana" />
<package android:name="com.tencent.mm" />
</queries>
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
③ 隐式启动Activity
在启动APP后,如何进入活动页?常规思路:找到入口节点 → 触发点击,但在这个电商APP里完全不需要这样做,可以通过 URL Scheme(页面内跳转协议) 来隐式启动活动页,封装下工具方法:
然后如何获得这个url呢?
- ① 搜索获取:有些人会分享出来,可以参考 《URL Scheme 查询指南》 提到的几个站点;
- ② 应用分享URL,电脑浏览器打开,F12抓包:不过现在很多电商都不支持分享链接了,只有分享口令;
- ③ 写Xposed插件拦截:
Activity.startActivity()
→ 本质上还是调用Activity.startActivityForResult()
→ 最终调用Instrumentation.execStartActivity()
:
所以Hook下这个方法,然后把Intent的值打印出来就可以啦,限于篇幅就不展开讲啦。写个简单Hook示例:
感兴趣的可以自己试试,接着写代码调用下跳转App和页面的方法:
运行后看看效果:
好的,正常进入活动页面~
0x2、定位结点
接着就到定位结点,触发交互了,因为我们在无障碍服务配置文件里声明的 android:accessibilityFlags 包含了 flagRequestEnhancedWebAccessibility,理论上也是能拿到网页节点的。库里封装了一个打印所有页面节点的方法:
在 noAnalyzeCallBack()
里调用下:
然后可以看到控制台输出的节点树信息:
如果WebView里的节点没显示或者显示不全,可以尝试切换到 tbs内核 运行:
- 给客服发送 debugtbs.qq.com,点击打开链接;
- 选择 安装线上内核,安装完后会自动重启;
- 重启后 选择DebugX5,不显示 请先安装内核 说明切换成功;
- 如果不想,把APP杀掉,重新启动,重复上述操作;
简单写下做任务的样例代码:
然后是任务判定,两个思路:
- 任务执行前,获取”去完成”节点列表,然后获取任务描述的节点列表,遍历比较y轴相差像素点少于50,建立关联关系;
- 任务执行后,对页面中任务相关的节点进行判断,比如:滑动浏览xx可得、立即入会,立即开卡、喜欢等等。
0x3、小结
今早兴高采烈来到工位,打开电脑准备完善下脚本,才发现活动TM已经结束了,所以本节只讲解了思路,没提供完整可用的代码,甚至连运行的效果图都整不出来,尴尬…
不过,在实践开发中,发现自己写的库有很多不足的地方,比如:
- 主动休眠1000ms有点呆,不支持轮询查找等待控件加载,就是那种waitFor();
- 打印所有页面节点,可以整一个悬浮框来调用;
- 多次查找结点,回调嵌套,不够优雅;
- 等等…
当然,后续肯定是要慢慢完善,有空就会写点,有问题的小伙伴欢迎评论区留言,也可以在仓库提issues~