蚂蚁森林自动收集能量(上)
紧接上期,这期主要解释这个项目中的一些小挫折以及如何解决。
蚂蚁森林自动收集能量这个项目并不大,喜欢玩它的人也不多,但是核心的一些实现方式很有推广意义。
如图所示
通过UI Automator Viewer工具我们能看到能量球的文本信息、类名、包名、以及其他属性包括在屏幕中的坐标bounds[左,右][上,下]
。
我们创建一个矩形储存这个bounds
Rect rect = new Rect();
BtnNode.getBoundsInScreen(rect);
//为了准确点击,我们模拟点击中心点
手势点击(rect.centerX(),rect.centerY());
但是这是针对已知的能量球按钮才能用啊
1.怎么判断按钮是不是可以点击的能量球呢
好的,让我们来分析一下这个view
结点
通过结点树,我们可以看到它是webView
下的某个子view
节点,这个view
结点中包含了能量球Button(1个),左方“雨水”Button,右方其中两个Button、下方四个Button共八个Button结点。当然这是我们自己蚂蚁森林的情况。在好友的界面中,按钮情况会变化,但是依然包含能量球Button。
在大量的样本中总结出,能量球Button中心点的坐标都在一个矩形范围内
//界定能量球的范围
private int RIGHT = 900;
private int LEFT = 150;
private int BOTTOM = 850;
private int TOP = 200;
对于那些不能再收取或者还没到收取时间的Button,依然在这个范围内,我们依然会点击它,但是不会影响什么。如果要更精确,可以利用后文的方法判断颜色,来筛选点击。我觉得多一两个也不会影响什么时间复杂度。
/**
* @param nowNode:webView节点
* role 收取能量
*/
private void getEnergy(AccessibilityNodeInfo nowNode ){
if(nowNode==null) return;
//切换到只包含能量球的View节点,加快处理速度
nowNode = nowNode.getChild(0).getChild(0).getChild(2);
int resultCount = 0;
// dfs(nowNode);
//获得Button节点的个数,因为该节点只包含Button
int buttonCount = nowNode.getChildCount();
debug("按钮数量:"+buttonCount);
//下标从1开始,第0个Button必然不是能量球Button
AccessibilityNodeInfo nowButtonNode;
Rect corr = new Rect();
for(int i=0;i<buttonCount;i++) {
//遍历 所有能量球
nowButtonNode = nowNode.getChild(i);
debug("该Button:" + nowButtonNode.getText().toString() + "_ 长度:" + nowButtonNode.getText().toString().length());
nowButtonNode.getBoundsInScreen(corr);
//如果在范围之内,就是按钮,就点。
if(checkInBound(corr.centerX(),corr.centerY())){
debug("能量球坐标" + "(" + corr.centerX() + "," + corr.centerY() + ")");
dispatchGestureView(1, corr.centerX(), corr.centerY());
debug("收集了能量");
sleep(500);
resultCount++;
}
}
debug("点击了"+resultCount+"个能量球");//点击的能量球个数
}
我们知道,可收取的好友在列表中很可能是间断出现的,好友比较多,我们如果每个好友都进去观察观察,那时间复杂度会退化到O(N),收取的周期就会很长。这肯定是不能使用户满意的。
怎么判断是否可收取
怎么判断颜色呢
我们需要一个接口,这个接口通过屏幕截图获得像素信息。
public class GBData {
static ImageReader reader;
private static Bitmap bitmap;
public static int getColor(int x, int y) {
if (reader == null) {
Log.d("", "getColor: reader is null");
return -1;
}
Image image = reader.acquireLatestImage();
if (image == null) {
if (bitmap == null) {
Log.d("", "getColor: image is null");
return -1;
}
return bitmap.getPixel(x, y);
}
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
}
bitmap.copyPixelsFromBuffer(buffer);
image.close();
return bitmap.getPixel(x, y);
}
}
调用很方便
int color = GBData.getColor(x坐标,y坐标);
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
根据坐标获得RGB值,如果是白色,就说明没有。用这个判断准确率非常高,但是要不停更新webView结点,因为滚动中,坐标都在变化。
怎么启用截图功能呢
网上有就不说了。
只是需要一点骚操作,先看效果
可以看到打开应用后就直接打开了。
1.配置Manifests文件
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
2.打开无障碍服务的代码
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "包名/类名");
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, "1");//1表示开启
3.通过ADB赋予权限
安装应用后,手机连上电脑,打开USB调试
打开ADB目录,shift+鼠标右键,打开powerShell
ADB下载
.\adb shell pm grant com.example.automayiforest android.permission.WRITE_SECURE_SETTINGS