Stetho的通信原理

Stetho简介

stetho是Facebook推出的安卓APP网络诊断和数据监控的工具,接入方便,功能强大,是Android开发者必备的友好工具。 主要功能包括:

  • 实时查看App的布局
  • 网络请求抓包
  • 数据库、SharedPreferences文件内容监控
  • 自定义dumpapp插件
  • 对于JavaScript的支持

具体的使用方法可以看这篇文章。 本文主要想讲一下自定义dumpapp插件的通信原理。

dumpapp插件示例

在主机上给设备发送一个files tree命令,得到如下结果:

$ ./dumpapp files tree
+---lib
+---cache
|   +---com.android.opengl.shaders_cache
+---files

在app中对应这样一段java代码,来处理files tree命令。

  private void doTree(PrintStream writer) throws DumpUsageException {
    File baseDir = getBaseDir(mContext);
    printDirectoryVisual(baseDir, 0, writer);
  }

问题是,为什么在主机上执行一段脚本(dumpapp.py)后会让设备上的app执行相应的处理程序呢?

一般PushService可以完成类似的功能,后台下发一条指令,客户端完成指定的动作。对于Stetho这样的Android调试工具来说,显然不需要使用后台,用ADB就可以实现。


ADB通信的原理

ADB的结构是一个client-server的结构,包含3个部分:

  • Client : 发送命令。客户端在PC主机上运行,在shell里使用Adb命令的时候就会开启一个client。
  • Daemon : 在设备上执行命令。守护进程在设备上后台运行。(aabd运行在Andriod设备的底层)
  • Server : 管理客户端(client)和守护进程(daemon)的连接。server在PC主机上后台运行。

smartsocket

android提供了smartsocket,详见这里

— smartsockets ——————————————————- Port 5037 is used for smart sockets which allow a client on the host side to request access to a service in the host adb daemon or in the remote (device) daemon. The service is requested by ascii name, preceeded by a 4 digit hex length. Upon successful connection an “OKAY” response is sent, otherwise a “FAIL” message is returned. Once connected the client is talking to that (remote or local) service. client: server: “OKAY” client: server: “FAIL”

总结来说,就是可以给adb-server发送一条指令<service-name>,然后adb-server会转发给adbd,让adbd来执行<service-name>. 举例来说,当我们执行adb shell cat /proc/net/unix,最终就是通过adbd在设备上执行的。

Stetho的通信模型如下:

其中stetho-server就是app启的一个Thread用来accept客户端的connect。


程序流程

可以通过在关键位置打上断点的方式来看程序的流程。 Python可以用Pycharm来打断点。 如图配置一个debug版本,这样就可以以./dumapp -l的方式debug了。

Android app当然是用Android Studio打断点了。

dumpapp.py流程分析

详见代码(dumpapp.py)

例子1: adb.select_service('shell:cat /proc/net/unix')

通过这个命令其实是在找到指定的Unix域套接字。

/proc/net/unix文件下可以看到所有的unix域套接字,Path字段前面有@符号的表示它是一个ABSTRACT类型的socket,如果是绝对路径则表示是FILESYSTEM类型的。

例子2: 发起一个connect到Unix域套接字的请求 adb.select_service('localabstract:%s' % (socket_name))

这里的python用到的几个service协议应该是android提供的smartsocket本身就支持的,在与adb的端口号连接后就能使用socket来发送service的名字给android设备了。 详见这里. 如下的命令就可以直接跟stetho-server连接。

stetho-server流程分析

详见代码LocalSocketServer.java

这里创建ServerSocket时的address格式是stetho_+进程名+_ devtools_remote


Unix域套接字

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket(Unix域协议)。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

Unix域协议所用的API就是在不同主机上执行客户/服务通信所用的套接字API。

Android中的Unix域套接字

在Android API中,有几个类对Unix域套接字(也叫localsocket)进行了封装,不仅可以用来应用程序之间进行IPC通信,还可以跨应用程序层和Linux层运行的程序进行通信。 LocalSocket在Unix域名空间创建一个套接字(非服务端)。 LocalSocketImpl是Framework层Socket的实现,通过JNI调用系统socket API。 LocalServerSocket创建服务器端Unix域套接字,与LocalSocket对应。

创建socket时指定的domain类型是AF_UNIX。

/**
 * Creates a socket in the underlying OS.
 */
public void create (int sockType) throws IOException {
    //...
    fd = Os.socket(OsConstants.AF_UNIX, osType, 0);    
}

通过搜索发现LocalSocketImpl的native实现是在libandroid_runtime.so中。

比如listen的native实现就是调用了socket的listen函数。

/* private native void listen_native(int fd, int backlog) throws IOException; */
static void
socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog)
{
    int ret,fd;
    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    ret = listen(fd, backlog);
    if (ret < 0) {
        jniThrowIOException(env, errno);
        return;
    }
}

参考

ADB原理,Wi-Fi连接,常用命令及拓展 《UNIX网络编程卷1》 Android LocalSocket与Socket 区别 如何给安卓APP安装听诊器,检查数据问题

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

GlusterFS分布式文件系统使用简介

术语简介 GlusterFS是一个开源的分布式文件系统。更多特性介绍附录的参考文档。 Brick:GFS中的存储单元,通过是一个受信存储池中的服务器的一个导出...

73960
来自专栏散尽浮华

MySQL高可用架构-MHA环境部署记录

一、MHA介绍 MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshim...

77660
来自专栏老安的博客

openstack虚拟机内文件遭破坏的急救方案

    openstack虚拟机存放于ceph存储,由于用户将系统的grub误删除,导致系统无法正常引导。现在用户要求抢救文件。

9610
来自专栏互扯程序

Linux常用Shell脚本,值得学习及收藏

在运维中,尤其是linux运维,都知道脚本的重要性,脚本会让我们的 运维事半功倍,所以学会写脚本是我们每个linux运维必须学会的一门功课,这里收藏linux运...

28210
来自专栏容器云生态

Docker1.12尝试

前言:在docker1.12中默认增加了swarm mode 编排功能,并且官方支持更多的插件来进行docker的网路和外接存储插件,不过目前测试swarm m...

494100
来自专栏白驹过隙

Socket编程回顾,一个最简单服务器程序

23530
来自专栏Java编程技术

UML建模(组件图)

组件图是为了展示组元(components),组元提供的接口(provided inerfaces)和需要调用的接口(required interfaces),...

90520
来自专栏Java架构沉思录

Linux常用Shell脚本知多少

在运维中,尤其是linux运维,都知道脚本的重要性,脚本会让我们的 运维事半功倍,所以学会写脚本是我们每个linux运维必须学会的一门功课,这里收藏linux运...

18210
来自专栏晨星先生的自留地

日志攻防初探之windows篇(iis日志介绍)

47260
来自专栏北京马哥教育

DNS从入门到管理(一)

DNS概述 DNS(Domain Name System,域名系统),域名和IP地址相互映射的一个分布式数据库,通过主机名,最终得到该主机名对应的IP地址的过程...

69360

扫码关注云+社区

领取腾讯云代金券