预计阅读时间5分钟
MIUI14更新后,李跳跳等工具便出现问题,为了满足使用的需求,便开始研究实现过程,下面是学习记录,解决方案请直接跳至第6点。
1.AccessibilityService无障碍服务
无障碍服务主要是用来协助残障用户更好的使用手机,但是各位大佬基于无障碍服务开发了很多玩法,例如抢红包、跳广告等,接下来我们新建个项目来实现跳广告功能。
1.1 创建服务
class MyAccessibilityService : AccessibilityService() {
private val TAG = javaClass.simpleName
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
Log.e(TAG, "onAccessibilityEvent$event")
if (event?.eventType == TYPE_WINDOW_CONTENT_CHANGED) {
if (event.className == "android.widget.TextView") {
//获取跳过按钮并点击
try {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
skip(nodeInfo, "跳过")
}
} catch (e: Error) {
Log.e(TAG, "$e")
}
}
}
}
/**
* 匹配跳过规则
*/
private fun skip(nodeInfo: AccessibilityNodeInfo?, str: String?) {
val nodeInfoList = nodeInfo?.findAccessibilityNodeInfosByText(str)
if (nodeInfoList == null || nodeInfoList.isEmpty()) {
//页面跳转过程中,有可能获取不到
return
}
for (info in nodeInfoList) {
val charSequence = info.text
if (charSequence != null) {
val msg = charSequence.toString()
if (msg.contains("跳过")) {
info.performAction(ACTION_CLICK)
Toast.makeText(this, "跳过广告", Toast.LENGTH_SHORT).show()
}
}
}
}
override fun onInterrupt() {
}
}
1.2配置服务
res目录下新建service_config.xml文件,名称可以自定义下一步会用到
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:accessibilityFlags="flagDefault"
android:canRequestTouchExplorationMode="true"
android:description="@string/app_name"
android:notificationTimeout="100"/>
1.3 添加清单文件
<service android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/service_config" />
</service>
1.4 启动服务
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//跳转无障碍模式设置界面
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}
2.实现无障碍功能
gif动画
3.获取UI布局
3.1 findAccessibilityNodeInfosByText
在1.1中使用findAccessibilityNodeInfosByText来进行模糊匹配,为了满足精准匹配的需求,我们也可以通过获取id来实现(findAccessibilityNodeInfosByViewId)
3.2 findAccessibilityNodeInfosByViewId
使用uiautomatorviewer工具分析app界面
注:工具路径 在安卓sdk中 C:\Users\XX\AppData\Local\Android\Sdk\tools\bin\uiautomatorviewer.bat
运行的命令框请勿关闭
详情
private fun skip(nodeInfo: AccessibilityNodeInfo?) {
val nodeInfoList = nodeInfo?.findAccessibilityNodeInfosByViewId("com.moutai.mall:id/tvSkip")
if (nodeInfoList == null || nodeInfoList.isEmpty()) {
//页面跳转过程中,有可能获取不到
return
}
for (info in nodeInfoList) {
info.performAction(ACTION_CLICK)
Toast.makeText(this, "跳过广告", Toast.LENGTH_SHORT).show()
}
}
3.3 遍历父节点
通过获取父节点的AccessibilityWodeInfo,再通过该对象的getChild 方法来查询目标节点
private fun findInfo(
info: AccessibilityNodeInfo?,
str: String?,
): AccessibilityNodeInfo? {
if (info == null) return null
if (info.childCount == 0) {
if (info.text!=null){
if (info.text.toString().contains(str!!)) {
info.performAction(ACTION_CLICK)
Log.e("findInfo","$info")
}
return info
}
} else {
for (i in 0 until info.childCount) {
findInfo(info.getChild(i), str)
}
}
return null
}
4.遇到的问题
4.1 android findAccessibilityNodeInfosByViewId return null although the node id exists
遇到这个问题时排查下获取nodeInfo是不是根活动视图,如果没有解决,再看下配置文件accessibilityEventTypes中的设置
最开始出现是使用event!!.source来获取,该方法是获取当前界面的可访问节点信息,但在跳转过程中有可能获取不到。
5.防范方式
1. 重写TextView的findViewsWithText方法
2. Event干扰
3. 使用onTouch代替onClick
6.MIUI14使用跳过功能
进入开发者选项--》启用MIUI功能--》关闭--》开启无障碍模式--》开启跳过广告的app--》重新启用MIUI功能
亲测使用正常!
以上就是无障碍服务的学习过程,如果有好的建议和想法可以留言或者私信,如果想深入了解可以访问官方文档,方便的话给个赞或者评分吧。