Android Adb调试功能漫谈
2015-09-13 20:42
465 查看
人不能停止奔跑,但是当人跑累了就应该停下来看看之前走过的路。
写篇技术文章还得文艺一把,其实就想把前些日子学习到的积累一下。
做Android开发的朋友都知道,我们开发时离不开Adb(Android Debug Bridge),比如从本地上传Apk然后用一句
来安装apk,再来一句
来启动应用程序,其实这些命令都已经集成在IDE里面了。
将截图复制到电脑盘中
input text 往当前输入框里面输入文字
input keyevent 模拟输入(Home,返回,音量和各种字符的输入等等)
input tap 模拟手指在屏幕上的点击
input swipe 模拟手指在屏幕在的滑动
有上所有的输入都是针对全局的,不能指定对某个App进行输入,注意这个命令里面包含shell说明input是android系统里面的内置命令,经过测试input命令在各版本当中的差异很大,有的版没有tap操作,有的版本没有swipe操作,由于当时测试的时候没有记录的习惯,已经不记得细致的差异了
这个命令可以模拟出所以的按键事件,有一些Android上的按键脚本工具上会用到这套命令,录制和播放就是用的这两个命令,但是如果我为了做Android上的测试,只写脚本,不要录制怎么办(不同的机型和版本上面event的组合差别很大,而且输入设备也不一样)?
上面这些命令经常被用到Android的自动化测试里面
Created with Raphaël 2.1.0输入(点击等操作)获取输出(截屏)结果是否正确(图像分析)记录正确日志结束记录错误日志yesno
当然我们可以用强大的robotium,直接操作控件进行操作,这样当然更方便,我这边只是提供一种黑盒测试的思路,当我们没有程序源码时?当我们就想简单的操控Android设备时?
不能直接操作控件
没有分析截屏的库,只能简单的判断两图是否完全相等来判定结果
但是也有优点
不需要源码做任何配合
操作是针对Android设备的全局操作,不受App限制(可以直接调用系统电话程序拨打电话)
截屏是截取的framebuffer,用Adb得先形成文件再下载起来,对IO损耗太大
这两个缺点足以让非常多的人抛弃它了
但是里面有几个库隐藏的功能非常强大,本人当时分析过的这一套的jar包(chimpchat.jar, common.jar, ddmlib.jar, guava.jar, monkeyrunner.jar),它能做到对于Android设备全局的
字符串输入
模拟各种按键输入
手指点击模拟输入
手指滑动模拟输入(步数,滑动执行的时间可以自定义设置)
这不是和上面的adb shell input一样的功能吗?是一样的,但是monkey的功能在各版本中都是一样,非常稳定,而且也不用去考虑adb shell sendevent在各版本,每种硬件上的差异化了
图 1
图 2
蓝色框出来的代码不就是adb的常规功能,红色框出的代码不就是那几个模拟输入的函数?(更正:懒得重截图了,takeSnapshot是adb的常规功能,但是实现方式不一样,这里面是直接获取framebuffer)
不过这个包里面没有提供本地AdbServer的代码,只有AdbClient代码,也就是用这个包去调用adb功能必须先本地运行一个AdbServer。
图 3
再来看这一段,里面就是模拟输入的详细实现,keyDown和keyUp组合实现一次键盘输入,touch还分了up,down和move这样我们可以根据这几个函数定制很多自己的动作,上面的input tap和input swap能实现吗?哈哈,优势慢慢体现出来了。
图 4
从上图可以看出模拟输入函数和Adb的一些常用调试功能函数用的类不一样,也就是方式不一样,解释一下,上面有一个12399,其实本来是12345,我是为了测试改成12399了
图 5
上面这段代码展示出了如何创建模拟输入功能实例,其实是在Andorid侧执行了monkey命令把监听端映射出来,我们连接该端口和monkey通信实现的模拟输入的功能,Adb的功能不多解释,文章太多了,也是AdbServer连接到Android设备的Adbd(Daemon)进行通信实现的,两部分功能实现原理类似,只是monkey功能(我们后面把模拟输入功能叫monkey功能)需要临时启动程序,而非守护程序。
图 6
不同sdk版本引用的包可能会有一些不一样,这个真正使用时得自己慢慢调试
是不是有点意思了?哈哈,下一期讲些啥呢?用C++实现这些功能?啊?这也可以?我去
截屏
模拟按键操作
发送原始输入的事件
用另一种思路替换掉上面的命令
关键库chimpchat源码
关键函数
用java实现Adb的功能而且更强
引用包
函数调用
目录
写篇技术文章还得文艺一把,其实就想把前些日子学习到的积累一下。
做Android开发的朋友都知道,我们开发时离不开Adb(Android Debug Bridge),比如从本地上传Apk然后用一句
adb install /sdcard/..../abc.apk
来安装apk,再来一句
adb shell am start -n com.example.hello/com.example.hello.MainActivity
来启动应用程序,其实这些命令都已经集成在IDE里面了。
Adb除了这些功能之外还有其他功能吗?
1. 截屏
截图命令adb shell /system/bin/screencap -p /sdcard/screenshot.png
将截图复制到电脑盘中
adb pull /sdcard/screenshot.png D:\Test\
2. 模拟按键操作
adb shell input usage: input ... input text <string> input keyevent <key code number or name> input tap <x> <y> input swipe <x1> <y1> <x2> <y2>
input text 往当前输入框里面输入文字
input keyevent 模拟输入(Home,返回,音量和各种字符的输入等等)
input tap 模拟手指在屏幕上的点击
input swipe 模拟手指在屏幕在的滑动
有上所有的输入都是针对全局的,不能指定对某个App进行输入,注意这个命令里面包含shell说明input是android系统里面的内置命令,经过测试input命令在各版本当中的差异很大,有的版没有tap操作,有的版本没有swipe操作,由于当时测试的时候没有记录的习惯,已经不记得细致的差异了
3. 发送原始输入的事件
adb shell getevent adb shell sendevent
这个命令可以模拟出所以的按键事件,有一些Android上的按键脚本工具上会用到这套命令,录制和播放就是用的这两个命令,但是如果我为了做Android上的测试,只写脚本,不要录制怎么办(不同的机型和版本上面event的组合差别很大,而且输入设备也不一样)?
上面这些命令经常被用到Android的自动化测试里面
Created with Raphaël 2.1.0输入(点击等操作)获取输出(截屏)结果是否正确(图像分析)记录正确日志结束记录错误日志yesno
当然我们可以用强大的robotium,直接操作控件进行操作,这样当然更方便,我这边只是提供一种黑盒测试的思路,当我们没有程序源码时?当我们就想简单的操控Android设备时?
用另一种思路替换掉上面的命令
在Android测试里面还有另外一个工具叫Monkey Runner,用它的人非常少,因为非常不方便不能直接操作控件
没有分析截屏的库,只能简单的判断两图是否完全相等来判定结果
但是也有优点
不需要源码做任何配合
操作是针对Android设备的全局操作,不受App限制(可以直接调用系统电话程序拨打电话)
截屏是截取的framebuffer,用Adb得先形成文件再下载起来,对IO损耗太大
这两个缺点足以让非常多的人抛弃它了
但是里面有几个库隐藏的功能非常强大,本人当时分析过的这一套的jar包(chimpchat.jar, common.jar, ddmlib.jar, guava.jar, monkeyrunner.jar),它能做到对于Android设备全局的
字符串输入
模拟各种按键输入
手指点击模拟输入
手指滑动模拟输入(步数,滑动执行的时间可以自定义设置)
这不是和上面的adb shell input一样的功能吗?是一样的,但是monkey的功能在各版本中都是一样,非常稳定,而且也不用去考虑adb shell sendevent在各版本,每种硬件上的差异化了
关键库chimpchat源码
图 1
关键函数
图 2
蓝色框出来的代码不就是adb的常规功能,红色框出的代码不就是那几个模拟输入的函数?(更正:懒得重截图了,takeSnapshot是adb的常规功能,但是实现方式不一样,这里面是直接获取framebuffer)
不过这个包里面没有提供本地AdbServer的代码,只有AdbClient代码,也就是用这个包去调用adb功能必须先本地运行一个AdbServer。
图 3
再来看这一段,里面就是模拟输入的详细实现,keyDown和keyUp组合实现一次键盘输入,touch还分了up,down和move这样我们可以根据这几个函数定制很多自己的动作,上面的input tap和input swap能实现吗?哈哈,优势慢慢体现出来了。
图 4
从上图可以看出模拟输入函数和Adb的一些常用调试功能函数用的类不一样,也就是方式不一样,解释一下,上面有一个12399,其实本来是12345,我是为了测试改成12399了
private ChimpManager createManager(String address, int port) { System.out.println("ChimpManager createManager start"); try { //Step1. 创建一个端口转发,把本地PC的端口映射到Android设备的端口,而且值都是port device.createForward(port, port); System.out.println("createForward "+port+" OK"); } catch (TimeoutException e) { LOG.log(Level.SEVERE, "Timeout creating adb port forwarding", e); return null; } catch (AdbCommandRejectedException e) { LOG.log(Level.SEVERE, "Adb rejected adb port forwarding command: " + e.getMessage(), e); return null; } catch (IOException e) { LOG.log(Level.SEVERE, "Unable to create adb port forwarding: " + e.getMessage(), e); return null; } //Step2. 在Android端执行 monkey 命令,参数为 --port 后面根据数字形式的端口 String command = "monkey --port " + port; executeAsyncCommand(command, new LoggingOutputReceiver(LOG, Level.FINE)); // Sleep for a second to give the command time to execute. try { Thread.sleep(1000); } catch (InterruptedException e) { LOG.log(Level.SEVERE, "Unable to sleep", e); } InetAddress addr; try { addr = InetAddress.getByName(address); } catch (UnknownHostException e) { LOG.log(Level.SEVERE, "Unable to convert address into InetAddress: " + address, e); return null; } // We have a tough problem to solve here. "monkey" on the device gives us no indication // when it has started up and is ready to serve traffic. If you try too soon, commands // will fail. To remedy this, we will keep trying until a single command (in this case, // wake) succeeds. boolean success = false; ChimpManager mm = null; long start = System.currentTimeMillis(); while (!success) { long now = System.currentTimeMillis(); long diff = now - start; if (diff > MANAGER_CREATE_TIMEOUT_MS) { LOG.severe("Timeout while trying to create chimp mananger"); return null; } try { Thread.sleep(MANAGER_CREATE_WAIT_TIME_MS); } catch (InterruptedException e) { LOG.log(Level.SEVERE, "Unable to sleep", e); } Socket monkeySocket; try { //Step3. 创建到本地port端口的连接,还记得第一步吗?其实是连接到Android侧的port端口 monkeySocket = new Socket(addr, port); } catch (IOException e) { LOG.log(Level.FINE, "Unable to connect socket", e); success = false; continue; } try { //Step4. 保存连接实例 mm = new ChimpManager(monkeySocket); } catch (IOException e) { LOG.log(Level.SEVERE, "Unable to open writer and reader to socket"); continue; } try { //Step5. 唤醒设备 mm.wake(); } catch (IOException e) { LOG.log(Level.FINE, "Unable to wake up device", e); success = false; continue; } success = true; } System.out.println("ChimpManager createManager end"); return mm; }
图 5
上面这段代码展示出了如何创建模拟输入功能实例,其实是在Andorid侧执行了monkey命令把监听端映射出来,我们连接该端口和monkey通信实现的模拟输入的功能,Adb的功能不多解释,文章太多了,也是AdbServer连接到Android设备的Adbd(Daemon)进行通信实现的,两部分功能实现原理类似,只是monkey功能(我们后面把模拟输入功能叫monkey功能)需要临时启动程序,而非守护程序。
用java实现Adb的功能而且更强
分析下来,其实用MonkeyRunner的一套jar包就可以实现adb的大部分功能(没验证过是不是所有的功能)、Adb没实现的monkey功能和传原始framebuffer的截屏功能 now just do it引用包
这些包在 adt-bundle-windows-x86_64-‘Date’\sdk\tools\lib 下面可以找到图 6
不同sdk版本引用的包可能会有一些不一样,这个真正使用时得自己慢慢调试
函数调用
private AndroidDebugBridge bridge = null; private Init(){ ... //初始化ADB chimpchat = ChimpChat.getInstance(); //AndroidDebugBridge 是个单例模式,不要担心会创建多个实例 bridge = AndroidDebugBridge.getBridge(); try { while(!bridge.hasInitialDeviceList()){ Thread.sleep(100); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 等待设备连接 * @param deviceID 只连接特定的设备号用adb devices查看 * @return boolean 是否成功 */ public boolean connect(String deviceID){ boolean ret = false; try{ if(chimpchat == null){ ret = false; }else{ if(CommonUtils.isNullOrEmpty(deviceID)){ deviceID = ".*"; } IChimpDevice device = chimpchat.waitForConnection(1000, deviceID); if(device!=null){ devices.put(deviceID, device); ret = true; } } }catch(Exception err){ err.printStackTrace(); ret = false; } return ret; } //获取到device之后,怎么使用看图1就知道了
是不是有点意思了?哈哈,下一期讲些啥呢?用C++实现这些功能?啊?这也可以?我去
目录
Adb除了这些功能之外还有其他功能吗截屏
模拟按键操作
发送原始输入的事件
用另一种思路替换掉上面的命令
关键库chimpchat源码
关键函数
用java实现Adb的功能而且更强
引用包
函数调用
目录
相关文章推荐
- Android标题栏的各种设置
- Wiki_Android_dp和px之间的转换
- Android(java)学习笔记195:学生信息管理系统案例(SQLite + ListView)
- 安卓模拟器 手游最后一片未知蓝海
- 【Android开源项目解析】QQ“一键下班”功能实现解析——学习Path及贝塞尔曲线的基本使用
- Android StateListDrawable遇到的问题
- 使用RecyclerView实现带Header和Footer的GridView
- Android(java)学习笔记244:多媒体之SurfaceView
- Caused by: android.os.NetworkOnMainThreadException错误解决办法
- Android 异常记录
- Android实现FTP客户端服务端多图下载【两部android手机之间传输】
- android 自定义圆角imageview
- <Android Framework 之路>Android5.1 Camera Framework(四)——框架总结
- Android5.1 Camera Framework(四)——框架总结
- [转]Android x86模拟器Intel Atom x86 System Image配置与使用方法
- Android:30分钟弄明白Touch事件分发机制
- 破解android手机付费软件视频教程
- Android图片压缩工具类
- Android图片压缩工具类
- Android开发从零开始笔记