快到年底了,又到了拼手速搶紅包的時候了贸辈;其實很早之前就做過搶紅包軟件了,包括QQ和微信肠槽;但是大家都懂的擎淤,自己一個月前寫的代碼現(xiàn)在看起來都像是一坨shit一樣;所以自己開始重新寫一個搶紅包的軟件(其實是因為實在是太簡單了)秸仙,只做微信嘴拢,因為QQ發(fā)紅包的確用的太少了,而且QQ紅包花樣也太多了寂纪,什么唱歌席吴、畫畫、成語接龍...
目標(biāo)
- 快捞蛋,天下武功無堅不摧孝冒、唯快不破,肯定要比人的手速快
- 準(zhǔn)拟杉,只要你手機解鎖了庄涡,在任意一個界面都可以快速搶到紅包
- 狠,其實狠不狠沒什么關(guān)系了搬设,最重要的是全自動穴店,自己不用任何操作撕捍,不然怎么解放雙手
- 穩(wěn),肯定要能一直搶紅包泣洞,來一個搶一個忧风,來兩個搶兩個,搶紅包一時爽球凰,一直搶一直爽狮腿;
手機配置要求
- Android系統(tǒng) 7.0及以上,輔助功能7.0以上支持模擬點擊弟蚀,模擬點擊不是必須的蚤霞,但是對于實現(xiàn)快很重要
- 手機不能太垃圾了,手機慢有外掛也發(fā)揮不出來呀
實現(xiàn)原理
實現(xiàn)方法就是利用Android輔助功能义钉,開啟輔助功能相當(dāng)于開啟了一個服務(wù)昧绣,在手機界面改變的時候,就能監(jiān)聽到該頁面的一些信息并且能拿到界面的一些控件捶闸,然后可以對控件進行模擬點擊夜畴,從而實現(xiàn)我們想要的功能。
除此以外删壮,不僅能夠?qū)Λ@取到的控件進行模擬點擊贪绘,在Android7.0及以上的版本,我們可以模擬任意位置的點擊包括觸摸央碟、滑動等等税灌,就是說我們可以實現(xiàn)任何人能夠進行的操作,這個是很有用的亿虽,可以做出很多有意思的東西菱涤,如果再配上截圖、錄屏和圖像識別洛勉,就更有意思了粘秆。
模擬點擊,就是說我們的手機界面自己動收毫,整個流程像是一只手在幫你操作一樣的攻走;其實我見過更牛逼的方法,連解鎖都不需要直接就領(lǐng)了紅包此再,界面沒有任何變化的昔搂;感覺上是通過通信,發(fā)數(shù)據(jù)給微信服務(wù)器實現(xiàn)的引润,當(dāng)然這種是需要root權(quán)限的巩趁,并且得去解析微信的通信協(xié)議,我自然沒時間去搞(其實有時間也不一定能搞出來)。
具體實現(xiàn)
輔助功能
首先是輔助功能议慰,新建一個Service繼承AccessibilityService
public class LuckMoneyService extends AccessibilityService
然后去AndroidManifest文件里面去注冊一下這個Service
<service
android:name=".service.LuckMoneyService"
android: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/accessible_service_wx_config" />
</service>
在meta-data
節(jié)點下有個resource
值蠢古,這是個xml文件,里面配置了該輔助的一些信息别凹,在res目錄下新建一個文件夾草讶,名字叫xml,然后新建一個xml文件炉菲,名字和resource
配置的一樣就行了
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeNotificationStateChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews|flagReportViewIds"
android:canRetrieveWindowContent="true"
android:canRequestFilterKeyEvents="true"
android:description="@string/wx_luck_money"
android:canRequestEnhancedWebAccessibility="true"
android:notificationTimeout="20"
android:packageNames="com.tencent.mm"
android:canPerformGestures="true" />
里面配置了一些參數(shù)堕战,比如notificationTimeout
是指定多少毫秒監(jiān)聽一次界面變化的,packageNames
是指定監(jiān)聽哪個應(yīng)用的拍霜,刪掉這個配置就是監(jiān)聽全局嘱丢,建議一定要刪除掉,我這里只是展示用祠饺,description
是對于該輔助的描述越驻,其他配置不管也罷。
然后在LuckMoneyService里面重寫一下onAccessibilityEvent方法
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//界面發(fā)生了變化
}
每當(dāng)界面改變的時候就會回調(diào)這個方法道偷,通過event
我們就可以獲取到界面的信息包括界面上的控件
簡單的用法
//獲取當(dāng)前界面包名
String packageName = event.getPackageName().toString();
//獲取當(dāng)前類名
String className = event.getClassName().toString();
//獲取當(dāng)前界面父布局的控件
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
//在父布局里面根據(jù)子控件**顯示的文字**找到該子控件
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
//在父布局里面根據(jù)子控件的**id**找到該子控件
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
//點擊該控件
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
上面的操作都比較基礎(chǔ)缀旁,根據(jù)控件顯示的文字查找控件,找出來的肯定是TextView和Button了勺鸦,根據(jù)ID查找控件并巍,ID就是指的寫布局文件的時候設(shè)置的控件的ID
模擬觸摸
模擬觸摸就是可以模擬人的觸摸動作,也比較簡單
protected void gestureOnScreen(Path path, long startTime, long duration,
AccessibilityService.GestureResultCallback callback) {
GestureDescription.Builder builde = new GestureDescription.Builder();
builde.addStroke(new GestureDescription.StrokeDescription(path, startTime, duration));
GestureDescription gestureDescription = builde.build();
dispatchGesture(gestureDescription, callback, null);
}
可以看到需要傳入path
就是一個路徑嘛换途,模擬滑動的路徑懊渡,用canvas畫過畫的都知道這東西還是比較簡單的,不清楚也沒關(guān)系军拟,繼續(xù)看距贷,startTime
就是多久后開始模擬事件,duration
就是該滑動的時間吻谋,其他回調(diào)什么的為空就可以了;
輔助功能能做的東西大概就上面這些了现横,接下來看看
微信應(yīng)用外的紅包處理
首先實現(xiàn)在微信界面外怎么搶紅包漓拾,在微信界面外有紅包出現(xiàn)必然會在通知欄會顯示微信紅包(如果沒開通知消息,那你自己開一下不就完事了嗎)戒祠,只需要在回調(diào)方法里面判斷一下是不是通知消息骇两,如果是通知消息,獲取里面的信息姜盈,判斷是不是微信紅包通知消息低千,是就點擊該消息,會自動跳轉(zhuǎn)到聊天界面;
因為我們是監(jiān)聽界面變化來實現(xiàn)功能的示血,所以在一個界面觸發(fā)了界面變化的時候棋傍,接下來的處理就應(yīng)該交給下一個界面的方法了,所以微信界面外的操作就是這些了
/**
* 紅包標(biāo)識字段
*/
public static final String HONG_BAO_TXT = "[微信紅包]";
//通知欄消息难审,判斷是不是紅包消息
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event.getParcelableData();
//獲取通知消息詳情
String content = notification.tickerText.toString();
//解析消息
String[] msg = content.split(":");
String text = msg[1].trim();
if (text.contains(HONG_BAO_TXT)) {
PendingIntent pendingIntent = notification.contentIntent;
try {
//點擊消息瘫拣,進入聊天界面
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
其中PendingIntent這個東西寫過通知欄的都知道,這個是設(shè)置跳轉(zhuǎn)到哪個界面的告喊,所以直接調(diào)用它的方法就完成了界面跳轉(zhuǎn)了
聊天界面的紅包處理
界面外的紅包點擊通知欄消息就來到了聊天界面麸拄,其實所有的界面都必須經(jīng)過這個界面才能領(lǐng)取到紅包,所以這個界面很重要黔姜;
實現(xiàn)
思路是這樣的拢切,聊天消息肯定是一個列表控件,其實是個ListView秆吵,而且肯定有控件ID淮椰,我們獲取到這個ListView,然后遍歷它的每個消息(只能遍歷到當(dāng)前界面顯示的)帮毁,判斷這個消息是不是微信紅包实苞,如果是,并且未被領(lǐng)取烈疚,而且這個紅包還得是別人發(fā)的黔牵,不是自己發(fā)的,我們才去點擊這個消息爷肝,觸發(fā)界面變化猾浦,然后丟給下一個界面處理;
//獲取聊天消息列表List控件
AccessibilityNodeInfo nodeInfo = findViewByID(DETAIL_CHAT_LIST_ID);
//這個消息列表不為空灯抛,那么肯定在聊天詳情頁
if (nodeInfo != null) {
//判斷有沒有未領(lǐng)取紅包并進行點擊
clickItem(nodeInfo);
return;
}
/**
* 進行消息列表未領(lǐng)取紅包的點擊
*
* @param nodeInfo
*/
private void clickItem(AccessibilityNodeInfo nodeInfo) {
//遍歷消息列表的每個消息
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
//獲取到子控件
AccessibilityNodeInfo nodeInfoChild = nodeInfo.getChild(i);
//獲取紅包控件
AccessibilityNodeInfo target = findViewByID(nodeInfoChild, AUM_ID);
//獲取頭像的控件
AccessibilityNodeInfo avatar = findViewByID(nodeInfoChild, AVATAR_ID);
boolean selfLuckMoney = false;
//獲取頭像的位置金赦,判斷紅包是否是自己發(fā)的,自己發(fā)的不搶
if (avatar != null) {
Rect rect = new Rect();
avatar.getBoundsInScreen(rect);
if (rect.left > screenWidth / 2) {
selfLuckMoney = true;
}
}
//如果不是自己發(fā)的紅包对嚼,并且獲取到的微信紅包這個控件不為空
if (target != null && !selfLuckMoney) {
//已領(lǐng)取這個控件為空夹抗,紅包還沒有被領(lǐng)取
if (findViewByID(nodeInfoChild, AUL_ID) == null) {
//點擊紅包控件
performViewClick(target);
return;
}
}
}
}
里面每個細節(jié)都注釋了,獲取ListView控件纵竖,獲取到了說明是在消息界面漠烧,獲取到消息列表的每一個控件,根據(jù) 是否是紅包消息靡砌,是否是別人發(fā)的已脓,是否是未領(lǐng)取的三點,去判斷是否是可以領(lǐng)取的紅包通殃,然后點擊可領(lǐng)取的紅包度液,到達彈出開的這個彈窗的界面;
monitor
如何獲取這個ListView控件的ID呢堕担,而我又是如何知道是ListView的呢已慢,可以通過一個工具來實現(xiàn)蛇受,就是在sdk工具下面的一個叫monitor的工具兢仰,其實之前的AndroidStudio是帶這個工具的,但是后來界面上是沒有了察蹲,但是其實還在的
/Users/Tyhj/Library/Android/sdk/tools/monitor
連上手機,打開這個工具,手機上打開你要查看的界面采驻,點擊工具手機的小手機的圖標(biāo)礼旅,就會截屏菲嘴,顯示出這個界面的信息
紅包彈窗界面處理
同樣的紅包彈窗這個界面也是必須經(jīng)過的,十分重要城豁;你要說這個彈窗界面也比較簡單,我們判斷一下是不是這個界面跟磨,然后點擊開不就完事兒了迎变;測試可以發(fā)現(xiàn),這個彈窗出現(xiàn)的時候,當(dāng)前的界面className是這個
**
* 紅包彈出的class的名字
*/
private static final String ACTIVITY_DIALOG_LUCKYMONEY = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI";
事情沒有這么容易笋熬,當(dāng)我去獲取這個開的這個控件的時候蔬捷,發(fā)現(xiàn)為空勾给,獲取不到,其實整個彈窗都獲取不到握截,遇到這個問題的人肯定不少畜眨;
//獲取開的控件布局
AccessibilityNodeInfo target = findViewByID("com.tencent.mm:id/dan");
其實深究下去恬汁,發(fā)現(xiàn)獲取根布局都為空了约巷,測試發(fā)現(xiàn)必須等待一段時間再去獲取這個彈窗才行,但是等多久呢贪婉,大概幾百毫秒吧,不定時的,不同手機也不一定军洼,那么隨便設(shè)一個就不行巩螃,因為你時間設(shè)置小了,程序可能會卡在這里搶不了紅包了匕争,肯定不行避乏;設(shè)置大了,行甘桑,但是影響速度呀拍皮。那么開個循環(huán)去獲取直到獲取到不為空行嗎?不行跑杭,奇怪的就是你一次去獲取為空了铆帽,之后獲取都為空了;只有等待一段時間后第一次去獲取才不為空德谅,這TMD就很奇怪了爹橱,看了一下的確沒法解決;這個問題其實和手機有關(guān)窄做,在三星s9上的確有問題愧驱,在華為nova5 pro上沒問題
//獲取根布局
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
如果獲取不到,那其實還有個辦法就是模擬點擊椭盏,在紅包彈窗彈出來的時候组砚,我瘋狂點擊這個開字的位置,就行了庸汗;開字的位置可以通過屏幕比例來計算出來惫确,這個就算不同的手機屏幕都可以點擊到這個開字;但是其實還是有個問題蚯舱,彈窗彈出其實有個動畫改化,不同的手機其實彈出的時間也不不一樣,華為nova5 pro都不用等待枉昏,可以直接執(zhí)行點擊操作陈肛,三星s9得等待200ms左右
//當(dāng)前為紅包彈出窗(那個開的那個彈窗)
if (className.equals(ACTIVITY_DIALOG_LUCKYMONEY)) {
//進行紅包開點擊
clickOpen();
return;
}
/**
* 點擊開紅包按鈕
*/
private void clickOpen() {
//等待紅包彈窗完成,直接使用模擬點擊比較快兄裂,根據(jù)手機性能等待響應(yīng)的時長
SystemClock.sleep(100);
for (int i = 0; i < 20; i++) {
SystemClock.sleep(10);
//計算了一下這個開字在屏幕中的位置句旱,按照屏幕比例計算
clickOnScreen(screenWidth / 2, screenHeight * POINT_Y_SCAL, 1, null);
}
/*AccessibilityNodeInfo target = findViewByID("com.tencent.mm:id/dan");
if (target != null) {
performViewClick(target);
return;
} else {
//如果沒有找到按鈕阳藻,再進行模擬點擊
for (int i = 0; i < 20; i++) {
SystemClock.sleep(10);
clickOnScreen(screenWidth / 2, screenHeight * POINT_Y_SCAL, 1, null);
}
}*/
}
點擊了這個開字后,進入了紅包詳情頁谈撒,進行下一步處理腥泥。
紅包詳情頁處理
進入了紅包詳情頁,紅包已經(jīng)到手了啃匿,想要繼續(xù)搶紅包蛔外,肯定需要退出去,這個簡單溯乒,有返回鍵的方法夹厌;這時候你可以返回聊天界面繼續(xù)搶這個群的紅包(如果專搶一個群的,這樣效率高)裆悄,也可以返回到最近消息列表(微信主頁面第一個界面)矛纹,可以搶其他群的紅包(搶其多個群的紅包,這樣效率高)光稼,也可以退回手機主界面(搶紅包效率低或南,因為還需要點擊通知欄消息進去);可以設(shè)置一下钟哥,如果開啟專搶一個群迎献,就退回該群聊天界面,否則退回最近消息列表界面腻贰。
//紅包領(lǐng)取后的詳情頁面吁恍,自動返回
if (className.equals(LUCKY_MONEY_DETAIL)) {
//返回聊天界面
performGlobalAction(GLOBAL_ACTION_BACK);
//如果不是專搶一個群
if (!isSingle) {
SystemClock.sleep(50);
performGlobalAction(GLOBAL_ACTION_BACK);
}
return;
}
最近消息列表界面處理
當(dāng)領(lǐng)完紅包后,退出到最近消息列表界面是比較好的選擇播演;這個界面上當(dāng)收到紅包消息通知欄是不會有提醒的冀瓦;我們需要根據(jù)界面的顯示去判斷有沒有紅包;其實也是特別簡單写烤,它也是一個ListView翼闽,同樣的遍歷一下每個item,判斷有沒有微信紅包消息洲炊,然后點擊進入聊天消息界面
//在最近聊天列表感局,檢測有沒有紅包消息出現(xiàn)
nodeInfo = findViewByID(HUMAN_LIST);
//聯(lián)系人列表
if (nodeInfo != null) {
//判斷最近聊天列表有沒有未領(lǐng)取紅包
clickHumanItem(nodeInfo);
return;
}
/**
* 進行聯(lián)系人列表的紅包消息點擊
*
* @param nodeInfo
*/
private void clickHumanItem(AccessibilityNodeInfo nodeInfo) {
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo nodeInfoChild = nodeInfo.getChild(i);
AccessibilityNodeInfo target = findViewByID(nodeInfoChild, HUMAN_LIST_TXT_ID);
if (target != null && target.getText() != null && target.getText().toString().contains(HONG_BAO_TXT)) {
performViewClick(target);
return;
}
}
}
看似沒有問題,實則有一個問題暂衡,就是在這個聊天列表里面询微,沒法判斷這個紅包是別人發(fā)的還是你自己發(fā)的,如果是你自己發(fā)的那肯定有問題的狂巢,這是一個坑撑毛,當(dāng)然可以通過保存一些數(shù)據(jù),比如說第一次進去后發(fā)現(xiàn)是自己發(fā)的紅包就退出來唧领,如果界面沒變化第二次就不再進行點擊了藻雌;但是其實問題也不大吧雌续,最多就是你發(fā)完紅包后自己再發(fā)個消息就可以避免了。
測試總結(jié)
其實到這里就全完成了胯杭,實際效果也不錯驯杜,測了一下,4個人和一個輔助比做个,發(fā)了20次紅包艇肴,輔助大概能搶到18次吧,并不是百分百搶到叁温,主要是人有準(zhǔn)備的話瘋狂點屏幕其實也挺快的(單身20年的同學(xué)的手速不得不服,畢竟有個地方我還是sleep了100毫秒的核畴,其實去掉應(yīng)該更快的)膝但,一般情況下輔助還是有絕對優(yōu)勢的。
一般情況下感覺用到的這些控件ID谤草、布局跟束、界面所在的類、包名什么的是不太會改變的丑孩,當(dāng)然如果微信版本升級比較大冀宴,估計布局什么的有變化,還得根據(jù)新的布局去重新實現(xiàn)温学,但是思路其實都是一樣的略贮。
更新
更新前搶一次紅包時間大概為1300毫秒左右,更新了彈窗等待那一步仗岖,不等待直接模擬點擊一次也是可以的(自己手機測試通過)逃延,更新后時間減少到1000毫秒左右;自己測試手速搶紅包轧拄,時間大概是1400毫秒以上揽祥,感覺真的比人手速快了
項目地址
里面有一些方法是封裝了的,方便調(diào)用檩电,具體實現(xiàn)可以看代碼
原文地址:Android微信搶紅包輔助
github地址:Android微信搶紅包輔助
軟件下載地址(老版本):https://github.com/tyhjh/LuckMoney/raw/master/%E6%8A%A2%E7%BA%A2%E5%8C%85.apk