Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Accessibility使用及原理簡介

Android Accessibility使用及原理簡介

編輯:關於Android編程

Accessibility是Android從API 4開始提供的一個功能,它主要目的是幫助一些因為有視覺,聽覺,身體障礙而無法完全使用觸摸屏或鈴聲等的用戶來使用Android的。而實際上現在很多開發者都用它來實現一些其他功能了,比如說微信搶紅包,自動安裝APK,強制停止應用等。下面來簡單介紹一下它的相關使用及原理。

AccessibilityService

它最主要的接口是類AccessibilityService。AccessibilityService是Service的子類,我們可以繼承這個類並實現它的抽象方法來監視一個應用的界面元素狀態的變化,比如focus變化,一個按鈕被click等等。當有這些變化的時候,系統會將這些信息封裝在AccessibilityEvent裡面,回調AccessibilityService的onAccessibilityEvent(AccessibilityEvent)方法。我們可以實現onAccessibilityEvent來處理這些AccessibilityEvent。下面看一步一步地使用示例:

實現AccessibilityService

這裡使用ApiDemo當中將文字轉換為語音的例子來介紹,這段代碼在/samples//ApiDemos/src/com/example/android/apis/accessibility/TaskBackService,如何使用ApiDemo可以參考Samples。下面是簡單介紹裡面的核心代碼:

/**

 * 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的輔助,可以看一下TaskListView的界面:

AccessibilityService的配置聲明

首先它跟普通的Service一樣,需要在Manifest文件中聲明:


     
         
     
     

 

與其他Service不同的是裡面有個元數據標志了配置資源—taskbackconfig。taskbackconfig裡面的內容如下:


其中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有整體把握。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved