之前寫(xiě)的微信搶紅包軟件效果還可以,反正過(guò)年家里的群里面的紅包永遠(yuǎn)是我第一個(gè)搶到;其實(shí)微信搶紅包還是比較簡(jiǎn)單的,稍微理一下邏輯也是比較容易實(shí)現(xiàn)的,基本上沒(méi)什么難點(diǎn)。但是想要實(shí)現(xiàn)自動(dòng)偷取螞蟻森林的能量還是有點(diǎn)難度的,這里分享一下實(shí)現(xiàn)思路。
要實(shí)現(xiàn)的功能
功能是這樣的,運(yùn)行輔助軟件后,自動(dòng)跳轉(zhuǎn)到支付寶頁(yè)面,然后我們手動(dòng)打開(kāi)螞蟻森林,手動(dòng)進(jìn)入好友總排名,然后輔助就開(kāi)始找到那些可以收取能量的好友,進(jìn)去收取能量,然后返回來(lái)繼續(xù)。其實(shí)是可以實(shí)現(xiàn)全自動(dòng)的,但是我實(shí)現(xiàn)以后覺(jué)得有點(diǎn)蠢,還是只實(shí)現(xiàn)偷好友能量這步比較好。

實(shí)現(xiàn)方法
螞蟻森林的界面都是一個(gè)標(biāo)題欄加一個(gè)網(wǎng)頁(yè),內(nèi)容都在網(wǎng)頁(yè)里面。單純的靠Android輔助功能是無(wú)法獲取里面控件,無(wú)法實(shí)現(xiàn)這個(gè)功能的;圖象識(shí)別配合模擬點(diǎn)擊是萬(wàn)能的;只要識(shí)別出頁(yè)面中這個(gè)可收取的能量的這個(gè)手的圖標(biāo)的位置,再通過(guò)模擬點(diǎn)擊,就可以進(jìn)入朋友的森林界面,再次通過(guò)圖象識(shí)別,識(shí)別出可以收取的能量的位置,就可以進(jìn)行能量的收取;
屏幕截圖
首先是獲取屏幕的截圖,獲取截圖可以使用Android的錄屏功能來(lái)實(shí)現(xiàn),在開(kāi)啟輔助前,開(kāi)啟錄屏功能,然后到這個(gè)界面的時(shí)候,通過(guò)當(dāng)前頁(yè)面的類(lèi)名和標(biāo)題是可以知道當(dāng)前處于總排行榜界面的,這時(shí)候獲取當(dāng)前界面的截屏,然后進(jìn)行圖象識(shí)別,獲取到第一個(gè)可收取能量的圖標(biāo)的位置,點(diǎn)擊進(jìn)入朋友的森林界面,進(jìn)行能量的收取,然后返回,返回的時(shí)候,重新截圖識(shí)別,找下一個(gè)可收取能量的圖標(biāo);當(dāng)識(shí)別不到可收取的圖標(biāo)的時(shí)候,模擬滑動(dòng)進(jìn)行翻頁(yè),然后繼續(xù)進(jìn)行識(shí)別。
//螞蟻森林界面的標(biāo)題控件
nodeInfo = findViewByID(ANT_FOREST_TITLE_ID);
String title = null;
if (nodeInfo != null && nodeInfo.getText() != null) {
title = nodeInfo.getText().toString();
}
//標(biāo)題存在,且在朋友列表
if (title != null && ANT_FOREST_TITLE.equals(title)) {
nodeInfo = findViewByID(ANT_FOREST_BACK_ID);
if (nodeInfo != null) {
//在更多好友界面,判斷界面是處于非滑動(dòng)狀態(tài)
if (!uiScrolling) {
//找到可以收取能量的按鈕進(jìn)行點(diǎn)擊
findEnergyItem();
return;
}
}
圖像識(shí)別
圖象識(shí)別的話,之前有使用過(guò)openCV,里面的模板匹配應(yīng)該比較符合這里的用法;這樣的圖象識(shí)別在做輔助軟件的時(shí)候特別容易用到,之前都是根據(jù)需要寫(xiě)的一些用顏色作為識(shí)別的算法,每次都要重新寫(xiě),比較操心,所以我自己想寫(xiě)一些通用的、簡(jiǎn)單的圖象識(shí)別方法,可以復(fù)用,類(lèi)似兩張圖片,找出小的圖片在大圖中的位置這種模板匹配;自己利用圖象上的點(diǎn)的顏色來(lái)進(jìn)行對(duì)比判斷寫(xiě)了個(gè)識(shí)別方法(如果兩張圖片每個(gè)點(diǎn)顏色都相同,那圖片肯定就一樣了呀),還行能用,識(shí)別一張圖片2960x1440的大概需要700毫秒左右;如下圖,識(shí)別出圖標(biāo)的位置,我使用半透明的黃色標(biāo)志了一下位置
/**
* 找有能量的item
*/
@RequiresApi(api = Build.VERSION_CODES.N)
private void findEnergyItem() {
//進(jìn)行截屏
Bitmap bitmap = ScreenRecordUtil.getInstance().getScreenShot();
//進(jìn)行圖象識(shí)別
List<Rect> rects = cross.matching(bitmap, template);
if (rects.size() > 0) {
Rect rect = rects.get(0);
//點(diǎn)擊識(shí)別出的坐標(biāo),進(jìn)入下一個(gè)界面,觸發(fā)下一個(gè)界面的方法進(jìn)行處理
clickOnScreen((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2, 10, null);
SystemClock.sleep(1000);
return;
}
//未找到按鈕
uiScrolling = true;
//進(jìn)行滑動(dòng)操作
performScrollDownward(ScreenUtil.SCREEN_HEIGHT - 100, ScreenUtil.SCREEN_HEIGHT / 2, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
//滑動(dòng)完成繼續(xù)識(shí)別
uiScrolling = false;
}
});
}

在好友的森林界面的偷能量,也是通過(guò)當(dāng)前頁(yè)面的類(lèi)名和標(biāo)題去判斷當(dāng)前界面處于好友的森林界面
if (nodeInfo != null) {
//在好友的森林里面
if (title != null && !title.equals(ANT_FOREST_TITLE) && title.contains(ANT_FOREST_TITLE)) {
//進(jìn)行偷能量操作
if (!isCollecting) {
getEnergy();
//返回上一頁(yè)
performBackClick();
SystemClock.sleep(WAIT_SLIPPING_TIME);
isCollecting = false;
}
}
}
顏色識(shí)別偷能量

偷能量的操作,也是圖象識(shí)別去實(shí)現(xiàn),看圖可以知道可收取的能量的顏色有些不同,寫(xiě)個(gè)簡(jiǎn)單的算法,通過(guò)顏色來(lái)判斷一個(gè)點(diǎn)是否有可以獲取的能量,然后模擬點(diǎn)擊進(jìn)行收取
/**
* 收取能量
*/
private void getEnergy() {
isCollecting = true;
//獲取屏幕截屏
Bitmap bitmap = ScreenRecordUtil.getInstance().getScreenShot();
int width = bitmap.getWidth();
int startPointY = (int) (bitmap.getHeight() * START_HEIGHT_TOP_SCALE);
int endPointY = (int) (bitmap.getHeight() * START_HEIGHT_BOTTOM_SCALE);
for (int y = startPointY; y < endPointY; y = y + 25) {
for (int x = 0; x < width; x = x + 10) {
int currentColor = bitmap.getPixel(x, y);
//顏色對(duì)比可收取能量
if (labColorLike.isLike(ENERGY_COLOR, currentColor, 10)) {
//點(diǎn)擊收取能量
clickOnScreen(x, y - 40, 10, null);
SystemClock.sleep(50);
}
}
}
}
總體效果還是不錯(cuò)的,收收能量沒(méi)什么問(wèn)題;其實(shí)也實(shí)現(xiàn)也比較簡(jiǎn)單,就是圖像識(shí)別加上模擬點(diǎn)擊。