Stetho 调试桥,查看数据库和网络的 Chrome 插件

Stetho 调试桥,查看数据库和网络的 Chrome 插件

Stetho 调试桥,查看数据库和网络的 Chrome 插件


Stetho 调试桥,查看数据库和网络的 Chrome 插件


Stetho 是 Facebook 在 2015 年开源的一个 Android 调试工具,核心思路是把 Chrome DevTools 的远程调试协议(Chrome DevTools Protocol,CDP)嫁接到 Android 应用上。这意味着你可以在 PC 端的 Chrome 浏览器里,像调试网页一样去 inspect 一个原生 Android 应用的内部状态——数据库、SharedPreferences、网络请求,甚至是 View 层级。这个工具在 2015-2018 年那段时间的 Android 开发圈子里相当流行,尤其是在调试网络请求和数据库内容这两件事上,它几乎是当时最顺手的方案。现在 2024 年回头看,Stetho 已经停止维护,Facebook 把它归档了,但理解它的设计思路和实际局限,对于今天选择调试工具仍然有参考价值。


为什么 Stetho 的设计思路很巧妙


Android 原生开发长期以来有个痛点:调试工具链和 Web 前端相比显得笨重。你想看一个 App 里的 SQLite 数据库内容,常规做法是把数据库文件 pull 出来,用 SQLite Browser 或者命令行 sqlite3 打开。如果数据有加密,或者应用运行在内部存储空间,这个 pull 操作本身就要先 root 设备或者找 debug 版本的 backup 接口。网络请求更麻烦,早期没有 OkHttp 的 logging interceptor 的时候,很多人是在代码里手动打 log,或者用 Charles/Fiddler 这类外部抓包工具,配置 HTTPS 证书信任又要折腾半天。


Stetho 的解法很直接:Android 设备上跑一个本地的 WebSocket 服务器,Chrome 通过 chrome://inspect 的页面发现这个服务,然后建立连接。连接建立之后,Stetho 在 App 进程里收集各种运行时信息,通过 WebSocket 把数据序列化发给 Chrome,Chrome 那边用现成的 DevTools 前端渲染。这个架构里,Stetho 不需要自己写 UI,Chrome DevTools 的 UI 是白嫖的;也不需要发明新的协议,CDP 是现成的。Facebook 只需要做两件事:在 Android 端实现 CDP 的一个子集,以及把 Android 特有的数据结构(Cursor、SharedPreferences、OkHttp 的 Request/Response)映射到 CDP 对应的 domain 上。


具体到集成方式,Stetho 的接入非常轻。在 Application 的 onCreate 里加一行:


Stetho.initializeWithDefaults(this);

如果你需要网络监控,再给 OkHttp 配一个 StethoInterceptor:


new OkHttpClient.Builder()
    .addNetworkInterceptor(new StethoInterceptor())
    .build();

就这么两步。initializeWithDefaults 会启动那个 WebSocket 服务,同时注册默认的 dumpapp 插件和 inspect 模块。StethoInterceptor 是 OkHttp 的 network interceptor,能拿到完整的请求和响应数据,包括请求头、响应头、body 内容。Stetho 不支持 Retrofit 直接集成,但因为 Retrofit 底层也是 OkHttp,所以 interceptor 的加法和纯 OkHttp 场景完全一样。


Chrome 端的操作也很自然。手机连上 USB,打开 Chrome 浏览器,地址栏输入 chrome://inspect,你会看到设备列表,下面列出了所有支持远程调试的目标。Stetho 的 inspect 入口会显示为 "xxx (powered by Stetho)",点击 inspect 就弹出一个熟悉的 DevTools 窗口。这个窗口和调试网页时看到的几乎一模一样,左边是 Elements/Resources/Network/Console 几个 tab,Resources 下面能看到 Web SQL、Local Storage——对 Stetho 来说,这些节点被重定向到了 Android 的 SQLite 数据库和 SharedPreferences。


数据库 inspect 的实际体验


Stetho 的数据库查看功能是我当年用得最多的场景。它会把应用 databases 目录下的所有 .db 文件列出来,点开之后显示表结构,支持执行 SQL 查询。这个体验和用 Chrome 调试 Web SQL 数据库几乎一致,左侧是数据库和表列表,右侧是数据网格视图,可以点列头排序,也可以直接写 SQL。


但实际用下来有几个细节限制。第一,Stetho 只支持 SQLite,如果你用了 Room 或者其他 ORM,最终落盘的还是 SQLite,所以不影响,但如果是 Realm 这类非 SQLite 的嵌入式数据库,Stetho 就无能为力了。第二,数据库文件必须位于应用私有的 databases 目录,或者你通过 DatabaseFilesProvider 显式告诉 Stetho 其他路径。有些应用会把数据库放在外部存储或者自定义路径,这时候需要额外配置。第三,也是最容易踩坑的一点:Stetho 的数据库视图是只读的。你可以执行 SELECT,但不能直接通过 UI 修改数据。有些开发者第一次用的时候以为能像调试 Web 应用的 LocalStorage 那样双击单元格改值,结果发现改不了,这其实是因为 CDP 的 Database domain 本身没有定义 UPDATE/DELETE 的 UI 操作映射,Stetho 也没有自己扩展。


另外,数据库连接在 inspect 期间是被 Stetho 持有的。如果你的应用代码里同时有写操作,可能会遇到 SQLite 的并发锁定问题。Android 的 SQLiteDatabase 默认是串行化的,但 Stetho 的 inspect 连接走的是另一个线程,虽然 Stetho 内部用了 WAL 模式相关的兼容处理,但在某些旧版本上,长时间开着 inspect 窗口然后应用频繁读写数据库,偶尔会遇到 database is locked 的异常。这个不是必现,但我在 2016 年左右的一个项目里确实碰到过,最后是在 release 构建里彻底关掉 Stetho 解决的。


网络监控的实现与局限


Stetho 的网络监控依赖 OkHttp 的 interceptor 机制。StethoInterceptor 实现了 Interceptor 接口,在 intercept 方法里把 Request 和 Response 的详细信息通过 NetworkEventReporter 接口上报。这个上报过程是同步的,但数据通过 WebSocket 推送到 Chrome 是异步的,所以不会阻塞请求本身。


在 Chrome DevTools 的 Network tab 里,你能看到完整的请求瀑布流,包括 URL、Method、Status、Type、Size、Time 这些列。点进单个请求,有 Headers、Preview、Response、Timing 几个子 tab。Headers 里分 Request Headers 和 Response Headers,Preview 和 Response 会尝试根据 Content-Type 做格式化,比如 JSON 会高亮,图片会显示缩略图。


这个体验在 2015 年是非常惊艳的,但有几个硬边界。第一,它只支持 OkHttp,不支持 HttpURLConnection 或者 Volley 这类旧网络栈。当年 OkHttp 已经在普及了,但老项目迁移不是一蹴而就的。第二,请求 body 的捕获有大小限制。Stetho 默认会截断过大的 body,这个阈值在源码里写死为 100KB 左右(具体版本有浮动,我记得 1.5.0 附近是 102400 bytes)。如果你的 API 返回大量数据,在 DevTools 里看到的是截断后的内容,底部会提示 "(truncated)"。这个限制可以通过自定义 StethoInterceptor 的构造参数调整,但文档里没怎么提,很多人不知道。第三,也是更隐蔽的问题:Stetho 抓到的请求是经过 interceptor 链处理后的最终请求,如果你的应用里有多个 interceptor,比如先做签名、再做加密,Stetho 看到的是签名和加密之后的数据,而不是业务代码里构造的原始请求。这在调试的时候容易误导人,你以为请求参数不对,实际上是被前面的 interceptor 改过了。


HTTPS 的处理倒是比较省心。因为 Stetho 是在应用进程内部拦截的,拿到的是解密后的明文数据,不需要像 Charles 那样配置证书信任或者做 MITM。这是内部 interceptor 相对于外部抓包工具的一个显著优势,尤其在调试 SSL Pinning 或者证书校验逻辑的时候,Stetho 可以看到应用实际发出的请求内容,而 Charles 可能已经被 SSL Pinning 拦在外面了。


dumpapp 和命令行扩展


除了 Chrome DevTools 的图形界面,Stetho 还提供了一个叫 dumpapp 的命令行工具。这个工具通过 adb forward 建立本地 socket 连接,可以直接向应用发送命令。Stetho 默认带了一些基础命令,比如 prefs 操作 SharedPreferences,files 查看内部文件,db 执行数据库操作。


dumpapp 的扩展机制是插件化的。你可以实现 DumperPlugin 接口,注册自己的命令。比如我当时写过一个插件用来清理特定缓存目录,或者触发某个后台任务的立即执行。这个在自动化测试或者 QA 反复验证某个场景的时候挺有用,不用改代码重新编译,命令行敲一条指令就能改变应用状态。


不过 dumpapp 的实际使用频率远低于 Chrome inspect。一方面是因为命令行工具的学习成本,另一方面是 Stetho 的文档对 dumpapp 的介绍非常简略,官方 README 里大部分篇幅都在讲 Chrome 集成,dumpapp 只是提了一句 "see the dumpapp/ directory"。想要自定义插件,得去读源码里的示例,对于不想深入 Stetho 内部的人来说门槛有点高。


停止维护之后的替代方案


Stetho 的最后一个正式版本是 1.5.1,发布于 2018 年 3 月。Facebook 在 2021 年左右把仓库归档,issue 和 PR 都不再处理。停止维护的原因没有官方说明,但推测和几个因素有关:Facebook 自己的 Android 技术栈在转向更内部的方案;Jetpack 逐渐提供了官方替代;以及 Chrome DevTools Protocol 本身在不断演进,Stetho 实现的 CDP 版本落后,维护成本越来越高。


今天如果你需要类似的功能,有几个选择。


Flipper 是 Facebook 在 Stetho 之后推出的下一代调试工具,地址是 https://github.com/facebook/flipper。它继承了 Stetho 的基本架构——本地服务 + 桌面端 UI,但桌面端不是 Chrome,而是基于 Electron 的独立应用。Flipper 的插件生态比 Stetho 丰富很多,数据库、网络、SharedPreferences、性能监控都有官方插件,而且支持 iOS 和 React Native。缺点是桌面端安装包体积大,启动慢,而且有些插件的稳定性不如 Stetho 时代那么可靠。Flipper 现在是活跃维护的,但配置复杂度明显高于 Stetho,小项目可能觉得过重。


Android Studio 的 Database Inspector 是官方方案,从 Android Studio 4.1 开始提供。它不需要应用端集成任何库,只要跑在 API 26+ 的设备上,AS 直接通过 Android Debug Bridge 读取数据库文件。体验上比 Stetho 更原生,支持实时刷新、查询编辑,而且因为是官方工具,和 Room 的集成更好,可以直接看到 DAO 方法的调用。缺点是必须连着 Android Studio,不能像 Chrome 那样独立窗口,而且旧设备不支持。


Chucker(https://github.com/ChuckerTeam/chucker)是一个专门做网络请求监控的库,和 Stetho 的 Network 功能对标。它不在 Chrome 里展示,而是在应用内部弹出一个 Notification,点击后进入一个内置的 Activity 列表展示请求历史。优点是完全没有桌面端依赖,QA 测试的时候特别方便,缺点是 UI 比较简陋,而且只能看网络,不能看数据库。


App Inspection 是 Android Studio 内置的调试面板,整合了 Database Inspector、Network Inspector、Background Task Inspector 等。Network Inspector 从 Android Studio 4.0 开始支持,能抓 HTTP/HTTPS 请求,但和 Stetho 一样只支持 OkHttp(通过其内置的 interceptor 机制)。App Inspection 的优势是零集成成本(现代 Android Studio 自带),劣势是功能相对基础,而且和 AS 绑定。


集成 Stetho 的具体坑


虽然 Stetho 停止维护了,但有些老项目还在用,或者你想快速搭一个轻量调试环境,这里列几个实际集成时容易踩的坑。


R8/ProGuard 混淆规则。Stetho 用到了反射,尤其是 dumpapp 的插件发现和 CDP 的 JSON 序列化部分。如果 release 构建开了混淆,Stetho 的初始化可能会失败,或者 inspect 窗口打开后空白。官方没有提供 consumer ProGuard 规则,需要自己加:


-keep class com.facebook.stetho.** { *; }
-dontwarn com.facebook.stetho.**

更好的做法是在 release 构建里彻底排除 Stetho,不要只是 debugImplementation,而是在代码里用 BuildConfig 条件编译:


if (BuildConfig.DEBUG) {
    Stetho.initializeWithDefaults(this);
}

WebSocket 端口冲突。Stetho 默认的 inspect 服务端口是 9229,和 Node.js 的调试端口一样。如果你在开发机器上同时跑 Node 应用和 Stetho,可能会遇到 chrome://inspect 里出现混乱的目标列表,或者连接不上。可以通过 Stetho.initialize 的自定义配置改端口,但文档里没明说,得看 Stetho.InitializerBuilder 的源码。


多进程应用。Stetho 的 WebSocket 服务是在调用 initialize 的那个进程里启动的。如果你的应用有多个进程(比如 WebView 的独立进程、或者 Service 跑在另一个进程),其他进程里的数据库和网络请求不会被自动监控。需要在每个进程里都初始化 Stetho,但这样 chrome://inspect 里会显示多个 inspect 目标,容易搞混。


OkHttp 版本兼容性。Stetho 1.5.1 依赖的 OkHttp 版本比较老。如果你的项目用了 OkHttp 4.x,StethoInterceptor 的某些内部 API 可能不兼容。实际测试下来,基础的请求/响应监控在 OkHttp 4.9 之前一般能工作,但 4.10 之后 OkHttp 改了一些 internal 包的可见性,Stetho 的反射调用可能会失败。Flipper 在这方面处理得更好,因为它维护得更频繁。


Android 10+ 的分区存储。Stetho 的数据库文件发现逻辑基于传统的文件路径遍历。Android 10 引入的分区存储(Scoped Storage)限制了应用对外部存储的访问,但 Stetho 主要操作的是内部 databases 目录,所以直接影响不大。不过如果你之前通过自定义 DatabaseFilesProvider 把数据库放在外部存储路径,Android 10+ 上可能会遇到权限问题。


个人使用经历中的具体场景


2017 年我参与一个电商 App 的重构,服务端 API 正在从 REST 向 GraphQL 迁移。迁移期间两套接口并存,前端需要根据用户灰度状态动态切换。调试的时候最烦的是确认实际发出去的请求到底是 REST 还是 GraphQL,以及 GraphQL 的 query 文本在客户端拼接得对不对。Stetho 的 Network tab 在这里帮了大忙,因为可以看到完整的 URL 和 POST body,不像 logcat 里被截断或者换行打乱格式。


但有个坑是 GraphQL 的响应通常是一个大的 JSON,里面嵌套着 dataerrors,Stetho 的 Preview tab 对 JSON 的折叠支持不如 Chrome 调试网页时那么好,尤其是深层嵌套的时候,点击展开/折叠的响应区域很小,容易误操作。而且前面提到的 100KB 截断限制在 GraphQL 场景下更容易触发,因为单个响应里可能包含大量商品数据。


另一个场景是调试 Room 数据库的迁移。我们有一个版本升级需要把旧表的数据做字段拆分,写 Migration 的时候不确定 ALTER TABLEINSERT INTO ... SELECT 的执行顺序对不对。Stetho 的数据库 inspect 让我能在迁移代码打断点的同时,实时查看表结构变化,确认新表是否按预期创建。但这里又遇到那个只读限制——我想手动插入一条测试数据验证查询逻辑,结果只能写死在代码里重新编译,或者通过 sqlite3 命令行操作,Stetho 的 UI 本身帮不上忙。


安全层面的考量


Stetho 的调试端口在应用启动后就一直监听,任何能通过 adb 连接到设备的人都可以 chrome://inspect 进去看数据。这在开发机上没问题,但如果测试包或者内部预览包流出去,理论上拿到手机的人都能 inspect。Stetho 本身没有认证机制,依赖的是 Android 的 USB 调试授权(RSA 指纹确认),但一旦授权过,后续连接是自动的。


更隐蔽的风险是,Stetho 的 WebSocket 服务绑定在 localhost,但通过 adb forward 可以映射到开发机的端口。这意味着如果开发机本身有恶意软件,或者处于不安全的网络环境,可能存在本地端口被扫描利用的风险。虽然这个攻击面很小,但 Facebook 内部后来推 Flipper 的时候,Flipper 的通信机制做了更多的安全加固,比如证书校验和可选的 TLS。


所以我的做法是:Stetho 只在 debug 构建类型里存在,而且 applicationIdSuffix ".debug" 确保不会和 release 包冲突。CI 打包的测试包如果是 debug 类型,会明确告知 QA 这个包带有调试端口,不要在非安全环境安装。


源码层面的有趣细节


Stetho 的源码值得读一读,尤其是它怎么把 Android 的对象模型映射到 CDP 上。com.facebook.stetho.inspector.elements 包里实现了一套 DOM 树的抽象,Android 的 View 层级被表示为 CDP 的 DOM domain 节点。这就是为什么在 Chrome DevTools 的 Elements tab 里,你能看到类似 HTML 的层级结构,每个 View 对应一个 "元素",有 class 属性(对应 Java 类名)、id 属性(对应 android:id)、以及 measured width/height 等布局信息。


这个映射不是完美的。Android 的 View 系统和 Web 的 DOM 模型有本质差异,比如 Android 的 View 没有 "innerHTML" 的概念,Stetho 只能暴露有限的属性和方法。而且 Elements tab 的实时刷新有性能问题,复杂布局的页面(比如 RecyclerView 嵌套)在 inspect 时可能会卡顿,因为 Stetho 需要遍历整个 View 树序列化数据。


网络部分的映射在 com.facebook.stetho.inspector.network 包里。NetworkEventReporter 接口定义了 requestWillBeSentresponseHeadersReceivedinterpretResponseStream 等回调,和 Chrome DevTools 的 Network domain 事件一一对应。Stetho 在这里做了一件比较巧妙的事:响应 body 的流式处理。它不会等整个响应下载完再上报,而是通过 ResponseHandler 接口边下载边推送,这样大文件的下载进度也能在 Network tab 的 Timing 里看到瀑布流。


今天的价值:为什么还在谈 Stetho


Flipper 功能更全面,官方工具更零成本,那为什么要花时间回顾一个停止维护五年的工具?


首先是轻量。Stetho 的核心库体积大约 200KB 左右(aar 包),加上 OkHttp interceptor 也不到 300KB。Flipper 的 Android SDK 部分就要几 MB,桌面端安装包上百 MB。对于资源受限的旧项目,或者只是想快速验证一个想法,Stetho 的集成负担小得多。


其次是简单。Stetho 的架构很透明,出问题容易定位。Chrome DevTools 是标准工具,前端同事也能看懂。Flipper 的插件机制更强大,但出了问题要排查 Electron 桌面端、插件版本、SDK 版本三者的兼容性,复杂度不在一个量级。


更重要的是理解历史设计。Stetho 是早期把 Web 技术栈的调试体验引入移动开发的尝试之一,它的成功和失败都影响了后来的工具。比如 Flipper 的插件架构、Android Studio 的 Database Inspector 的实时刷新、甚至 Chrome 自己后来对 PWA 和 WebView 的调试支持,都能看到类似的思路。理解 Stetho 怎么映射 CDP,对于今天想自己写调试工具或者扩展 Flipper 插件的人来说,仍然是很好的参考。


最后一点实操建议


如果你现在还想用 Stetho,有几个务实的做法。


第一,不要把它作为长期依赖。可以在项目里保留,但做好随时迁移的准备。把 Stetho 的初始化代码封装到一个 DebugTools 类里,对外只暴露 init()okHttpInterceptor() 两个方法,内部决定是否委托给 Stetho、Flipper、或者什么都不做。这样迁移的时候只需要改一处。


第二,注意 OkHttp 版本。如果已经在用 OkHttp 4.10+,建议先在一个分支上测试 StethoInterceptor 是否能正常工作,确认没有反射异常再合并。


第三,SharedPreferences 的 inspect 在 Stetho 里是通过 dumpapp 的 prefs 命令实现的,Chrome UI 里看不到。如果你主要需求是可视化查看和修改 SP,Stetho 并不好用,不如直接用 Android Studio 的 Device File Explorer 把 shared_prefs 的 XML 文件 pull 出来编辑,或者写个简单的 debug 界面放在应用里。


第四,chrome://inspect 的 discover 机制有时候不稳定,尤其是 Windows 上的 USB 驱动或者 macOS 上的 Android File Transfer 冲突时。如果设备列表里看不到 Stetho 目标,先检查 adb devices 是否正常,然后尝试 adb kill-server 重启。这个不是 Stetho 的问题,是 Chrome 的 USB 发现层的问题,但很多人会误以为是 Stetho 没启动成功。


Stetho 是一个特定历史时期的优秀工具,它的设计思路——复用 Web 生态的调试基础设施——在今天依然有效。但它的停止维护也说明了移动调试工具的一个趋势:官方化、集成化、重 IDE 轻独立工具。对于还在维护的老项目,Stetho 还能用,也值得用;对于新项目,直接上 Flipper 或者 Android Studio 的原生工具是更可持续的选择。关键是理解每个工具能做什么、不能做什么,以及这些边界背后的技术原因,而不是盲目追新或者怀旧。

Ktor 客户端替代 Retrofit,协程原生支持 2026-06-30
Kotlin 的 Inline Class 和 Value Class,字节码层面看什么 2026-07-01

评论区