学习AccessibilityService实现微信抢红包插件
原文鏈接:http://blog.csdn.net/qq_30379689/article/details/53242953
在你的手機更多設置或者高級設置中,我們會發現有個無障礙的功能,很多人不知道這個功能具體是干嘛的,其實這個功能是為了增強用戶界面以幫助殘障人士,或者可能暫時無法與設備充分交互的人們
它的具體實現是通過AccessibilityService服務運行在后臺中,通過AccessibilityEvent接收指定事件的回調。這樣的事件表示用戶在界面中的一些狀態轉換,例如:焦點改變了,一個按鈕被點擊,等等。這樣的服務可以選擇請求活動窗口的內容的能力。簡單的說AccessibilityService就是一個后臺監控服務,當你監控的內容發生改變時,就會調用后臺服務的回調方法
AccessibilityService使用
1.1 創建服務類
編寫自己的Service類,重寫onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法
public class QHBAccessibilityService extends AccessibilityService {/*** 當啟動服務的時候就會被調用*/@Overrideprotected void onServiceConnected() {super.onServiceConnected();}/*** 監聽窗口變化的回調*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();//根據事件回調類型進行處理}/*** 中斷服務的回調*/@Overridepublic void onInterrupt() {} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
下面是對AccessibilityService中常用的方法的介紹
- disableSelf():禁用當前服務,也就是在服務可以通過該方法停止運行
- findFoucs(int falg):查找擁有特定焦點類型的控件
- getRootInActiveWindow():如果配置能夠獲取窗口內容,則會返回當前活動窗口的根結點
- getSeviceInfo():獲取當前服務的配置信息
- onAccessibilityEvent(AccessibilityEvent event):有關AccessibilityEvent事件的回調函數,系統通過sendAccessibiliyEvent()不斷的發送AccessibilityEvent到此處
- performGlobalAction(int action):執行全局操作,比如返回,回到主頁,打開最近等操作
- setServiceInfo(AccessibilityServiceInfo info):設置當前服務的配置信息
- getSystemService(String name):獲取系統服務
- onKeyEvent(KeyEvent event):如果允許服務監聽按鍵操作,該方法是按鍵事件的回調,需要注意,這個過程發生了系統處理按鍵事件之前
- onServiceConnected():系統成功綁定該服務時被觸發,也就是當你在設置中開啟相應的服務,系統成功的綁定了該服務時會觸發,通常我們可以在這里做一些初始化操作
- onInterrupt():服務中斷時的回調
1.2 聲明服務
既然是個后臺服務,那么就需要我們在manifests中配置該服務信息
<service android:name=".AccessibilityService.QHBAccessibilityService"android:enabled="true"android:exported="true"android:label="@string/label"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter> </service>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
我們必須注意:任何一個信息配置錯誤,都會使該服務無反應
- android:label:在無障礙列表中顯示該服務的名字?
- android:permission:需要指定BIND_ACCESSIBILITY_SERVICE權限,這是4.0以上的系統要求的
- intent-filter:這個name是固定不變的
1.3 配置服務參數
配置服務參數是指:配置用來接受指定類型的事件,監聽指定package,檢索窗口內容,獲取事件類型的時間等等。其配置服務參數有兩種方法:
- 方法一:安卓4.0之后可以通過meta-data標簽指定xml文件進行配置
- 方法二:通過代碼動態配置參數
1.3.1 方法一
在原先的manifests中增加meta-data標簽指定xml文件
<service android:name=".AccessibilityService.QHBAccessibilityService"android:enabled="true"android:exported="true"android:label="@string/label"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/accessibility_service_config" /> </service>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
接下來是accessibility_service_config文件的配置
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged"android:accessibilityFeedbackType="feedbackGeneric"android:accessibilityFlags="flagDefault"android:canRetrieveWindowContent="true"android:description="@string/description"android:notificationTimeout="100"android:packageNames="com.tencent.mm" />- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
下面是對xml參數的介紹
- accessibilityEventTypes:表示該服務對界面中的哪些變化感興趣,即哪些事件通知,比如窗口打開,滑動,焦點變化,長按等。具體的值可以在AccessibilityEvent類中查到,如typeAllMask表示接受所有的事件通知
- accessibilityFeedbackType:表示反饋方式,比如是語音播放,還是震動
- canRetrieveWindowContent:表示該服務能否訪問活動窗口中的內容。也就是如果你希望在服務中獲取窗體內容,則需要設置其值為true
- description:對該無障礙功能的描述,具體體現在下圖?
- notificationTimeout:接受事件的時間間隔,通常將其設置為100即可
- packageNames:表示對該服務是用來監聽哪個包的產生的事件,這里以微信的包名為例
1.3.2 方法二
通過代碼為我們的AccessibilityService配置AccessibilityServiceInfo信息,這里我們可以抽取成一個方法進行設置
private void settingAccessibilityInfo() {String[] packageNames = {"com.tencent.mm"};AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo();// 響應事件的類型,這里是全部的響應事件(長按,單擊,滑動等)mAccessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;// 反饋給用戶的類型,這里是語音提示mAccessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;// 過濾的包名mAccessibilityServiceInfo.packageNames = packageNames;setServiceInfo(mAccessibilityServiceInfo); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在這里涉及到了AccessibilityServiceInfo類,AccessibilityServiceInfo類被用于配置AccessibilityService信息,該類中包含了大量用于配置的常量字段及用來xml屬性,常見的有:accessibilityEventTypes,canRequestFilterKeyEvents,packageNames等等
1.4 啟動服務
這里我們需要在無障礙功能里面手動打開該項功能,否則無法繼續進行,通過下面代碼可以打開系統的無障礙功能列表。此處若不理解,也可以先跳過或先下載下來代碼對照理解。
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivity(intent);- 1
- 2
- 1
- 2
1.5 處理事件信息
由于我們監聽了事件的通知欄和界面等信息,當我們指定packageNames的通知欄或者界面發生變化時,會通過onAccessibilityEvent回調我們的事件,接著進行事件的處理
@Override public void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();//根據事件回調類型進行處理switch (eventType) {//當通知欄發生改變時case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//當窗口的狀態發生改變時case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:break;} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
當我們微信收到通知時,狀態欄會有一條推送信息到達,這個時候就會被TYPE_NOTIFICATION_STATE_CHANGED監聽,執行里面的內容,當我們切換微信界面時,或者使用微信時,這個時候就會被TYPE_WINDOW_STATE_CHANGED監聽,執行里面的內容
AccessibilityEvent的方法
- getEventType():事件類型
- getSource():獲取事件源對應的結點信息
- getClassName():獲取事件源對應類的類型,比如點擊事件是有某個Button產生的,那么此時獲取的就是Button的完整類名
- getText():獲取事件源的文本信息,比如事件是有TextView發出的,此時獲取的就是TextView的text屬性。如果該事件源是樹結構,那么此時獲取的是這個樹上所有具有text屬性的值的集合
- isEnabled():事件源(對應的界面控件)是否處在可用狀態
- getItemCount():如果事件源是樹結構,將返回該樹根節點下子節點的數量
1.6 獲取節點信息
獲取了界面窗口變化后,這個時候就要獲取控件的節點。整個窗口的節點本質是個樹結構,通過以下操作節點信息
1.6.1 獲取窗口節點(根節點)
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();- 1
- 1
1.6.2 獲取指定子節點(控件節點)
//通過文本找到對應的節點集合 List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text); //通過控件ID找到對應的節點集合,如com.tencent.mm:id/gd List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
1.7 模擬節點點擊
當我們獲取了節點信息之后,對控件節點進行模擬點擊、長按等操作,AccessibilityNodeInfo類提供了performAction()方法讓我們執行模擬操作,具體操作可看官方文檔介紹,這里列舉常用的操作
//模擬點擊 accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); //模擬長按 accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); //模擬獲取焦點 accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS); //模擬粘貼 accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
搶紅包插件實現
2.1 原理分析
2.2 注意事項
2.3 獲取控件ID
當我們手機接入USB線時,在Android?Device Monitor中的選擇設備并開啟Dump View Hierarchy for UI Automator工具,通過它可以獲取控件信息
http://www.cnblogs.com/xdindex/p/4619675.html
獲取”開”按鈕ID和返回按鈕ID
2.4 代碼實現
注意:這里使用的是微信最新6.3.30版本的控件ID,如果是其他版本的請自行適配
public class QHBAccessibilityService extends AccessibilityService {private List<AccessibilityNodeInfo> parents;/*** 當啟動服務的時候就會被調用*/@Overrideprotected void onServiceConnected() {super.onServiceConnected();parents = new ArrayList<>();}/*** 監聽窗口變化的回調*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();switch (eventType) {//當通知欄發生改變時case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:List<CharSequence> texts = event.getText();if (!texts.isEmpty()) {for (CharSequence text : texts) {String content = text.toString();if (content.contains("[微信紅包]")) {//模擬打開通知欄消息,即打開微信if (event.getParcelableData() != null &&event.getParcelableData() instanceof Notification) {Notification notification = (Notification) event.getParcelableData();PendingIntent pendingIntent = notification.contentIntent;try {pendingIntent.send();Log.e("demo","進入微信");} catch (Exception e) {e.printStackTrace();}}}}}break;//當窗口的狀態發生改變時case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:String className = event.getClassName().toString();if (className.equals("com.tencent.mm.ui.LauncherUI")) {//點擊最后一個紅包Log.e("demo","點擊紅包");getLastPacket();} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {//開紅包Log.e("demo","開紅包");inputClick("com.tencent.mm:id/bg7");} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {//退出紅包Log.e("demo","退出紅包");inputClick("com.tencent.mm:id/gd");}break;}}/*** 通過ID獲取控件,并進行模擬點擊* @param clickId*/@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)private void inputClick(String clickId) {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);for (AccessibilityNodeInfo item : list) {item.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}/*** 獲取List中最后一個紅包,并進行模擬點擊*/private void getLastPacket() {AccessibilityNodeInfo rootNode = getRootInActiveWindow();recycle(rootNode);if(parents.size()>0){parents.get(parents.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK);}}/*** 回歸函數遍歷每一個節點,并將含有"領取紅包"存進List中** @param info*/public void recycle(AccessibilityNodeInfo info) {if (info.getChildCount() == 0) {if (info.getText() != null) {if ("領取紅包".equals(info.getText().toString())) {if (info.isClickable()) {info.performAction(AccessibilityNodeInfo.ACTION_CLICK);}AccessibilityNodeInfo parent = info.getParent();while (parent != null) {if (parent.isClickable()) {parents.add(parent);break;}parent = parent.getParent();}}}} else {for (int i = 0; i < info.getChildCount(); i++) {if (info.getChild(i) != null) {recycle(info.getChild(i));}}}}/*** 中斷服務的回調*/@Overridepublic void onInterrupt() {} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
當收到紅包發送的時候,Log的打印信息
11-21 13:53:06.275 2909-2909/com.handsome.boke2 E/demo: 進入微信 11-21 13:53:06.921 2909-2909/com.handsome.boke2 E/demo: 點擊紅包 11-21 13:53:07.883 2909-2909/com.handsome.boke2 E/demo: 開紅包 11-21 13:53:08.732 2909-2909/com.handsome.boke2 E/demo: 退出紅包- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
你可能會想到做一些竊取信息的軟件,比如獲取QQ密碼、支付寶密碼等等,哈哈,凡是EditText中設置inputType為password類型的,都無法獲取其輸入值
2.5 源碼下載
源碼下載 或者https://github.com/gongmingming總結
以上是生活随笔為你收集整理的学习AccessibilityService实现微信抢红包插件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: replaceselection();j
- 下一篇: 偶然发现一个大佬写的 React 脚手架