自動搶紅包,自動安裝原理之AccessibilityService

*本篇文章已授權(quán)微信公眾號 guolin_blog (郭霖)獨家發(fā)布

前段時間看別人博客的時候偶然間看到了Android微信自動回復(fù)功能,最后的效果也很不錯,博主在文中提到了<code>AccessibilityService</code>,以前壓根沒接觸過這東西,表示一臉懵逼。也是這個原因我去找了AccessibilityService相關(guān)的資料好好的看了一遍,發(fā)現(xiàn)這個東西真的太NB了,網(wǎng)上對AccessibilityService的應(yīng)用還是有不少的文章的,但是詳細的介紹資料還是比較少,對于剛剛學(xué)習(xí)這個的同學(xué)看完很多資料還是一臉茫然,于是才有了本文。我相信當(dāng)你在看完本文之后,再去看前面的那篇文章,我相信會輕松很多。


本文學(xué)習(xí)目錄

1.AccessibilityService是什么
2.AccessibilityService的創(chuàng)建與配置
3.怎么去使用AccessibilityService
4.一步一步構(gòu)建一個apk自動安裝器

1.AccessibilityService是什么

AccessibilityService是什么,官網(wǎng)是這樣解釋的

Accessibility services are intended to assist users with disabilities in using Android devices and apps. They run in the background and receive callbacks by the system whenAccessibilityEvents are fired.

也就是說這是個輔助功能,目的是輔助人們?nèi)ナ褂肁ndroid設(shè)備和應(yīng)用。它在后臺運行,可以接收系統(tǒng)的回調(diào)。

可見AccessibilityService的出現(xiàn)Google的初衷是輔助人們?nèi)ナ褂肁ndroid設(shè)備和應(yīng)用,但是當(dāng)你對它足夠了解了之后你會發(fā)現(xiàn)它的作用不僅僅只是這樣。對windows編程有了解的同學(xué)肯定知道hook(鉤子),AccessibilityService和windows下的hook有那么一點的相似。AccessibilityService可以攔截到系統(tǒng)發(fā)出的一些消息(比如窗體狀態(tài)的改變,通知欄狀態(tài)的改變,View被點擊了等等),當(dāng)攔截到這些事件我們就可以去做一些我們想做的事兒了~~~
AccessibilityService能做些什么呢? 比如自動化測試、自動搶紅包、自動安裝等等。在帶來便利的同時,也還是有需要注意的地方:當(dāng)你開啟了輔助功能之后會對你的隱私信息帶來一些風(fēng)險,所以還是需要謹慎的去開啟第三方的輔助功能。當(dāng)然這對于我們開發(fā)者而言都不是事,因為我們完全可以自己去開發(fā)屬于我們的輔助功能。

2.AccessibilityService的創(chuàng)建與配置

第一步就是自己去創(chuàng)建一個類繼承于AccessibilityService,并實現(xiàn)必須實現(xiàn)的兩個方法

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {

    }

    @Override
    public void onInterrupt() {

    }
}

隨后就是在res/xml目錄下新建一個xml文件(文件名隨意),后面會對這個文件作詳細的介紹。

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"  
    android:canRetrieveWindowContent="true"
    android:description="@string/my_accessibility_description"
    android:notificationTimeout="100"
    android:packageNames="com.tencent.mobileqq,com.android.packageinstaller" />

最后就是去配置清單文件了

 <service
            android:name=".service.MyAccessibilityService"
            android:enabled="true"
            android:exported="true"
            android:label="@string/app_name"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/my_services_config" />//在meta-data里申明配置信息
        </service>

別忘記了添加權(quán)限

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />

到這我們就可以把應(yīng)用跑到我們的手機上了,然后打開輔助功能,去開啟我們剛剛創(chuàng)建的這個輔助功能。如下圖:


當(dāng)然因為什么都沒做,開啟了也沒什么用,下面就去看看怎么使用吧!

3.怎么去使用AccessibilityService

先說說之前創(chuàng)建的那個xml文件中各個屬性的含義吧

  • <code>accessibilityEventTypes </code>: 用來設(shè)置響應(yīng)事件的類型,比如typeAllMask就是響應(yīng)全部事件,typeNotificationStateChanged就是響應(yīng)通知狀態(tài)的改變,如果需要響應(yīng)多種事件類型可以以 ‘ | ’ 隔開。
  • <code>accessibilityFeedbackType </code>: 給用戶的反饋方式,比如語音、震動等,這里用處不大。
  • <code>canRetrieveWindowContent</code> :是否可以獲取活動窗體的內(nèi)容,這個設(shè)置為true才可以取得窗體中的控件和事件源
  • <code>description</code> :輔助功能的描述
  • <code>notificationTimeout</code> :兩個相同類型事件發(fā)送到服務(wù)的事件間隔,單位毫秒
  • <code>packageNames</code> :指定響應(yīng)某個應(yīng)用的事件,取值為應(yīng)用的包名,多個以‘ , ’ 隔開。沒有此屬性則表示響應(yīng)全部應(yīng)用。這里我填寫的是手機qq和系統(tǒng)安裝器的包名

然后進入到我們的<code>MyAccessibilityService </code>中,定位到<code>onAccessibilityEvent</code>方法編寫如下代碼

log("-------------------------------------------------------------");
        int eventType = event.getEventType();//事件類型
        log("packageName:" + event.getPackageName() + "");//響應(yīng)事件的包名,也就是哪個應(yīng)用才響應(yīng)了這個事件
        log("source:" + event.getSource() + "");//事件源信息
        log("source class:" + event.getClassName() + "");//事件源的類名,比如android.widget.TextView
        log("event type(int):" + eventType + "");

        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知欄事件
                log("event type:TYPE_NOTIFICATION_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗體狀態(tài)改變
                log("event type:TYPE_WINDOW_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED://View獲取到焦點
                log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
                log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
                log("event type:TYPE_GESTURE_DETECTION_END");
                break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                log("event type:TYPE_WINDOW_CONTENT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                log("event type:TYPE_VIEW_CLICKED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                log("event type:TYPE_VIEW_TEXT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                log("event type:TYPE_VIEW_SCROLLED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
                log("event type:TYPE_VIEW_TEXT_SELECTION_CHANGED");
                break;
        }

        for (CharSequence txt : event.getText()) {
            log("text:" + txt);//輸出當(dāng)前事件包含的文本信息
        }

        log("-------------------------------------------------------------");

log方法就是打印信息用的,是對Log的一個簡單封裝。

在運行一次程序,打開我們的手機qq可以看見輸出日志如下

可以很清晰的看見當(dāng)我們打開qq或觸發(fā)很多的事件,第一個響應(yīng)的事件是<code>TYPE_WINDOW_STATE_CHANGED</code>,觸發(fā)此事件的事件源是<code>com.tencent.mobileqq.activity.SplashActivity</code>

接著,當(dāng)我點擊最上面的 ‘ 電話 ’,會得到如下日志:

我就不一一截圖操作的日志了,下來大家可以自行嘗試。到這里你用該對<code>onAccessibilityEvent</code>有了進一步的了解。

4.一步一步構(gòu)建一個apk自動安裝器

這一節(jié)來個實戰(zhàn)的應(yīng)用:做一個apk的自動安裝器,點擊apk文件即可開始自動安裝。

國內(nèi)的rom廠商大家都很清楚:百(sang)花(xin)齊(bing)放(kuang),已經(jīng)把原生的rom改的面目全非。所以想做一個面對全部手機的apk的自動安裝器還是比較麻煩的。我的手機是魅族的,下圖是魅族手機apk安裝的步驟(其他手機可能會略有不同,下來自己更改不同部分即可,這里主要講解原理):


可以看見點擊安裝包之后會彈出一個確認框,點擊繼續(xù)之后會出現(xiàn)下一步,點擊了下一步就可以點擊安裝了。我們要實現(xiàn)自動安裝無非就是用程序的方式會對相關(guān)按鈕的自動點擊,難點就在于怎么去找到這些按鈕并執(zhí)行點擊操作。

好了,下面我們可以來創(chuàng)建一個自動安裝的服務(wù)了,步驟和第一節(jié)描述的一樣將<code>packageNames</code>指定成<code>com.android.packageinstaller</code>就可以了。這里我們先別著急去實現(xiàn)功能(就算你想去實現(xiàn),也摸不著頭腦),我們還是像第二節(jié)那樣去輸出日志信息,查看日志輸出信息(對響應(yīng)事件信息的打?。┦鞘褂肁ccessibilityService的重點,看完日志的輸出信息,然后對其分析,才會知道具體的操作。
我們來看看在安裝apk文件時候輸出的部分日志信息:

從日志信息中可以看出:
1.當(dāng)我們點擊了apk文件的時候,如果該文件已經(jīng)存在,就會彈出一個對話框并且會響應(yīng)<code>TYPE_WINDOW_STATE_CHANGE</code>事件
2.當(dāng)我們點擊了繼續(xù)之后就會響應(yīng)<code>TYPE_VIEW_CLICKED</code>事件,并且繼續(xù)這個可以點擊的View是個Button
3.接著點擊下一步,同樣也會響應(yīng)<code>TYPE_VIEW_CLICKED</code>事件,并且繼續(xù)這個可以點擊的View是個TextView。最后就可以點擊安裝了(安裝那步忘了截圖,和第3步相應(yīng)的事件是一樣的)

經(jīng)過上面幾步的分析,我想你應(yīng)該對后面的邏輯還是比較清楚了,直接上代碼


public class AutoInstallService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        PrintUtils.printEvent(event);
        findAndPerformActionButton("繼續(xù)");
        findAndPerformActionTextView("下一步");
        findAndPerformActionTextView("安裝");
    }


    private void findAndPerformActionButton(String text) {
        if (getRootInActiveWindow() == null)//取得當(dāng)前激活窗體的根節(jié)點
            return;
        //通過文字找到當(dāng)前的節(jié)點
        List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
        for (int i = 0; i < nodes.size(); i++) {
            AccessibilityNodeInfo node = nodes.get(i);
            // 執(zhí)行點擊行為
            if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    private void findAndPerformActionTextView(String text) {
        if (getRootInActiveWindow() == null)
            return;
        //通過文字找到當(dāng)前的節(jié)點
        List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
        for (int i = 0; i < nodes.size(); i++) {
            AccessibilityNodeInfo node = nodes.get(i);
            // 執(zhí)行按鈕點擊行為
            if (node.getClassName().equals("android.widget.TextView") && node.isEnabled()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }
}

例子很簡單,所以也沒去對eventType做判斷,當(dāng)有其他需求的時候是需要對eventType做判斷了,代碼已經(jīng)注釋的比較詳細,我就不再對代碼作更多的講解了,有疑問可以留言。

到這里你應(yīng)該對AccessibilityService有了進一步的認識,會發(fā)現(xiàn)原來紅包助手之類軟件實現(xiàn)的原理就是利用AccessibilityService檢測通知欄的狀態(tài),然后再去做一些處理。是不是有點自己去做一個紅包助手的想法了?現(xiàn)在再去看看文章開始的那篇文章你的收獲會更大:Android微信自動回復(fù)功能

補充說明

AccessibilityService中還有幾個常用的方法 onServiceConnected、onInterrupt、onGesture??疵执笾乱仓朗裁磿r候會被調(diào)用。

在onServiceConnected中可以去配置AccessibilityService的一些信息,也就是之前在xml文件可以在這里通過代碼配置,不過這里沒法配置canRetrieveWindowContent屬性,剛開始也被這個坑了很久

@Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        PrintUtils.log("onServiceConnected");
//        //可用代碼配置當(dāng)前Service的信息
//        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
//        info.packageNames = new String[]{"com.android.packageinstaller", "com.tencent.mobileqq", "com.trs.gygdapp"}; //監(jiān)聽過濾的包名
//        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; //監(jiān)聽哪些行為
//        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; //反饋
//        info.notificationTimeout = 100; //通知的時間
//        setServiceInfo(info);
    }

官方文檔

本文源碼

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,828評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,261評論 6 342
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,105評論 4 61
  • 春秋流轉(zhuǎn) 流年共誰度? 茗一盞清茶 聽一耳風(fēng)雨 以思念下酒 以歲月入味 我在這里 等你帶一場夢來 浮世離歡 早已沉...
    弘慧閱讀 354評論 0 0

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