You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android现代系统中无障碍服务读取用户输入文本失败的技术求助

解决Android无障碍服务读取输入文本的问题

一、检查无障碍服务配置文件

首先确保res/xml/accessibility_service_config.xml配置正确,核心是开启获取窗口内容的权限:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeViewTextChanged|typeViewFocused"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    android:notificationTimeout="100" />
  • android:canRetrieveWindowContent="true":必须设置,否则无法获取任何控件的文本内容
  • accessibilityEventTypes需包含typeViewTextChanged,才能监听文本输入事件
  • description要在strings.xml中定义,用户在系统设置里能看到服务说明

二、修正代码获取完整输入文本

你的代码依赖event.getText()是错误的——这个方法返回的只是文本变化的片段,不是输入框的全部内容。正确做法是通过AccessibilityNodeInfo获取完整文本:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) {
        AccessibilityNodeInfo source = event.getSource();
        if (source == null) {
            // 当source为空时,遍历根节点查找当前聚焦的可编辑控件
            AccessibilityNodeInfo root = getRootInActiveWindow();
            if (root != null) {
                source = findFocusedEditableNode(root);
                root.recycle();
            }
            if (source == null) return;
        }

        // 获取输入框完整文本
        CharSequence fullText = source.getText();
        if (fullText == null || fullText.length() == 0) {
            source.recycle();
            return;
        }

        // 获取光标位置,确保取到的是光标前的最后一个单词
        int cursorPosition = source.getSelectionStart();
        if (cursorPosition == -1) cursorPosition = fullText.length();
        String textBeforeCursor = fullText.subSequence(0, cursorPosition).toString().trim();

        // 提取最后一个单词
        if (!textBeforeCursor.isEmpty()) {
            String[] words = textBeforeCursor.split("\\s+");
            String lastWord = words[words.length - 1];
            Log.d("AccessibilityService", "最后一个单词: " + lastWord);
        }

        source.recycle(); // 必须回收节点,避免内存泄漏
    }
}

// 递归查找当前聚焦的可编辑控件
private AccessibilityNodeInfo findFocusedEditableNode(AccessibilityNodeInfo root) {
    if (root == null) return null;
    if (root.isFocused() && root.isEditable()) {
        return root;
    }
    for (int i = 0; i < root.getChildCount(); i++) {
        AccessibilityNodeInfo child = root.getChild(i);
        AccessibilityNodeInfo result = findFocusedEditableNode(child);
        if (result != null) {
            return result;
        }
        child.recycle();
    }
    return null;
}
  • 必须调用source.recycle():AccessibilityNodeInfo是系统资源,使用后必须回收,否则会引发内存泄漏
  • 处理光标位置:通过getSelectionStart()获取光标位置,确保提取的是用户输入到光标处的最后一个单词,而非整个文本的最后一个单词
  • 兼容source为空的场景:部分情况下event.getSource()会返回null,需要遍历根节点查找目标控件

三、优化服务初始化代码

建议优先通过xml配置服务信息,若需动态调整,可在onServiceConnected()中补充:

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    AccessibilityServiceInfo info = getServiceInfo();
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED | AccessibilityEvent.TYPE_VIEW_FOCUSED;
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
    info.notificationTimeout = 100;
    info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
    setServiceInfo(info);
}

四、测试注意事项

  • 确保在系统设置中完全启用无障碍服务,部分设备需要二次确认权限
  • 优先测试系统原生应用(如短信、记事本),避免测试有隐私保护机制的应用(如微信、支付宝),这类应用可能限制无障碍服务访问
  • Android 12+需确保应用目标SDK版本适配,避免权限被系统拒绝

内容的提问来源于stack exchange,提问作者Sandah Aung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:29:59