Getting Lots of ANR due to AccessibilityNodeInfo getChild

本秂侑毒 提交于 2019-12-01 22:48:23
  1. The first important thing is to recycle all the nodes allocated by getChild, findAccessibilityNodeInfoByAccessibilityId or any other function. So you should have something like this:
AccessibilityNodeInfo findFirstViewByText(AccessibilityNodeInfo rootNode, String text) {
        if (rootNode == null) return null;
        if (rootNode.getText() != null && rootNode.getText().toString().equals(text))
            return rootNode;
        int childCount = rootNode.getChildCount();
        for (int i = 0; i < childCount; i++) {
            AccessibilityNodeInfo tmpNode = rootNode.getChild(i);
            if (tmpNode != null) {
                AccessibilityNodeInfo res = findFirstViewByText(tmpNode, text);
                if (res != null) {
                    return res;
                } else {
                    tmpNode.recycle();
                }
            }
        }
        return null;
    }
}

node = findFirstViewByText(rootNode, "something");
if(node != null) {
  node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
  node.recycle();
}
  1. Keep in mind that onAccessibilityEvent may be called too often while you interact with the target application and also your processing in this function is blocking UI Thread on the target application. You can overcome this by:

    • Throttling the calls to onAccessibilityEvent i.e. don't execute your code if the last execution happened less than x milliseconds ago, but make sure that last call will trigger execution of your code.
    • Improve the performance of your code by avoiding unnecessary traversing. Define states so the app will look only for the nodes relevant for the current state.
  2. Feel free to share more of your code so i/we can understand what exactly is your goal, that way we can be more helpful. As i can see from your log, there are several threads traversing the virtual dom of accessibility service and they are causing deadlock.

I'm highly suspicious of your collect nodes logic. It's very convolluted. It actually took me a good while to confirm that it was doing what I thought it was doing, and I'm still not convinced, though mostly because there are a few class/global variables that you have not shared, keep ing me from being 100% sure of anything. Allow me to describe what I think it's doing:

CollectNodes: Collects all descendants of the given node, and returns them in a list.

So, given that description, let me show you a simpler implementation.

public ArrayList<AccessibilityNodeInfo> collectNodes() {
    ArrayList<AccessibilityNodeInfo> results = new ArrayList<>();

    try {
        collectNodes(getRootInActiveWindow(), results);    
    } catch (Exception e) {
        //Handle exceptions, though, if you don't do stupid things exceptions shouldn't happen
    }

    return results;
}

private void collectNodes(AccessibilityNodeInfo nodeInfo, ArrayList<AccessibilityNodeInfo> nodes) {

    if (nodeInfo == null) return;

    if (someFilteringFunctionYouWantToApplyToLimitNodes(nodeInfo)) {
        nodes.add(nodeInfo);
    }

    for (int i = 0; i < nodeInfo.getChildCount(); i++) {
        collectNodes(nodeInfo.getChildAt(i), nodes);
    }
}

NOW, let's talk about some of the things, actually really only one specific thing that I removed from your implementation, and that is this line:

node.recycle();

I believe this is the actual line that is causing you problems. At no point in your code have you created any of your own AccessibilityNodeInfo objects. All of the objects that you're using belong to the the hierarchy of nodes that has been created for you by the Operating system. These nodes should be viewed as a READ ONLY state of your application at a point in time. You are recycling things and reusing them.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!