I haven’t written a good blog for a long time. I have been doing my own business for 20 years, working during the day and writing scripts at night. Although I did not learn a lot of things, I still want to share my little experience in automatic script.
preface
I have shared some introduction articles about Android auxiliary services before, and there are certainly countless related articles on the Internet now, which are no longer scarce. Here is a link to the article shared before, and friends who are not familiar with it can have a look at the previous article. As follows:
1. Introduction and configuration of Android auxiliary services
2. Android Accessibility and suspension Windows
3. Automatic wechat comments and likes for Android auxiliary services
Basic method
The secondary service provides two core methods by default. You can use text or ID to find the interface node. For details, see the link above
1, 2, findAccessibilityNodeInfosByViewId findAccessibilityNodeInfosByTextCopy the code
But those of you who have practiced it should know that there are limitations to these two methods. When we dump an interface, there is no text or id on the control node. Keep reading
Extension methods
In the absence of text or ID, we can loop through the root node information to find the desired control, such as by property desc, classname, bounds, etc. Is there any code out there that we can use?
Those of you who have worked on Android should know Uiautomator, an automated testing framework that provides many useful methods and interfaces. And uiAutomator core implementation is also based on auxiliary services, here refer to Auto. Js (currently on the market more popular automation script project) source, github address is github.com/hyb1996/Aut…
Stand on someone else’s shoulders and see how uiAutomator and accessibilityService can be combined. Open the auto-.js source code and find the UiSelector class. You can see that its constructor passes in the AccessibilityBridge, which can be interpreted as a bridge. The getService() method connects UiSelector and accessibilityService.
open class UiSelector(accessibilityBridge: AccessibilityBridge? , allocator: AccessibilityNodeInfoAllocator?) { private val mSelector = Selector() private var mSearchAlgorithm: SearchAlgorithm = DFS open fun id(id: String): UiSelector { mSelector.add(IdFilter.equals(id)) return this } fun idContains(str: String): UiSelector {mSelector. Add (idfilter. contains(STR)) return this} public abstract AccessibilityService getService(); // Core method protected Open fun findImpl(Max: Int): UiObjectCollection { val start = System.currentTimeMillis() val roots = mAccessibilityBridge!! .windowRoots() if (BuildConfig.DEBUG) Log.w(TAG, "find: roots = $roots") if (roots.isEmpty()) { return EMPTY } val result: MutableList<UiObject? > = ArrayList() for (root in roots) { if (root == null) { continue } if (root.packageName ! = null && mAccessibilityBridge!! .config.whiteListContains(root.packageName.toString())) { Log.d(TAG, "package in white list, return null") return EMPTY } result.addAll(findAndReturnList(createRoot(root, mAllocator), max - result.size)) if (result.size >= max) { break } } val end = System.currentTimeMillis() return of(result) } Funfindandreturnlist (node: UiObject, Max: Int = int.max_value): List<UiObject> { return mSearchAlgorithm.search(node, mSelector, max) } }Copy the code
Here mSearchAlgorithm is the search interface, corresponding to DFS (depth-first search algorithm), BFS (breadth-first search) algorithm, we know android view, can be understood as a “tree” structure, through this search can quickly traverse nodes.
object DFS : SearchAlgorithm { override fun search(root: UiObject, filter: Filter, limit: Int): ArrayList<UiObject> { val start = System.currentTimeMillis() val result = ArrayList<UiObject>() val stack = LinkedList<UiObject>() stack.push(root) while (stack.isNotEmpty()) { val parent = stack.pop() for (i in parent.childCount - 1 downTo 0) { val child = parent.child(i) ? : continue stack.push(child) } if (filter.filter(parent)) { result.add(parent) if (result.size >= limit) { break } } else { if (parent ! == root) {parent.recycle()}} val end = System.currentTimemillis () if (buildconfig.debug) log.w ("DFS", "time: " + (end - start) + "ms") return result } }Copy the code
And we can also see, as we’re walking through, as we’re pushing, if we look at the Filter interface, and we’re going to look at how the Filter is implemented, we can see what the Filter does, which is to match the conditions of each Selector of the mFilters in the Selector, only if all of them, The corresponding node is returned.
interface Filter { fun filter(node: UiObject): Boolean } class Selector : Filter { private val mFilters = LinkedList<Filter>() override fun filter(node: UiObject): Boolean { for (filter in mFilters) { if (! filter.filter(node)) { return false } } return true } fun add(filter: Filter) { mFilters.add(filter) } override fun toString(): String { val str = StringBuilder() for (filter in mFilters) { str.append(filter.toString()).append(".") } if (str.isNotEmpty()) { str.deleteCharAt(str.length - 1) } return str.toString() } }Copy the code
The node window
The getRootInActiveWindow() and getWindows() methods need to be distinguished. If the node cannot be located, you need to check which method is used for the window. One represents the currently active window and one represents all Windows. For example, in the case of ViewPager with Fragment, there will be overlapping. If the first method is used, node information may not be found. The second method, however, takes longer than just one window because you are searching across all Windows.
@Override
public AccessibilityNodeInfo getActiveRoot() {
return getRootInActiveWindow();
}
@Override
public List<AccessibilityWindowInfo> getActiveWindow() {
return getWindows();
}
Copy the code
At the end
This article learned how to expand the interface of auto-.js on the basis of UIAutomator to provide rich API for users to use, because it is really a bit defective to only rely on text and ID to locate nodes.
[Note: article source code study analysis use]