利用AccessibilityService實(shí)現(xiàn)探探自動(dòng)右劃

https://github.com/zhaokidd/TanTanAutoSwipe

核心類: AccessibiliiiiiiiityService


簡(jiǎn)單的說(shuō),AccessibilityService 為了方便殘障人士實(shí)現(xiàn)操作,可以監(jiān)控app的運(yùn)行,在界面上實(shí)現(xiàn)一些自動(dòng)操作.
AccessibilityService 的原理簡(jiǎn)單的來(lái)說(shuō)就是: 安卓中 View 組件會(huì)主動(dòng)發(fā)出各種狀態(tài)改變的廣播,Service收到后可以判斷狀態(tài)也可以獲取發(fā)出廣播的組件的view和view的子組件.
而實(shí)現(xiàn)一個(gè) AccessibilityerService 需要三步

  • 在xml文件中進(jìn)行配置
  • 在 AndroidManifest 文件中進(jìn)行配置
  • 實(shí)現(xiàn)AccessibilityService,監(jiān)控app界面中的操作.

配置 xml 文件


在 res -> xml -> 路徑下新建一個(gè) autoservice_config 文件

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags=""
    android:canRetrieveWindowContent="true"
    android:description="@string/app_name"
    android:notificationTimeout="100">

</accessibility-service>

這個(gè)配置文件中有兩個(gè)選項(xiàng)比較重要:

android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"

eventTypes 這個(gè)選項(xiàng)決定了你寫的Service能收到什么類型的事件。
android:canRetrieveWindowContent 這個(gè)選項(xiàng)決定了能否通過(guò) service提供的方法獲取到app中的view.


在AndroidManifest文件中配置 AccessibilityService


        <service
            android:name=".service.MyAutoService"
            android:enabled="true"
            android:label="@string/app_label"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
            android:process="@string/isolated_process">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>

            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/autoservice_config" />
        </service>

接著在manifest中配置 AccessibilityService,這里按配置規(guī)則來(lái)就可以了.


實(shí)現(xiàn)自己的自定義 AccessibilityService


接著就是最重要的部分,我們要根據(jù)要監(jiān)控的app實(shí)現(xiàn)自己的自定義AccessibilityService , 要做的并不復(fù)雜,實(shí)現(xiàn)要清楚幾個(gè)概念

AccessibilityNodeInfo

界面中所有的view元素都會(huì)作為這么一個(gè)類出現(xiàn).

AccessibilityEvent

Event事件,當(dāng)app進(jìn)出前后臺(tái)或者有點(diǎn)擊滑動(dòng)等事件的時(shí)候,都會(huì)發(fā)出這么一個(gè)event

onAccessibilityEvent()

接收 event的回調(diào)方法,我們就在這個(gè)回調(diào)中實(shí)現(xiàn)自己的邏輯.

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        String packageName = event.getPackageName().toString();
        if (PACKAGE_NAME_TANTAN.equals(packageName)) {
            Log.d(TAG, event.getPackageName().toString() + event.getEventType());
            switch (event.getEventType()) {
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
                    AccessibilityNodeInfo mNodeInfo = getRootInActiveWindow();
                    Rect rect = new Rect();
                    mNodeInfo.getBoundsInScreen(rect);
                    boolean isPerformed = mNodeInfo.performAction(GESTURE_SWIPE_RIGHT);
                    accessibilityNodeInfo = mNodeInfo;
                    traversalNodeInfo(mNodeInfo);
                    performLoveButtonClick(mLoveButton);
//                    performSwipeRight(mNodeInfo);
                    Log.d(TAG, "child counts is :" + mNodeInfo.getChildCount() + "\nrect is :" + rect.toString() + "\n performed is :" + isPerformed);
                    break;
            }
        }
    }

    private void performLoveButtonClick(final AccessibilityNodeInfo mLoveButton) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        boolean isClicked = mLoveButton.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                        Log.d(TAG, "love Button is clicked = " + isClicked);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    private void traversalNodeInfo(AccessibilityNodeInfo mNodeInfo) {
        int mChildCount = mNodeInfo.getChildCount();
        Log.d(TAG, "class name:" + mNodeInfo.getClassName().toString() + "  child count is :" + mChildCount + "\n");
        mLoveButton = mNodeInfo;
        if (mChildCount > 0) {
            for (int i = 0; i < mChildCount; i++) {
                traversalNodeInfo(mNodeInfo.getChild(i));
            }
        }
    }

思路很簡(jiǎn)單:在 onAccessibilityEvent() 方法中收到 event后,判斷事件類型,如果要監(jiān)控的app出現(xiàn)在前臺(tái)(接收到TYPE_WINDOW_STATE_CHANGED事件),此時(shí)遍歷所有view找到探探軟件中的喜歡按鈕,然后模擬點(diǎn)擊事件就OK了。


如何找到指定的view呢?


微信圖片_20170604132450.png

之前的話,可以通過(guò) android自帶的 uiautomatorviewer 這個(gè)工具來(lái)獲取到界面上的控件的id,然后通過(guò) findAccessibilityNodeInfosByViewId() 這個(gè)方法來(lái)獲取到制定的view,但是現(xiàn)在的很多app已經(jīng)把id隱藏掉了,所以這個(gè)方法行不通了.
現(xiàn)在除了遍歷所有的view也沒有更好的解決辦法了.
好在探探里的這個(gè)喜歡按鈕在view樹的最后一個(gè)節(jié)點(diǎn),我們只要前序遍歷這棵view樹,保留最后一個(gè)view就能獲取的這個(gè)button。

    private void traversalNodeInfo(AccessibilityNodeInfo mNodeInfo) {
        int mChildCount = mNodeInfo.getChildCount();
        Log.d(TAG, "class name:" + mNodeInfo.getClassName().toString() + "  child count is :" + mChildCount + "\n");
        mLoveButton = mNodeInfo;
        if (mChildCount > 0) {
            for (int i = 0; i < mChildCount; i++) {
                traversalNodeInfo(mNodeInfo.getChild(i));
            }
        }
    }
 boolean isClicked = mLoveButton.performAction(AccessibilityNodeInfo.ACTION_CLICK);

最后一步開啟一個(gè)工作線程,定時(shí)點(diǎn)擊button就ok了.

需要注意的問題


所有的自動(dòng)操作都需要app保持在與用戶交互的狀態(tài)下才能進(jìn)行,目前沒有找到在后臺(tái)也能讓監(jiān)控服務(wù)正常運(yùn)行的方法.

最后附上github地址,想了解的朋友可以看下源碼:https://github.com/zhaokidd/TanTanAutoSwipe

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,234評(píng)論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,663評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,282評(píng)論 6 342
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 7,363評(píng)論 0 17
  • 1在最沒有安全感的愛情里,尋找安全感,注定是要失望的。 五瓶啤酒下肚,一直強(qiáng)忍著的維然終于繃不住,在我面前哭成了淚...
    路人記閱讀 1,915評(píng)論 13 14

友情鏈接更多精彩內(nèi)容