13

I have read through the android documentation in https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html

I don't understand the description stated in the document about recycle() and refresh() method.

1. recycle() - Return an instance back to be reused.

  • The instance is return back to where?
  • In which scenario this instance going to be reused?
  • AccessibilityNodeInfo might contains child node, do i need to call recycle() when my code navigate to each of the node or just call recycle method at root node?

2. refresh() - Refreshes this info with the latest state of the view it represents

  • I thought when onAccessibilityEvent() method was called, the AccessibilityEvent object should contain the latest state?
  • AccessibilityNodeInfo might contains child node, do i need to call refresh() when my code navigate to each of the node or just call refresh method at root node?

2 Answers 2

12

The Android Accessibility API uses a pool of AccessibilityNodeInfo nodes. That way, iterating over large trees will not create many objects that would slow down the garbage collector. In other words, when you recycle() a node, you might later (on the next event for example, or when iterating over the same tree) receive the exact same node object again, but filled with completely different details. That's why it is important you don't hold references to nodes you have recycled (and for example try to compare them to other nodes).

When you obtain child nodes, you have to recycle each child node. When you do not get them, you do not need to recycle them. You can recycle children before you recycle parents, or the other way round, depending on how long you will need access to the objects.

When you receive a node, it contains the latest state. But when you perform an action on it (e.g. click or scroll), the state of the node or of other nodes may change. If you want to see these changes in real-time (and not only when you receive the next event), you have to refresh() the node (or you can refresh() the root and try to obtain a new copy of the node from the root)

When you freshly obtain child nodes, you do not need to refresh them (they are already fresh). You only need to refresh nodes that you obtained earlier (before doing some interaction with them or with other nodes).

3

recycle()

In my (relatively brief) usage of an accessibility service, it seems you are not expected to call recycle(), or at least not on nodes you have not explicitly requested (e.g. through one of the functions mentioned below); I have received some fairly arbitrary crashes from this due to the backing cache (in the SDK) clearing nodes that it itself was holding on to (!) and I've looked at the source code and it appears all called nodes are added to a backing cache that later gets cleared. There are few ways to safely check if a node was already recycled and the SDK code definitely does not.

By holding on to nodes I have found that they eventually get recycled by some SDK code, too, thus my observation that it may not be expected for developers to do it themselves.

refresh()

At the time you receive a node, it is accurate. If you perform any actions that give you another node (getChild(i), findAccessibilityNodeInfosByViewId(viewId), etc.), the SDK calls a function to retrieve fresh info -- so it will be accurate at the time.

It seems to be relatively safe to hold on to nodes so long as you check their validity; However, most fields nevermind functions will throw an exception if the node has already been recycled somewhere. So: I would recommend you create yourself a convenience isValid() function to check this on all nodes. Just check that it isn't null (some events will give you a null source!) and that the className attribute is not null (always safe to check, and is nulled when unused or recycled).

Not the answer you're looking for? Browse other questions tagged or ask your own question.