當前位置:
首頁 > 最新 > 輔助模式終究考驗的是想像力,先來看看怎麼用

輔助模式終究考驗的是想像力,先來看看怎麼用

承香墨影

只分享最有用的原創技術乾貨!

關注

題圖:by @photoartbykulpixx

正文共: 4457字

預計閱讀時間: 17分鐘


Hi,大家好,我是承香墨影!

Android 的輔助模式(Accessibility)功能非常的強大。基本上被獲取到授權之後,可以監聽手機上的任何事件,例如:屏幕點擊、窗口的變化、以及模擬點擊、模擬系統按鍵等等。

比較常見的實際使用例子,就是一般應用市場,會推薦開啟輔助模式,以便在安裝 Apk 的時候,自動幫你點擊「下一步」和「安裝」按鈕。還有個例子就是微信搶紅包插件,也是基於它來實現的。

Accessibility 的許可權非常的高,基本上你授權開啟某個別人提供的 AccessibilityService 之後,他就可以干很多事情而不讓你知道,而這些是不需要 Root 許可權的。所以一般小體量的產品,可能支持它並沒有什麼用,因為信任度太低了,大部分用戶根本不會打開。比較常見的就是一些工具類的 App,幫用戶節省一些點擊的時間。

雖然很多時候,Accessibility 不會被用在商業產品上,但是這並不妨礙我們使用 Accessibility 來做一些有意思的功能。


輔助模式是可以支持第三方開發,也就是我們可以按照文檔對其進行支持,只要用戶授權開啟此服務,我們就可以利用 Accessibility 提供的一些標準 Api 實現很多有意思的功能。

如果你想要使用輔助模式,你還需要如下步驟:

實現一個繼承自 AccessibilityService 的服務類。

設定配置信息,以便系統知道該輔助模式的一些基本信息,例如監聽那些事件。

在清單文件(AndroidManifest.xml)中,註冊此服務。

在系統設置中,找到「無障礙」,並開啟此服務。

接下來我們一步一步講解這裡的步驟和細節。

2.1 繼承 AccessibilityService

輔助模式,本質上還是一個服務,我們如果想要支持它,首先需要繼承 AccessibilityService 這個類。

AccessibilityService 類提供了很多需要重寫的方法,其中有兩個是強制重寫的:

當開啟了某個 AccessibilityService 服務之後,系統會在該服務監聽的事件發生的時候,回調它的 方法,並將該事件的信息當參數傳遞過去,如果你監聽的事件足夠多,它就會被頻繁調用。

而 方法會在系統事件被打斷的時候回調,也是會被頻繁調用,一般我們不需要做額外處理。

通常我們只需要在 方法中,編寫核心邏輯即可,其他的方法,只是輔助使用。

2.2 配置輔助模式

當創建一個 AccessibilityService 之後,我們還需要對其進行一些基本的配置,否則在系統設置的「無障礙」中,是看不到我們編寫的服務的。

配置 AccessibilityService 有兩種方式,

通過 xml 配置文件

通過 Java 代碼中動態配置。

但是其實有一些屬性是只能通過 XML 配置文件進行配置的,Java 代碼只是讓某一些配置項更靈活了而已,後面會細說。

1、xml 配置文件

想要使用 XML 配置文件,首先需要創建一個 res/xml 的目錄,並在其內創建一個 xml 文件,文件名隨意無要求,內部定義一個 標籤,在其中設定 AccessibilityService 的各項配置。例如我這裡創建一個 的文件,後面會用到這個文件。

XML 配置 AccessibilityService 是我們一個比較常用的配置方法,非常清晰且方便。

例如上面就是一個常見的配置,如果沒有特殊要求的話,直接複製過去,修改一些個別參數就可以使用。

各項屬性的含義:

accessibilityEventTypes:監聽的事件類型,例如:typeAllMask 表示全部事件,而 typeViewClicked 表示只監聽點擊事件。

accessibilityFeedbackType:監聽事件的反饋模式。

canRetrieveWindowContent:是否允許獲取視圖層級的訪問權,如果它被設置為 false, 方法會調用失敗。

accessibilityFlags:指定 Flag,一般用於指定根據 Node 獲取 View ID 的許可權。

packageNames:開啟監聽的應用包名,可以指定多個包名,通過逗號「,」分割,不設置此屬性標識全局監聽。

description:輔助功能的描述,它會顯示在系統設置的「無障礙」中的描述信息中。

notificationTimeout:響應的毫秒數。

這些可配置的參數,系統都提供了可選的配置參數,正常不需要額外定製的時候,使用上面默認的配置即可,如果有定製需要,還是查閱官方文檔獲得最全的介紹。

AccessibilityService:

https://developer.android.com/reference/android/accessibilityservice/AccessibilityService

2、Java 代碼中動態配置

除了 XML 文件配置的方式,我們還可以通過重寫 AccessibilityService 的 方法,我們首先需要構建一個 AccessibilityServiceInfo 對象,通過它的標準 Api 進行配置,再使用 方法將它設置給輔助模式。

會在應用成功連接到此輔助服務的時候系統調用,一般在其中做一些初始化的操作即可。

這裡提供的例子,其實和前面使用 XML 配置的效果一直。推薦使用 XML 的配置方式,會更清晰且靈活,而且像 description 這種屬性,在 AccessibilityServiceInfo 中,並沒有提供有效的類似setDescription()方法,這一點也確實是設計如此,畢竟服務沒有運行,就不存在描述信息,在系統設置的「無障礙」頁面,就讀取不到。

也就是說即便是使用 方法動態設置,也逃不脫使用 XML 配置文件的方式,我還是強烈建議都使用 XML 配置文件的方式配置輔助服務,主要是為了省事。

2.3 清單文件中註冊服務

本質上 AccessibilityService 還是一個 Service,使用它我們還需要在清單文件中配置它。

這就是一個標準的 Service,其中 會被解析在系統設置的「輔助模式中顯示」,而 和 按照格式寫就好了,沒什麼原因。

中,通過 屬性指定的就是我們在第二步編輯的配置文件路徑,指定它就好了。

2.4 開啟輔助模式

以上步驟都完成之後,你就可以在系統的「無障礙」設置里,看到你編寫的輔助模式的開關了。

默認為關閉狀態,打開它的時候,你會收到一個警告彈窗,說明當前你正在開啟一個無障礙的服務,它有哪些許可權,這個對話框,我們是控制不了的。

open-accessibility

注意這裡的 Title 就是清單文件里配置的 ,而描述就是 XML 配置文件里的 信息。

當你在系統設置里,能看到此開關的時候,就說明你的輔助模式的服務,配置的沒問題了,接下來就要思考如何使用它。


前面提到,在 AccessibilityService 里,我們最需要關注的就是 方法,它會在我們監聽的事件發生的時候,被系統回調,並傳遞過來該事件相關的信息。

接下來我們看看如何在 回調方法里,編寫具體的邏輯。

接下來 "程序員思維" 要上線了,把大象關冰箱,需要幾步。我們接下來來拆分輔助模式的步驟。

判斷事件, 會被回調多次,而我們只需要處理我們關心的事件,其他的忽略過濾掉即可。

找到需要控制的關鍵節點(Node),以便之後進行控制。

對關鍵節點,發送對於的操作事件,以便完成我們的步驟。

回收資源,防止資源泄露。

很簡單對不對,接下來我們細細的說下,這些步驟相關的方法和屬性。

3.1 判斷事件

當 被系統回調的時候,同時也會傳遞過來一個 AccessibilityEvent 對象,它其中包含了很多與當前事件相關的信息,有興趣可以看看源碼,我們這裡只關注最需要的幾個屬性。

1、eventType 判斷事件類型

通過 eventType 來判斷事件的類型,我們可以利用 方法獲取到它。

這些事件都很好辨認,例如:TYPE_NOTIFICATION_STATE_CHANGED 是一個窗口 View 發生了變化,TYPE_VIEW_CLICKED 是某個 View 發生了一次點擊事件等等。

2、packageName 判斷事件發生的 App

通過 方法,判斷出事件發生在那個 App 里的。

3、className 判斷當前發生事件的是那個類

通過 判斷當前發生事件的是那個類,例如 頁面的顯示,className 可能指向一個 Activity,一個按鈕的點擊,className 可能指向的是一個 Button,這些都是根據實際場景區分的。

4、text 判斷當前事件觸發源上的 Text

通過 獲取當前事件源的 text 屬性,可能是 TextView 的 Text,也可能是 Activity 的 Label 屬性,依然是根據實際情況區分。

一般我們可以通過以上幾種方式,猜測是否是我們需要監聽的事件,下一步就是我們找到我們要操作的源。

3.2 找到待控制的關鍵節點(Node)

通常我們是使用輔助模式去操作頁面上的某個元素,那這一步,就是為了找到它。

在輔助模式下,頁面上的每個元素,其實都是一個個 AccessibilityNodeInfo 節點,它是一個類似樹形的結構,其內和我們真實 App 內的布局層級是一致的,但是並不能將它單純的理解成一個 ViewTree。

既然是樹形結構,我們首先要獲取到根節點的 NodeInfo,可以通過以下兩個方式獲取:

event.getSource()

getRootInActiveWindow()

這兩個方法都會返回一個 AccessibilityNodeInfo 對象。 是AccessibilityEvent 的方法,它可用的前提是前面配置 的時候,被設置為 True。所以我推薦使用 方法來獲取。這兩個方法還是略微有些差異,有興趣可以打斷點看看信息,但是大多數情況下,對我們使用者來說是一致的。

獲得根節點的 AccessibilityNodeInfo 之後,就可以通過它找到我們想操作的關鍵節點,在 AccessibilityNodeInfo 中,提供了以下兩個方法來找到關鍵節點。

findAccessibilityNodeInfosByViewId(String viewId)

findAccessibilityNodeInfosByText(String text)

一個是依賴 ViewId,另外一個是依賴 Text 信息。

使用 ViewId 查找關鍵節點是穩妥的方案,而使用 Text 去查找,可能會找不到。

無論通過哪種方式查找關鍵節點,都是存在能找到多個 NodeInfo 的可能的,所以這兩個方法乾脆的都返回了一個 ,所以需要我們通過其他條件再過濾一遍,通常就是通過 Text 信息過濾。

如果是使用 的方法的話,還需要注意它實際上不是通過類似 或者 的方法來查找子節點的,而是通過類似 的方式,所以只要節點的 text 屬性包含查找的內容,都會被找到,這個我們額外還需要增加判斷條件。

如果這些方法都試過,還是找不到關鍵節點,可以通過遍歷的方式查找。

AccessibilityNodeInfo 既然是一個樹狀結構,也提供了我們遍歷樹的方法。

getParent():查找父節點。

getChild():返回子節點。

getChildCount():當前節點的子節點個數。

通過 和 兩個方法,我們是可以對整個 ViewNodeTree 進行遍歷,來找到我們關注的關鍵節點,這是一個最後的方案,並不推薦使用。

3.3 觸發事件

輔助模式一般都是幫助我們響應一些事件,而這些事件大體上,可以分為兩類。

全局系統事件。

View 事件。

對於全局系統事件,其實我們並不需要第二步找到的關鍵節點。AccessibilityService 提供了一個 方法,我們可以通過該方法,操作一些全局的系統事件,例如:模擬返回鍵點擊、模擬 HOME 鍵點擊、鎖屏等等。

這些事件被封裝在 AccessibilityService 中,以 為前綴,看看屬性說明就懂了。

除了全局系統事件之外,通常我們就是想操作第二步拿到的關鍵節點。

在 AccessibilityNodeInfo 中,提供了一個 的方法,可以通過該方法,對關鍵節點傳遞一個我們需要的事件。

這些事件都被定義在 AccessibilityNodeInfo 中,以 為前綴定義。例如:ACTION_CLICK 是一個點擊事件,ACTION_SET_TEXT 設置一個輸入。

這裡僅介紹一些比較常見的操作,更多的操作也是類似的使用方式。

1.View 的點擊

找到關鍵節點之後,就可以發送 模擬對這個 View 的點擊操作。

但是有時候它是不生效的,主要原因是因為你找到的這個關鍵節點,它的 為 false。

例如微信的這個公眾號分享彈窗,如果我們想要查找「發送給朋友」,其實最好的辦法是找到這個 TextView 控制項所代表的關鍵節點(NodeInfo),然後對它進行點擊。而實際上這個 TextView 是不具有點擊效果的,它的 為 false。

這個時候可以想一個折中的方案,去找關鍵節點(NodeInfo)的父節點,再去判斷它是否可點擊,可點擊則點擊它,否則繼續向上找。

雖然 AccessibilityNodeInfo 其實也開放了 方法,但是我不建議操作它,有些時候會拋出一個異常,不太穩定。

2.EditText 輸入文字

對 EditText 輸入文字,最少需要兩個參數,關鍵節點和輸入的文字。這就需要用到 的另外一個重載方法,它允許額外在傳遞一個 Bundle 來指定參數。

所有支持定義的額外參數,都被定義在 AccessibilityNodeInfo 中,並以 為前綴定義。

3.ListView 的滾動

AccessibilityNodeInfo 其實只能操作當前屏幕下可見的 節點,所以碰上 ListView 或者 RecycleView 這種列表,就需要對它進行滾動。

滾動的事件有兩種:

ACTION_SCROLL_FORWARD

ACTION_SCROLL_BACKWARD

一個前進一個後退,足夠使用了。

3.4 回收資源

在使用完 AccessibilityNodeInfo 之後,別忘了還需要調用 方法,釋放資源。


輔助模式如何使用,到現在已經講解的非常清楚了,後面基本上就是靠自己的想像力來做小功能了。

利用輔助模式,發揮想像力,你也可以做出很多有意思的功能。

「聯機圓桌」一年 50 個優質問題,上桌聯機學習。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 承香墨影 的精彩文章:

TAG:承香墨影 |