无障碍服务(AccessibilityService)
使用步驟
1 繼承 AccessibilityService
2 配置輔助模式
配置 AccessibilityService 有兩種方式,
- 通過 xml 配置文件
accessibilityEventTypes:監聽的事件類型,例如:typeAllMask 表示全部事件,而 typeViewClicked 表示只監聽點擊事件。
 accessibilityFeedbackType:監聽事件的反饋模式。
 canRetrieveWindowContent:是否允許獲取視圖層級的訪問權,如果它被設置為 false,node.getSource() 方法會調用失敗。
 accessibilityFlags:指定 Flag,一般用于指定根據 Node 獲取 View ID 的權限。
 packageNames:開啟監聽的應用包名,可以指定多個包名,通過逗號“,”分割,不設置此屬性標識全局監聽。
 description:輔助功能的描述,它會顯示在系統設置的“無障礙”中的描述信息中。
 notificationTimeout:響應的毫秒數。
更多配置參數:https://developer.android.com/reference/android/accessibilityservice/AccessibilityService
- 通過 Java 代碼中動態配置。
 重寫 AccessibilityService 的 onServiceConnected() 方法(不推薦)
3 清單文件中注冊服務
<service android:label="無障礙名"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"android:name=".WeForwardServer"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService"/></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility_config"/> </service>4 開啟輔助模式
默認為關閉狀態,打開它的時候,你會收到一個警告彈窗,說明當前你正在開啟一個無障礙的服務
編寫邏輯代碼
1 判斷事件
- eventType 判斷事件類型(通過 eventType 來判斷事件的類型,我們可以利用 getEventType() 方法獲取到它。)
- packageName 判斷事件發生的 App(通過 getPackageName() 方法,判斷出事件發生在那個 App 里的。)
- className 判斷當前發生事件的是那個類(通過 getClassName() 判斷當前發生事件的是那個類)
- text 判斷當前事件觸發源上的 Text(通過 getText() 獲取當前事件源的 text 屬性,可能是 TextView 的 Text,也可能是 Activity 的 Label 屬性)
2 找到待控制的關鍵節點(Node)
通常我們是使用輔助模式去操作頁面上的某個元素,那這一步,就是為了找到它。
在輔助模式下,頁面上的每個元素,其實都是一個個 AccessibilityNodeInfo 節點,它是一個類似樹形的結構,其內和我們真實 App 內的布局層級是一致的,但是并不能將它單純的理解成一個 ViewTree。
既然是樹形結構,我們首先要獲取到根節點的 NodeInfo,可以通過以下兩個方式獲取:
event.getSource() getRootInActiveWindow()這兩個方法都會返回一個 AccessibilityNodeInfo 對象。getSource() 是AccessibilityEvent 的方法,它可用的前提是前面配置 android:canRetrieveWindowContent 的時候,被設置為 True。所以我推薦使用 getRootInActiveWindow() 方法來獲取。這兩個方法還是略微有些差異,有興趣可以打斷點看看信息,但是大多數情況下,對我們使用者來說是一致的。
獲得根節點的 AccessibilityNodeInfo 之后,就可以通過它找到我們想操作的關鍵節點,在 AccessibilityNodeInfo 中,提供了以下兩個方法來找到關鍵節點。
findAccessibilityNodeInfosByViewId(String viewId) findAccessibilityNodeInfosByText(String text)一個是依賴 ViewId,另外一個是依賴 Text 信息。
使用 ViewId 查找關鍵節點是穩妥的方案,而使用 Text 去查找,可能會找不到。
無論通過哪種方式查找 關鍵節點 ,都是存在能找到多個 NodeInfo 的可能的,所以這兩個方法干脆的都返回了一個 List ,所以需要我們通過其他條件再過濾一遍,通常就是通過 Text 信息過濾。
var mNodeInfo = rootInActiveWindow var listItem = mNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/lp") for (item in listItem) {if (item.text.toString().equals("承香墨影")){nodeClick(item)} }如果是使用 findXxxByText() 的方法的話,還需要注意它實際上不是通過類似 == 或者 equals() 的方法來查找子節點的,而是通過類似 contain() 的方式,所以只要節點的 text 屬性包含查找的內容,都會被找到,這個我們額外還需要增加判斷條件。
如果這些方法都試過,還是找不到關鍵節點,可以通過遍歷的方式查找。
AccessibilityNodeInfo 既然是一個樹狀結構,也提供了我們遍歷樹的方法。
getParent():查找父節點。
 getChild():返回子節點。
 getChildCount():當前節點的子節點個數。
 通過 getChild() 和 getChildCount() 兩個方法,我們是可以對整個 ViewNodeTree 進行遍歷,來找到我們關注的關鍵節點,這是一個最后的方案,并不推薦使用。
3 觸發事件
輔助模式一般都是幫助我們響應一些事件,而這些事件大體上,可以分為兩類。
- 全局系統事件。
 對于全局系統事件,其實我們并不需要第二步找到的關鍵節點。AccessibilityService 提供了一個 performGlobalAction() 方法,我們可以通過該方法,操作一些全局的系統事件,例如:模擬返回鍵點擊、模擬 HOME 鍵點擊、鎖屏等等。
除了全局系統事件之外,通常我們就是想操作第二步拿到的關鍵節點。
在 AccessibilityNodeInfo 中,提供了一個 performAction() 的方法,可以通過該方法,對關鍵節點傳遞一個我們需要的事件。
這些事件都被定義在 AccessibilityNodeInfo 中,以 ACTION_ 為前綴定義。例如:ACTION_CLICK 是一個點擊事件,ACTION_SET_TEXT 設置一個輸入。
這里僅介紹一些比較常見的操作,更多的操作也是類似的使用方式。
- View 事件。
 找到關鍵節點之后,就可以發送 AccessibilityNodeInfo.ACTION_CLICK 模擬對這個 View 的點擊操作。
對 EditText 輸入文字,最少需要兩個參數,關鍵節點和輸入的文字。這就需要用到 performAction() 的另外一個重載方法,它允許額外在傳遞一個 Bundle 來指定參數。
所有支持定義的額外參數,都被定義在 AccessibilityNodeInfo 中,并以 ACTION_ARGUMENT_ 為前綴定義。
 3. ListView 的滾動
 AccessibilityNodeInfo 其實只能操作當前屏幕下可見的 節點,所以碰上 ListView 或者 RecycleView 這種列表,就需要對它進行滾動。
滾動的事件有兩種:
- ACTION_SCROLL_FORWARD
- ACTION_SCROLL_BACKWARD
一個前進一個后退,足夠使用了。
4 回收資源
nodeInfo.recycle();總結
以上是生活随笔為你收集整理的无障碍服务(AccessibilityService)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 【开源掌机】百问网DShanMCU-Mi
- 下一篇: Docker hub配置国内加速器
