Android Accessibility使用及事件流程簡介
Accessibility是Android從API 4開始提供的一個功能,它主要目的是幫助一些因為有視覺,聽覺,身體障礙而無法完全使用觸控式螢幕或鈴聲等的使用者來使用Android的。而實際上現在很多開發者都用它來實現一些其他功能了,比如說微信搶紅包,自動安裝APK,強制停止應用等。下面來簡單介紹一下它的相關使用及原理。
AccessibilityService
它最主要的介面是類AccessibilityService。AccessibilityService是Service的子類,我們可以繼承這個類並實現它的抽象方法來監視一個應用的介面元素狀態的變化,比如focus變化,一個按鈕被click等等。當有這些變化的時候,系統會將這些資訊封裝在AccessibilityEvent裡面,回撥AccessibilityService的onAccessibilityEvent(AccessibilityEvent)方法。我們可以實現onAccessibilityEvent來處理這些AccessibilityEvent。下面看一步一步地使用示例:
實現AccessibilityService
/**
* This class demonstrates how an accessibility service can query
* window content to improve the feedback given to the user.
*/
public class TaskBackService extends AccessibilityService implements OnInitListener {
/** Tag for logging. */
private static final String LOG_TAG = "TaskBackService/onAccessibilityEvent";
/** Comma separator. */
private static final String SEPARATOR = ", ";
/** The class name of TaskListView - for simplicity we speak only its items. */
private static final String TASK_LIST_VIEW_CLASS_NAME =
"com.example.android.apis.accessibility.TaskListView" ;
/** Flag whether Text-To-Speech is initialized. */
private boolean mTextToSpeechInitialized;
/** Handle to the Text-To-Speech engine. */
private TextToSpeech mTts;
@Override
public void onServiceConnected() {
// Initializes the Text-To-Speech engine as soon as the service is connected.
mTts = new TextToSpeech(getApplicationContext(), this);
}
/**
* Processes an AccessibilityEvent, by traversing the View's tree and
* putting together a message to speak to the user.
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (!mTextToSpeechInitialized) {
Log.e(LOG_TAG, "Text-To-Speech engine not ready. Bailing out.");
return;
}
// This AccessibilityNodeInfo represents the view that fired the
// AccessibilityEvent. The following code will use it to traverse the
// view hierarchy, using this node as a starting point.
//
// NOTE: Every method that returns an AccessibilityNodeInfo may return null,
// because the explored window is in another process and the
// corresponding View might be gone by the time your request reaches the
// view hierarchy.
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
// Grab the parent of the view that fired the event.
AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
if (rowNode == null) {
return;
}
// Using this parent, get references to both child nodes, the label and the checkbox.
AccessibilityNodeInfo labelNode = rowNode.getChild(0);
if (labelNode == null) {
rowNode.recycle();
return;
}
AccessibilityNodeInfo completeNode = rowNode.getChild(1);
if (completeNode == null) {
rowNode.recycle();
return;
}
// Determine what the task is and whether or not it's complete, based on
// the text inside the label, and the state of the check-box.
if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
rowNode.recycle();
return;
}
CharSequence taskLabel = labelNode.getText();
final boolean isComplete = completeNode.isChecked();
String completeStr = null;
if (isComplete) {
completeStr = getString(R.string.task_complete);
} else {
completeStr = getString(R.string.task_not_complete);
}
String taskStr = getString(R.string.task_complete_template, taskLabel, completeStr);
StringBuilder utterance = new StringBuilder(taskStr);
// The custom ListView added extra context to the event by adding an
// AccessibilityRecord to it. Extract that from the event and read it.
final int records = event.getRecordCount();
for (int i = 0; i < records; i++) {
AccessibilityRecord record = event.getRecord(i);
CharSequence contentDescription = record.getContentDescription();
if (!TextUtils.isEmpty(contentDescription )) {
utterance.append(SEPARATOR);
utterance.append(contentDescription);
}
}
// Announce the utterance.
mTts.speak(utterance.toString(), TextToSpeech.QUEUE_FLUSH, null);
Log.d(LOG_TAG, utterance.toString());
}
private AccessibilityNodeInfo getListItemNodeInfo(AccessibilityNodeInfo source) {
AccessibilityNodeInfo current = source;
while (true) {
AccessibilityNodeInfo parent = current.getParent();
if (parent == null) {
return null;
}
if (TASK_LIST_VIEW_CLASS_NAME.equals(parent.getClassName())) { //找到TaskListView
return current;
}
// NOTE: Recycle the infos. 記得回收AccessibilityNodeInfo
AccessibilityNodeInfo oldCurrent = current;
current = parent;
oldCurrent.recycle();
}
}
/**
* {@inheritDoc}
*/
@Override
public void onInterrupt() {
/* do nothing */
}
上面這段程式碼是對TaskList的輔助。
AccessibilityService的配置宣告
首先它跟普通的Service一樣,需要在Manifest檔案中宣告:
<service android:name=".MyAccessibilityService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice" android:resource="@xml/taskbackconfig" />
</service>
與其他Service不同的是裡面有個元資料標誌了配置資源—taskbackconfig。taskbackconfig裡面的內容如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:packageNames="com.example.android.apis"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_query_window_description" />
其中packageNames限制了監視的包名,accessibilityEventType表示該AccessibilityService想要收到的Event事件型別。accessibilityFeedbackType表示提供的feedback型別,canRetrieveWindowContent是否可以獲取視窗的內容,description表示描述資訊,在設定中輔助功能裡面允許該應用進行輔助設定時,會顯示在那個頁面下面。
事件處理流程簡述
Accessibility是能夠獲取到控制元件的訊息,這些資訊是從哪傳遞出來的呢?傳遞的整個過程是怎樣的呢?
從View開始
事件是跟View相關的,它從View開始的,View實現了一個叫AccessibilityEventSource的介面,AccessibilityEventSource介面包含了兩個函式:
public void sendAccessibilityEvent(int eventType);
public void sendAccessibilityEventUnchecked(AccessibilityEvent event);
這兩個函式會用來發送AccessibilityEvent,比如在View收到點選事件時,當View的Focus狀態變化時等等,View呼叫這兩個介面來開啟發送AccessibilityEvent。
反向遞迴請求到ViewRootImpl
另外ViewGroup也實現了ViewParent介面,而ViewParent有一個介面函式requestSendAccessibilityEvent
,在子View中,sendAccessibilityEvent方法最終會呼叫getParent().requestSendAccessibilityEvent,一層一層地往上呼叫,最終在ViewRootImpl裡面使用AccessibilityManager的sendAccessibilityEvent
方法binder機制將AccessibilityEvent傳送給AccessibilityManagerService。
AccessibilityManagerService 管理核心
AccessibilityManagerService是在SystemServer中初始化的,並且新增到ServiceManager中。AccessibilityManagerService在有包變化的時候(安裝,解除安裝)更新AccessibilityService繫結,如下程式碼所示:
private void updateServicesLocked(UserState userState) {
if (userState.mIsAccessibilityEnabled) {
manageServicesLocked(userState);
} else {
unbindAllServicesLocked(userState);
}
}
傳送到AccessibilityService
在出現新的AccessibilityService時會去繫結AccessibilityService獲取繫結的結果IAccessibilityServiceClient(實際上是AccessibilityService中onBinder返回的IAccessibilityServiceClientWrapper的Proxy),跟AccessibilityService通訊相關的資訊儲存在AccessibilityManagerService.Service類當中。當AccessibilityManagerService收到Event後,會遍歷所有的Service,並通過IAccessibilityServiceClient將Event傳送給AccessibilityService。
從AccessibilityEvent事件產生到傳送到AccessibilityService,整個流程就是這樣了。整個過程如下圖所示:
這裡就簡單介紹一下流程,有機會再好好分析一下原始碼。
總結
AccessibilityService給我們提供了很多方便,我們可以利用AccessibilityService做很多巧妙的事情。使用AccessibilityService主要步驟就是繼承AccessibilityServcie服務,實現onAccessibilityEvent方法,配置好相關的內容,最後在AndroidMainfest宣告相關配置。知道AccessibilityService怎麼使用,最好也瞭解整個AccessibilityEvent事件流程,以能夠對AccessibilityService有整體把握。
相關推薦
Android Accessibility使用及事件流程簡介
Accessibility是Android從API 4開始提供的一個功能,它主要目的是幫助一些因為有視覺,聽覺,身體障礙而無法完全使用觸控式螢幕或鈴聲等的使用者來使用Android的。而實際上現在很多開發者都用它來實現一些其他功能了,比如說微信搶紅包,自動安裝A
Android 螢幕旋轉事件流程分析
WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm) [java] view plain copy print?pr
Android中Intent詳解(二)之使用Intent廣播事件及Broadcast Receiver簡介
通過第一篇的講解,我們已經看到了如何使用Intent來啟動新的應用程式元件,但是實際上他們也可以使用sendBroadcast方法來在元件間匿名的廣播訊息。 作為一個系統級別的訊息傳遞機制,Intent可以在程序之間傳送結構化的訊息。因此,通過實現Broadcast Rec
Android異步框架RxJava 1.x系列(二) - 事件及事件序列轉換原理
方式 名稱 pri 新的 pos strong 之一 調度 star 前言 在介紹 RxJava 1.x 線程調度器之前,首先引入一個重要的概念 - 事件序列轉換。RxJava 提供了對事件序列進行轉換的支持,這是它的核心功能之一。 正文 1. 事件序列轉換定
Android 7.0 Gallery圖庫源碼分析3 - 數據加載及顯示流程
不為 isempty stat submit mode 準備工作 RKE xtu ida 前面分析Gallery啟動流程時,說了傳給DataManager的data的key是AlbumSetPage.KEY_MEDIA_PATH,value值,是”/combo/{/loca
Android Activity startActivity流程簡介
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
OPhone/Android的學習 1 —初步知識,TextView Button Layout及事件響應
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Android O Settings原始碼流程分析(資料載入之獲取及修改預設設定屬性值)
Android O Settings 靜態介面篇 介面渲染篇 資料載入篇之一級選單 資料載入篇之二級選單 資料載入篇之獲取及修改預設設定屬性值 搜尋欄篇 載入預設設定值及修改:(涉及SettingsProvider) 示例:(裝置自動亮
JavaScript事件冒泡簡介及應用
一、什麼是事件冒泡 在一個物件上觸發某類事件(比如單擊onclick事件),如果此物件定義了此事件的處理程式,那麼此事件就會呼叫這個處理程式,如果沒有定義此事件處理程式或者事件返回true,那麼這個事件會向這個物件的父級物件傳播,從裡到外,直至它被處理(父級物件
Android 6.0許可權機制及開發流程詳解
許可權機制變更的背景 在Android6.0之前,app在安裝時會提示使用者此app需要使用哪些許可權,但使用者只能選擇同意或拒絕安裝,而不能單獨對某項許可權進行授予或拒絕。只要使用者選擇了安裝,即表示使用者接受了app對這些許可權的使用,如果使用者不希望app獲取某些涉及隱
Android O: 觸控事件傳遞流程原始碼分析(上)
前面的部落格中,我們通過例子分析了一下Android中事件傳遞的流程, 詳細內容可以參考:Android觸控事件傳遞機制簡要分析 貫穿整個Android的觸控事件分發的流程,基本可以抽象成以下的虛擬碼: public boolean dispatchT
Android原始碼分析——Looper,Messagequeue,Message,handler初始化及handler機制簡介
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }直接上原始碼可見是個private的構造
Android原始碼解析(二十七)-->HOME事件流程
上一篇文章中我們介紹了android系統的截圖事件,由於截圖事件是一種系統全域性處理事件,所以事件的處理邏輯不是在App中執行,而是在PhoneWindowManager中執行。而本文我們現在主要講解android系統中HOME按鍵的事件處理,和截圖事件類似
Android應用程式activity啟動流程簡介
無論是通過點選應用程式圖示來啟動Activity,還是通過Activity內部呼叫startActivity介面來啟動新的Activity,都要藉助於應用程式框架層的ActivityManagerService服務程序。Service也是由ActivityManager
Android原始碼解析(二十八)-->電源開關機按鍵事件流程
前面我們講解了系統截圖按鍵處理流程,HOME按鍵處理流程,今天再來講解一下電源開關機按鍵事件流程,當然這也是系統按鍵處理流程方面的最後一篇部落格了。 和截圖按鍵、HOME按鍵的處理流程類似,電源按鍵由於也是系統級別的按鍵,所以對其的事件處理邏輯是和截圖按鍵
Android 系統啟動流程簡介
1. Init 程序啟動流程2. Zygote啟動流程3. SystemServer啟動流程1. Init 程序啟動流程Android啟動流程 init程序 –> Zygote程序 –> SystemServer程序 –>各種應用程序Init 程序ini
Android之Android 6.0許可權機制及開發流程詳解
許可權機制變更的背景 在Android6.0之前,app安裝時會提示使用者此app需要使用哪些許可權,但使用者不能單獨對某項許可權進行授權或拒絕,只要使用者選擇了安裝,即表示使用者接受了app對這些許可權的使用,如果使用者不希望app獲取某些涉及隱私的資訊,例如讀取
網路程式設計之即時通訊程式(聊天室)------(一)通訊流程簡介及通訊協議定製
在開始講之前,我想先跟大家描述一下,這個所謂的通訊程式具體是一個什麼樣的東西。該通訊程式類似一個弱版本的qq,登入時需要進行註冊,登入成功後,可以實現即時的通訊,群聊,私聊,同時還可傳檔案。先上個圖 服務端:
Android 事件處理簡介
一、單擊事件 public TextView text = null; public Button btn = null; @Override protected vo
【android】點選touch事件流程分析
1、onTouch和onTouchEvent的區別 public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && mOnTouch