android Instrumentation模拟相关触摸事件

1,496 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

使用背景

近日接触远程控制项目,当控制端发送相关的点击、滑动等事件时,被控制端需要接收相关点位数据,然后去实现相应的事件。

前期使用adb shell命令的方式进行实现相关的事件,但是无奈这种方式响应的延迟比较大,通常被控制端接收到服务器命令之后响应到屏幕上的事件会延迟2/3秒左右,而且不同的事件响应的时间也会有所不同,所以这种方式最终还是处于能接受,但是希望继续优化,同时这种方式需要root权限才能执行,否则只能控制自己的程序页面

找到替代了方法,使用Instrumentation进行模拟相关的事件,这个类通过查找资料,是属于安卓的测试框架,可以进行控制和测试应用。显而易见我们可以通过它来实现命令的下发控制,通过测试发现基本没有延迟,操作起来丝滑流畅,这种方式需要程序系统签名才能执行,否则只能控制自己的程序页面。

原文更新

为了更加方便的调用,封装了相关事件的方法,整合成了依赖库,请到github查看详细依赖对接文档github-InstrumentationUtil,如果对您有帮助还请给个star。

开始使用

Instrumentation不能在主线程中运行,需要运行在子线程。针对这个类的详细使用方法文档,可查阅官方文档进行了解,鉴于自己对此类了解也是不足,就不啰嗦啦。

点击事件

直接传入需要点击坐标即可

/**
 * 点击事件
 *
 * @param x 点位 x 轴坐标
 * @param y 点位 y 轴坐标
 */
private void sendEvent(final float x, final float y) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Instrumentation inst = new Instrumentation();
                inst.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0));
                inst.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0));
            } catch (Exception ex) {
                ex.printStackTrace();
                LogUtils.e("点击事件Error:"+ex.toString());
            }
        }
    }).start();
}

长按事件

这里是通过加了一个延迟实现的长按事件,可能此方法有所不妥,但是从使用上还未发现问题。

/**
 * 长按事件
 *
 * @param x1 点位 x 轴坐标
 * @param y1 点位 y 轴坐标
 */
private void sendLongTouch(final float x1, final float y1) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Instrumentation iso = new Instrumentation();
            float x = x1;
            float y = y1;
            try {
                iso.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0));
                Thread.sleep(800);
                iso.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0));
                iso.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0));
            } catch (InterruptedException e) {
                e.printStackTrace();
                LogUtils.e("长按事件Error:"+e.toString());
            }

        }
    }).start();
}

发送keyEvent事件

这里的num就是KeyEvent代表的值,和adb中的是一致。

/**
 * 发送普通 keyEvent
 *
 * @param num
 */
private void sendKeyEvent(final int num) {
    new Thread() {
        public void run() {
            try {
                Instrumentation inst = new Instrumentation();
                inst.sendKeyDownUpSync(num);
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.e("发送普通 keyEvent:"+e.toString());
            }
        }
    }.start();
}

发送文本事件

这里的发送文本要比adb shell方式好用的多,adb shell方式对于一些特殊的例如:双引号等一些特殊符号,要做处理,而这种方式可以直接发送。

/**
 * 发送字符串
 *
 * @param text
 */
private void sendKeyEventText(final String text) {
    new Thread() {
        public void run() {
            try {
                Instrumentation inst = new Instrumentation();
                inst.sendStringSync(text);
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.e("发送字符串error :"+e.toString());
            }
        }
    }.start();
}

发送滑动事件

滑动事件处理起来相对的麻烦,不同于adb shell直接传入开始、结束点位的坐标即可。不过可以通过计算拿到结束点位的坐标与开始点位的坐标进行计算判断是上下左右哪个方向的移动事件。然后再去算出x或者y移动的差值,之后可以通过MotionEvent.ACTION_MOVE事件,同时增加eventTime事件,和对应的x/y移动的距离,可大致进行实现滑动效果。

结束语

在远程控制中的控制部分,通常需要特定的权限才能执行,需要系统rom给提供开放相关权限的支持!文章如有不足之处,还请指正。如果对您有帮助还请给此库一个star,谢谢~。 github地址