Java实现安卓手机模拟操作


在这里插入图片描述

第一部分:安卓模拟操作基础

1.1 安卓输入系统概述

1.1.1 输入事件传递机制

安卓系统中的输入事件遵循以下传递路径:

  1. 硬件层:触摸屏、物理按键等硬件设备产生原始输入信号
  2. 内核层:Linux内核通过设备驱动接收原始输入事件
  3. 系统服务层:InputManagerService处理输入事件
  4. 应用层:事件通过View层级传递到具体应用
1.1.2 输入事件类型
  • 按键事件(KeyEvent):物理按键、键盘输入
  • 触摸事件(MotionEvent):触摸屏交互
  • 轨迹球事件(TrackballEvent):现已较少使用
  • 游戏控制器事件:手柄等外设输入

1.2 模拟操作的核心类

1.2.1 Instrumentation类
public class Instrumentation {
    
    
    // 发送按键事件
    public void sendKeySync(KeyEvent event) {
    
     ... }
    
    // 发送触摸事件
    public void sendPointerSync(MotionEvent event) {
    
     ... }
    
    // 发送字符串
    public void sendStringSync(String text) {
    
     ... }
}
1.2.2 KeyEvent类
public class KeyEvent extends InputEvent implements Parcelable {
    
    
    // 常用按键代码
    public static final int KEYCODE_HOME = 3;
    public static final int KEYCODE_BACK = 4;
    public static final int KEYCODE_DPAD_CENTER = 23;
    public static final int KEYCODE_ENTER = 66;
    
    // 构造方法
    public KeyEvent(long downTime, long eventTime, int action, 
                   int code, int repeat) {
    
     ... }
}
1.2.3 MotionEvent类
public abstract class MotionEvent extends InputEvent implements Parcelable {
    
    
    // 动作类型
    public static final int ACTION_DOWN = 0;
    public static final int ACTION_UP = 1;
    public static final int ACTION_MOVE = 2;
    
    // 获取坐标
    public final float getX() {
    
     ... }
    public final float getY() {
    
     ... }
    
    // 创建事件
    public static MotionEvent obtain(long downTime, long eventTime,
                                    int action, float x, float y, 
                                    int metaState) {
    
     ... }
}

1.3 权限要求

1.3.1 普通权限
<uses-permission android:name="android.permission.INTERNET" />
1.3.2 特殊权限
<!-- 注入事件权限 -->
<uses-permission android:name="android.permission.INJECT_EVENTS" 
                 tools:ignore="ProtectedPermissions" />

注意:INJECT_EVENTS是系统权限,普通应用无法获取,需要系统签名或root权限。

第二部分:基础模拟操作实现

2.1 按键模拟

2.1.1 单个按键操作
// 模拟返回键
public void simulateBackKey() {
    
    
    long downTime = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis();
    
    KeyEvent downEvent = new KeyEvent(downTime, eventTime, 
                                    KeyEvent.ACTION_DOWN, 
                                    KeyEvent.KEYCODE_BACK, 0);
    KeyEvent upEvent = new KeyEvent(downTime, eventTime, 
                                  KeyEvent.ACTION_UP, 
                                  KeyEvent.KEYCODE_BACK, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendKeySync(downEvent);
        inst.sendKeySync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}
2.1.2 组合按键操作
// 模拟Home键长按(打开最近任务)
public void simulateLongPressHome() {
    
    
    long downTime = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis() + 1000; // 长按1秒
    
    KeyEvent downEvent = new KeyEvent(downTime, downTime, 
                                   KeyEvent.ACTION_DOWN, 
                                   KeyEvent.KEYCODE_HOME, 0);
    KeyEvent upEvent = new KeyEvent(downTime, eventTime, 
                                 KeyEvent.ACTION_UP, 
                                 KeyEvent.KEYCODE_HOME, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendKeySync(downEvent);
        Thread.sleep(1000); // 保持按下状态
        inst.sendKeySync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}

2.2 触摸模拟

2.2.1 单点触摸
// 模拟在(x,y)位置的点击
public void simulateTap(int x, int y) {
    
    
    long downTime = SystemClock.uptimeMillis();
    long eventTime = SystemClock.uptimeMillis();
    
    // 按下动作
    MotionEvent downEvent = MotionEvent.obtain(downTime, eventTime, 
                                             MotionEvent.ACTION_DOWN, 
                                             x, y, 0);
    // 抬起动作
    MotionEvent upEvent = MotionEvent.obtain(downTime, eventTime + 100, 
                                           MotionEvent.ACTION_UP, 
                                           x, y, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendPointerSync(downEvent);
        inst.sendPointerSync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        downEvent.recycle();
        upEvent.recycle();
    }
}
2.2.2 滑动操作
// 模拟从(startX,startY)滑动到(endX,endY)
public void simulateSwipe(int startX, int startY, int endX, int endY) {
    
    
    long downTime = SystemClock.uptimeMillis();
    
    // 按下动作
    MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, 
                                            MotionEvent.ACTION_DOWN, 
                                            startX, startY, 0);
    // 移动动作
    MotionEvent moveEvent = MotionEvent.obtain(downTime, downTime + 50, 
                                             MotionEvent.ACTION_MOVE, 
                                             startX + (endX-startX)/2, 
                                             startY + (endY-startY)/2, 0);
    // 抬起动作
    MotionEvent upEvent = MotionEvent.obtain(downTime, downTime + 100, 
                                           MotionEvent.ACTION_UP, 
                                           endX, endY, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendPointerSync(downEvent);
        inst.sendPointerSync(moveEvent);
        inst.sendPointerSync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        downEvent.recycle();
        moveEvent.recycle();
        upEvent.recycle();
    }
}

2.3 文本输入模拟

2.3.1 单个字符输入
// 模拟输入单个字符
public void simulateCharInput(char c) {
    
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendCharacterSync(KeyEvent.KEYCODE_A); // 替换为对应字符的键码
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}
2.3.2 字符串输入
// 模拟输入字符串
public void simulateTextInput(String text) {
    
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendStringSync(text);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}

第三部分:高级模拟操作技术

3.1 多指触控模拟

3.1.1 MotionEvent多指支持

安卓通过指针索引(pointer index)和指针ID(pointer id)支持多点触控:

  • 每个触摸点有唯一的pointer id
  • pointer index是当前活动触摸点的索引
3.1.2 双指缩放实现
// 模拟双指缩放手势
public void simulatePinchZoom(int centerX, int centerY, float scaleFactor) {
    
    
    long downTime = SystemClock.uptimeMillis();
    int startDistance = 100; // 初始两指距离
    int endDistance = (int)(startDistance * scaleFactor);
    
    // 第一指按下
    MotionEvent downEvent1 = MotionEvent.obtain(downTime, downTime,
                                              MotionEvent.ACTION_DOWN,
                                              centerX - startDistance/2,
                                              centerY, 0);
    // 第二指按下
    MotionEvent downEvent2 = MotionEvent.obtain(downTime, downTime,
                                              MotionEvent.ACTION_POINTER_DOWN |
                                              (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
                                              centerX + startDistance/2,
                                              centerY, 0);
    
    // 移动过程
    List<MotionEvent> moveEvents = new ArrayList<>();
    for (int i = 1; i <= 5; i++) {
    
    
        float fraction = i / 5f;
        int currentDistance = (int)(startDistance + (endDistance - startDistance) * fraction);
        long eventTime = downTime + i * 50;
        
        MotionEvent moveEvent = MotionEvent.obtain(downTime, eventTime,
                                                 MotionEvent.ACTION_MOVE,
                                                 centerX - currentDistance/2,
                                                 centerY, 0);
        moveEvent.setLocation(centerX + currentDistance/2, centerY);
        moveEvents.add(moveEvent);
    }
    
    // 抬起第二指
    MotionEvent upEvent2 = MotionEvent.obtain(downTime, downTime + 300,
                                            MotionEvent.ACTION_POINTER_UP |
                                            (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
                                            centerX + endDistance/2,
                                            centerY, 0);
    // 抬起第一指
    MotionEvent upEvent1 = MotionEvent.obtain(downTime, downTime + 350,
                                            MotionEvent.ACTION_UP,
                                            centerX - endDistance/2,
                                            centerY, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendPointerSync(downEvent1);
        inst.sendPointerSync(downEvent2);
        
        for (MotionEvent event : moveEvents) {
    
    
            inst.sendPointerSync(event);
            event.recycle();
        }
        
        inst.sendPointerSync(upEvent2);
        inst.sendPointerSync(upEvent1);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        downEvent1.recycle();
        downEvent2.recycle();
        upEvent1.recycle();
        upEvent2.recycle();
    }
}

3.2 复杂手势模拟

3.2.1 画圆手势
// 模拟画圆手势
public void simulateCircleGesture(int centerX, int centerY, int radius) {
    
    
    long downTime = SystemClock.uptimeMillis();
    int steps = 36; // 圆的细分段数
    double angleStep = 2 * Math.PI / steps;
    
    // 按下动作
    MotionEvent downEvent = MotionEvent.obtain(downTime, downTime,
                                            MotionEvent.ACTION_DOWN,
                                            centerX + radius,
                                            centerY, 0);
    
    // 移动动作序列
    List<MotionEvent> moveEvents = new ArrayList<>();
    for (int i = 1; i <= steps; i++) {
    
    
        double angle = angleStep * i;
        int x = (int)(centerX + radius * Math.cos(angle));
        int y = (int)(centerY + radius * Math.sin(angle));
        long eventTime = downTime + i * 20;
        
        MotionEvent moveEvent = MotionEvent.obtain(downTime, eventTime,
                                                 MotionEvent.ACTION_MOVE,
                                                 x, y, 0);
        moveEvents.add(moveEvent);
    }
    
    // 抬起动作
    MotionEvent upEvent = MotionEvent.obtain(downTime, downTime + steps * 20,
                                           MotionEvent.ACTION_UP,
                                           centerX + radius,
                                           centerY, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendPointerSync(downEvent);
        
        for (MotionEvent event : moveEvents) {
    
    
            inst.sendPointerSync(event);
            event.recycle();
        }
        
        inst.sendPointerSync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        downEvent.recycle();
        upEvent.recycle();
    }
}

3.3 无障碍服务实现模拟操作

3.3.1 无障碍服务配置

AndroidManifest.xml配置:

<service android:name=".MyAccessibilityService"
         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>

res/xml/accessibility_service_config.xml:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagRequestFilterKeyEvents"
    android:canRequestFilterKeyEvents="true"
    android:canPerformGestures="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
3.3.2 手势分发实现
public class MyAccessibilityService extends AccessibilityService {
    
    
    
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    
    
        // 处理无障碍事件
    }
    
    @Override
    public void onInterrupt() {
    
    
        // 服务中断处理
    }
    
    // 分发手势
    public boolean dispatchGesture(int x, int y) {
    
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    
    
            Path path = new Path();
            path.moveTo(x, y);
            
            GestureDescription.Builder builder = new GestureDescription.Builder();
            builder.addStroke(new GestureDescription.StrokeDescription(
                path, 0, 50));
            
            return dispatchGesture(builder.build(), null, null);
        }
        return false;
    }
    
    // 更复杂的多指手势
    public boolean dispatchMultiFingerGesture(List<Point> points) {
    
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    
    
            GestureDescription.Builder builder = new GestureDescription.Builder();
            
            for (Point point : points) {
    
    
                Path path = new Path();
                path.moveTo(point.x, point.y);
                builder.addStroke(new GestureDescription.StrokeDescription(
                    path, 0, 100));
            }
            
            return dispatchGesture(builder.build(), null, null);
        }
        return false;
    }
}

第四部分:实用案例与应用场景

4.1 自动化测试应用

4.1.1 测试用例录制与回放
public class TestRecorder {
    
    
    private List<InputEvent> recordedEvents = new ArrayList<>();
    private boolean isRecording = false;
    
    public void startRecording() {
    
    
        recordedEvents.clear();
        isRecording = true;
    }
    
    public void stopRecording() {
    
    
        isRecording = false;
    }
    
    public void recordEvent(InputEvent event) {
    
    
        if (isRecording) {
    
    
            recordedEvents.add(event);
        }
    }
    
    public void replayEvents() {
    
    
        try {
    
    
            Instrumentation inst = new Instrumentation();
            for (InputEvent event : recordedEvents) {
    
    
                if (event instanceof KeyEvent) {
    
    
                    inst.sendKeySync((KeyEvent)event);
                } else if (event instanceof MotionEvent) {
    
    
                    inst.sendPointerSync((MotionEvent)event);
                }
                Thread.sleep(50); // 事件间延迟
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

4.2 游戏辅助工具

4.2.1 连点器实现
public class AutoClicker {
    
    
    private boolean isClicking = false;
    private int clickInterval = 500; // 毫秒
    private int targetX, targetY;
    private Thread clickingThread;
    
    public void startClicking(int x, int y) {
    
    
        targetX = x;
        targetY = y;
        isClicking = true;
        
        clickingThread = new Thread(() -> {
    
    
            while (isClicking) {
    
    
                simulateTap(targetX, targetY);
                try {
    
    
                    Thread.sleep(clickInterval);
                } catch (InterruptedException e) {
    
    
                    break;
                }
            }
        });
        clickingThread.start();
    }
    
    public void stopClicking() {
    
    
        isClicking = false;
        if (clickingThread != null) {
    
    
            clickingThread.interrupt();
        }
    }
    
    public void setClickInterval(int interval) {
    
    
        clickInterval = interval;
    }
}

4.3 远程控制实现

4.3.1 通过Socket接收指令
public class RemoteControlService extends Service {
    
    
    private static final int PORT = 12345;
    private ServerSocket serverSocket;
    private boolean isRunning = true;
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        new Thread(() -> {
    
    
            try {
    
    
                serverSocket = new ServerSocket(PORT);
                
                while (isRunning) {
    
    
                    Socket clientSocket = serverSocket.accept();
                    handleClient(clientSocket);
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }).start();
        
        return START_STICKY;
    }
    
    private void handleClient(Socket clientSocket) {
    
    
        try {
    
    
            BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()));
            
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
    
    
                processCommand(inputLine);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    
    private void processCommand(String command) {
    
    
        String[] parts = command.split(",");
        switch (parts[0]) {
    
    
            case "TAP":
                int x = Integer.parseInt(parts[1]);
                int y = Integer.parseInt(parts[2]);
                simulateTap(x, y);
                break;
            case "SWIPE":
                int startX = Integer.parseInt(parts[1]);
                int startY = Integer.parseInt(parts[2]);
                int endX = Integer.parseInt(parts[3]);
                int endY = Integer.parseInt(parts[4]);
                simulateSwipe(startX, startY, endX, endY);
                break;
            case "KEY":
                int keyCode = Integer.parseInt(parts[1]);
                simulateKey(keyCode);
                break;
        }
    }
    
    @Override
    public void onDestroy() {
    
    
        isRunning = false;
        try {
    
    
            if (serverSocket != null) {
    
    
                serverSocket.close();
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        super.onDestroy();
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    
    
        return null;
    }
}

第五部分:优化与进阶

5.1 性能优化

5.1.1 事件批量发送
public void sendEventsBulk(List<InputEvent> events) {
    
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        
        // 开始批量
        Method beginBatchMethod = Instrumentation.class.getDeclaredMethod("beginBatch");
        beginBatchMethod.setAccessible(true);
        beginBatchMethod.invoke(inst);
        
        // 发送事件
        for (InputEvent event : events) {
    
    
            if (event instanceof KeyEvent) {
    
    
                inst.sendKeySync((KeyEvent)event);
            } else if (event instanceof MotionEvent) {
    
    
                inst.sendPointerSync((MotionEvent)event);
            }
        }
        
        // 结束批量
        Method endBatchMethod = Instrumentation.class.getDeclaredMethod("endBatch");
        endBatchMethod.setAccessible(true);
        endBatchMethod.invoke(inst);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}
5.1.2 事件时间戳优化
// 更精确的事件时间戳控制
public void simulatePreciseTap(int x, int y, long duration) {
    
    
    long downTime = SystemClock.uptimeMillis();
    long upTime = downTime + duration;
    
    MotionEvent downEvent = MotionEvent.obtain(downTime, downTime,
                                             MotionEvent.ACTION_DOWN,
                                             x, y, 0);
    MotionEvent upEvent = MotionEvent.obtain(downTime, upTime,
                                           MotionEvent.ACTION_UP,
                                           x, y, 0);
    
    try {
    
    
        Instrumentation inst = new Instrumentation();
        inst.sendPointerSync(downEvent);
        Thread.sleep(duration);
        inst.sendPointerSync(upEvent);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        downEvent.recycle();
        upEvent.recycle();
    }
}

5.2 兼容性处理

5.2.1 不同安卓版本适配
public void simulateClickCompat(int x, int y) {
    
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    
    
        // 使用无障碍服务API
        if (myAccessibilityService != null) {
    
    
            myAccessibilityService.dispatchGesture(x, y);
        }
    } else {
    
    
        // 使用Instrumentation
        simulateTap(x, y);
    }
}
5.2.2 不同设备分辨率适配
public Point convertCoordinates(int x, int y) {
    
    
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    int screenWidth = metrics.widthPixels;
    int screenHeight = metrics.heightPixels;
    
    // 假设原始坐标是基于1080x1920设计的
    float scaleX = screenWidth / 1080f;
    float scaleY = screenHeight / 1920f;
    
    return new Point((int)(x * scaleX), (int)(y * scaleY));
}

5.3 安全与权限管理

5.3.1 动态权限申请
private static final int REQUEST_INJECT_PERMISSION = 1;

public void checkAndRequestPermissions(Activity activity) {
    
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
        if (!Settings.canDrawOverlays(activity)) {
    
    
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                                     Uri.parse("package:" + activity.getPackageName()));
            activity.startActivityForResult(intent, REQUEST_INJECT_PERMISSION);
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    
    
    if (requestCode == REQUEST_INJECT_PERMISSION) {
    
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
            if (Settings.canDrawOverlays(this)) {
    
    
                // 权限已授予
            } else {
    
    
                // 权限被拒绝
            }
        }
    }
}

结语

本文详细介绍了使用Java实现安卓手机模拟操作的各种技术和方法,从基础的单点触摸、按键模拟到复杂的多指手势、无障碍服务实现,涵盖了开发过程中可能遇到的多种场景和需求。

需要注意的是,模拟用户操作涉及到用户隐私和设备安全,在实际开发中应当:

  1. 明确告知用户应用将执行的操作
  2. 获取用户的明确授权
  3. 仅在必要的情况下使用这些技术
  4. 遵循Google Play和其他应用商店的相关政策

猜你喜欢

转载自blog.csdn.net/sixpp/article/details/147155876
今日推荐