老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 4
这一部分的代码逻辑关系是这样的:
- 344行: 一个外部循环每次从上次保存下来的设备列表获得一个设备Device实例
- 350行: 再在一个内部循环从最新的设备列表中获得一个设备Device实例
- 353行:然后分别比较两个设备的序列号是否相等,相等则代表这个设备没有被移除。
- 357行: 如果设备没有被移除的话,那么判断这个设备是否是状态变化了?
- 358-373行: 变化了就需要把设备的状态改变过来
- 363-368行: 特别是在设备变成offline变成online状态后,需要去对该设备里面的可调试进程进行监控
- 372-373行: 并把该设备标识成还没有获取基本信息的状态,因为每个已经连接的online设备都应该保存有基本的build等信息
- 379行: 如果分析完一个设备发现只是状态变化了的话,最后把它从新设备列表中删除掉,因为已经分析过了,下面不需要再使用它了
- 384-388行: 如果设备已经被移除了的话(在新设备列表里面通过序列号找不到了), 那么需要把该设备移除出监控范围
以上代码是设备被移除和设备状态有更新时候的处理,那么新设备该怎么处理呢?毕竟一开始设备都是新的,这个才是关键点。
326 private void updateDevices(ArrayList<Device> newList) {
...
395 // at this point we should still have some new devices in newList, so we
396 // process them.
397 for (Device newDevice : newList) {
398 // add them to the list
399 mDevices.add(newDevice);
400 mServer.deviceConnected(newDevice);
401
402 // start monitoring them.
403 if (AndroidDebugBridge.getClientSupport()) {
404 if (newDevice.isOnline()) {
405 startMonitoringDevice(newDevice);
406 }
407 }
408
409 // look for their build info.
410 if (newDevice.isOnline()) {
411 devicesToQuery.add(newDevice);
412 }
413 }
414 }
415
416 // query the new devices for info.
417 for (Device d : devicesToQuery) {
418 queryNewDeviceForInfo(d);
419 }
420 }
421 newList.clear();
422 }
代码8-4-6 DeviceMonitor - updateDevices处理新增加设备
- 397行: 对所有剩余没有处理的新设备做一个循环
- 399行: 把该设备Device对象保留起来,下次有更新的时候需要用来跟新的设备列表做对比,正如代码5-4-5所做的事情
- 404-405行: 开始对新设备进行监控,其实说白了就是对新设备里面的每个可调式进程的vm建立一个客户端进行监控
- 418行: 获得新设备的基本信息,这个我们最后来分析
这里我们先重点看405行startMonitoringDevice:
509 private boolean startMonitoringDevice(Device device) {
510 SocketChannel socketChannel = openAdbConnection();
511
512 if (socketChannel != null) {
513 try {
514 boolean result = sendDeviceMonitoringRequest(
socketChannel, device);
515 if (result) {
516
517 if (mSelector == null) {
518 startDeviceMonitorThread();
519 }
520
521 device.setClientMonitoringSocket(socketChannel);
522
523 synchronized (mDevices) {
524 // always wakeup before doing the register. The synchronized block
525 // ensure that the selector won't select() before the end of this block.
526 // @see deviceClientMonitorLoop
527 mSelector.wakeup();
528
529 socketChannel.configureBlocking(false);
530 socketChannel.register(mSelector, SelectionKey.OP_READ, device);
531 }
532
533 return true;
534 }
535 }
... //省略错误处理代码
}
代码8-4-7 DeviceMonitor - startMonitoringDevice
514行首先给ADB服务器发送监听请求获得所有可调试的应用进程PID列表:
674 private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device)
675 throws TimeoutException, AdbCommandRejectedException, IOException {
676
677 try {
678 AdbHelper.setDevice(socket, device);
679
680 byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
681
682 AdbHelper.write(socket, request);
683
684 AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
685
686 if (!resp.okay) {
687 // request was refused by adb!
688 Log.e("DeviceMonitor", "adb refused request: " + resp.message);
689 }
690
691 return resp.okay;
692 }
...//省略错误处理代码
}
代码8-4-8 DeviceMonitor - sendDeviceMonitoringRequest
其实这段代码和上面的“代码8-4-5 DeviceMonitor - sendDeviceListMonitoringRequest”是类似的,只是发送是要在678行先把连接切换到目标监控设备(AdbHelper.setDevice方法将在下一章进行想尽描述)以及最后发送的命令变成是”track-jdwp”命令而已。最终这个命令其实等同于你在命令调用ADB命令行客户端发送命令”adb jdwp”,返回来的就是所有可调式应用进程的PID,请看以下输出结果示例,其与上图8-4-1中DDMS的Devices模块打印的进程PID是一致的:
图 8-4-2 adb jdwp 命令输出
获取到设备里面运行的可调试进程PID列表后,大家应该也可以想到下一步动作就是为每一个PID,也就是为每一个进程的vm虚拟机创建一个客户端线程来通过JDWP协议监控调试了,这也就是为什么DDMS能够动态获得每个进程的动态信息的原因了。
进程VM虚拟机监控代码分析到这里在本书中应该就算差不多了,如果再往下分析的话就需要去分析DDMS更多的知识以及JDWP协议相关的东西了,毕竟这不是我们这本书的重点,所以分析到这里让大家对DDMS工作原理有个基本认知就好了,再往下分析一大堆不相关代码就有走题和凑字数的嫌疑了。
这里我们根据上面承诺的,还是要看看“代码8-4-6 DeviceMonitor - updateDevices处理新增加设备”中481行对新增加的设备是如何通过调用“queryNewDeviceForInfo”这个方法来获取基本信息的,获取的又是什么信息:
442 private void queryNewDeviceForInfo(Device device) {
443 // TODO: do this in a separate thread.
444 try {
445 // first get the list of properties.
446 device.executeShellCommand(
“getprop”,
447 new GetPropReceiver(device));
448
449 queryNewDeviceForMountingPoint(device,
“EXTERNAL_STORAGE”);
450 queryNewDeviceForMountingPoint(device,
“ANDROID_DATA”);
451 queryNewDeviceForMountingPoint(device,
“ANDROID_ROOT”);
452
453 // now get the emulator Virtual Device name (if applicable).
454 if (device.isEmulator()) {
455 EmulatorConsole console = EmulatorConsole.getConsole(device);
456 if (console != null) {
457 device.setAvdName(console.getAvdName());
458 console.close();
459 }
460 }
461 }
...//省略错误处理部分代码
}
代码8-4-9 DeviceMonitor - queryNewDeviceForInfo
这个方法所做的事情就是:
- 446行: 首先通过Device类的executeShellCommand方法发送类似”adb shell getprop”的命令去获得所有支持的系统属性,这个方法最终调用的是AdbHelper类的executeShellCommand方法,它会接收一个专门用来对指定shell命令如getprop的返回值进行处理的接收类实例。AdbHelper的工作原理以及处理”adb shell getprop”返回结果的接收类GetPropReceiver我们在下一章”第7章 MonkeyDevice实现原理”中会进行详细阐述。
- 449-451行: 获得文件系统几个重要的挂载点,相信这也是给DDMS的File Explorer功能用的
获取完系统属性后,我们就要看下新设备的文件系统的那几个挂载点是怎么获得的了,我们进入到对应方法:
483 private void queryNewDeviceForMountingPoint(final Device device, final String name)
484 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException
485 {
486 device.executeShellCommand("echo $" + name,
new MultiLineReceiver()
487 {
488 public boolean isCancelled() {
489 return false;
490 }
491
492 public void processNewLines(String[] lines)
493 {
494 for (String line : lines) {
495 if (!line.isEmpty())
496 {
497 device.setMountingPoint(name, line);
498 }
499 }
500 }
501 });
502 }
代码8-4-10 DeviceMonitor - queryNewDeviceForMountingPoint
这个跟上面的发送getprop命令有类似的地方,只是命令换了”adb shell $name”和返回值处理类是重新实现的而已,但原理都一样。这里$name换成上面调用方法形参对应的”EXTERNAL_STORAGE”,”ANDROID_DATA”和“ANDROID_ROOT”就行了,以下就是本人通过命令行执行的效果:
图8-4-4 挂载点
最后把这个几个挂载点保存起来到Device实例的mMountpoints这个映射表里面:
67 private final Map<String, String> mMountPoints = new HashMap();
...
783 void setMountingPoint(String name, String value) {
784 this.mMountPoints.put(name, value);
785 }
代码8-4-11 Device - setMountingPoint
老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 4的更多相关文章
- 老李推荐:第5章7节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles
老李推荐:第5章7节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles poptest是国内唯一一家培养测试开 ...
- 老李推荐:第5章6节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 初始化事件源
老李推荐:第5章6节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 初始化事件源 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试 ...
- 老李推荐:第5章3节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 启动脚本
老李推荐:第5章3节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 启动脚本 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性 ...
- 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用
老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionP ...
- 老李推荐:第5章2节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 启动流程概览
老李推荐:第5章2节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 启动流程概览 每个应用都会有一个入口方法来供操作系统调用执行,Monkey这个应用的入口方法就 ...
- 老李推荐:第5章1节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 官方简介
老李推荐:第5章1节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 官方简介 在MonkeyRunner的框架中,Monkey是作为一个服务来接受来自Monkey ...
- 第5章1节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 官方简介(原创)
天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文“寻求合作伙伴编写<深入理解 MonkeyRunner>书籍“.但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在 ...
- 老李推荐:第14章9节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-遍历控件树查找控件
老李推荐:第14章9节<MonkeyRunner源码剖析> HierarchyViewer实现原理-遍历控件树查找控件 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员 ...
- 老李推荐:第14章5节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态
老李推荐:第14章5节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态 poptest是国内唯一 ...
- 老李推荐:第14章6节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-启动ViewServer
老李推荐:第14章6节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-启动ViewServer poptest是国内唯一一家培养 ...
随机推荐
- OC中extern,static,const的用法
1.const的作用: const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p). 例如 NSString *const SIAlertViewWillDismissNotification; ...
- fetch使用的常见问题及解决办法
首先声明一下,本文不是要讲解fetch的具体用法,不清楚的可以参考MDN fetch教程. 引言 说道fetch就不得不提XMLHttpRequest了,XHR在发送web请求时需要开发者配置相关请求 ...
- Google addword 策略
https://support.google.com/adwords/answer/1704392?hl=zh-Hans&authuser=0 顾客会用自己的思路去解决生意上的问题,想要拥有自 ...
- 30分钟掌握 C#6
1. 只读自动属性(Read-only auto-properties) C# 6之前我们构建只读自动属性: public string FirstName { get; private set; } ...
- C++:基础篇-32位和64位系统区别及字节数
今儿面试了一个刚刚毕业的,但是不知道一个int.long.double这几个都是多少位,我给你们总结一下哈: 常用数据类型对应字节数 可用如sizeof(char),sizeof(char*)等得出 ...
- Eclipse的Spring IDE插件的安装和使用
Spring IDE是Spring官方网站推荐的Eclipse插件,可提供在研发Spring时对Bean定义文件进行验证并以可视化的方式查看各个Bean之间的依赖关系等. 安装 使用Eclipse M ...
- 走进 Redis 的世界
NoSQL(Not Only SQL) 在现今已经应用非常普遍了,尤其是 Redis 和 MongoDB.我们现在来说说 Redis. 前世 Redis 是一个意大利人 Salvatore Sanfi ...
- enum类型的本质(转)
原地址:http://www.cppblog.com/chemz/archive/2007/06/05/25578.html 至从C语言开始enum类型就被作为用户自定义分类有限集合常量的方法被引入到 ...
- WeMall商城系统的Android app商城中的wemall-mobile代码
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改. [适合研究学习,支持wemall3.x版本] 1.快 ...
- 算法模板——sap网络最大流 3(递归+邻接矩阵)
实现功能:同之前 可以看见的是这次的程序优美了许多,代码简短了一倍还多,可是速度却是和原来的邻接表一个级别的(在Codevs上面草地排水那题的运行时间比较,但是显然数据很大时应该比那个慢些),原理差不 ...