2193 字
11 分钟

Orbit for Android:让 YAML 和后台各守边界

这篇文章的目录

做完 Windows 上的 Orbit,我没有直接把桌面界面缩小搬到手机上。手机的难处不一样:应用会被划掉,同一个包名可能同时出现在主用户和分身里,配置里写了控制接口,也未必就能用普通 HTTP 客户端访问。

我想保留的,是 Windows 版那个很朴素的出发点:自己的 YAML 仍然由自己决定,管理器把运行过程交代清楚。

于是有了 Orbit for Android。它用 Kotlin、Jetpack Compose 和 Material 3 做界面,在已 Root 的 Android 设备上管理一个官方 Mihomo 实例。当前公开版本是 0.1.15,要求 Android 9 及以上、ARM64。它依赖 Root 后台,不提供免 Root 的 VPNService 模式。

首页先说清楚现在发生了什么#

我希望打开应用时,第一眼看到的是当前状态,而不是一屏等着配置的开关。所以首页围绕内核、当前配置、面板、应用绕过以及几项诊断信息来组织。

点击当前配置卡片进入配置管理,点击 HTTP 延迟卡片刷新检测。资源信息里,CPU 的 100% 指一个核心;RSS 是物理内存占用,只在首页前台采样。这些解释会放在信息附近,避免一个没有上下文的数字让人自己猜。

Orbit Android v0.1.15 实际运行首页:Mihomo、当前配置、应用绕过、HTTP 延迟与资源占用

图:Orbit Android v0.1.15 的实际运行截图,内核为 Mihomo v1.19.33。配置、已生效的 274 个应用身份、延迟和资源占用均为截屏当时的状态;单张截图不能代表不同设备或网络下的性能。

Android 版的颜色比 Windows 更收敛:明暗主题都以中性色为主,靠文字、边线和灰阶区分状态。选中、不可用、保存中这些状态需要说清楚,而不是只用一种颜色暗示。

这也是我理解的“顺手”:屏幕变窄、字体变大以后,长配置名仍能读,按钮仍能找到。界面漂亮很重要,但先得让人把事情做完。

原始配置和运行配置要分开#

导入本地文件或 HTTPS 地址之后,Orbit 保存、导出的是原始 YAML 字节。需要编辑时另存一个副本,避免在修改过程中覆盖掉来源配置。

TUN、DNS、端口、代理组、规则、控制接口和面板地址,都由用户 YAML 决定。Orbit 不替配置补一个 HTTP 控制接口,不强制改密钥,也不会偷偷装一份内置面板。虽然源码树中保留了历史面板资源,当前运行流程并不因此启用它们。

面板的行为很直接:如果 YAML 配好了 HTTP(S) 控制接口和 external-ui,Orbit 从实际运行配置读取控制信息,打开对应的 /ui/。下载和管理面板仍走 Mihomo 自己的机制。只有 Unix / Pipe 接口时,应用会提示 HTTP API 和 Web UI 能力不可用。

我特意区分“选中了什么配置”和“内核实际用了什么配置”。一个尚未应用的候选地址,不能提前当成当前控制接口。接口、密钥或面板配置变化时,走已有的受控重启流程,避免界面连着新地址,内核却还在用旧设置。

配置入口也会做必要的解析约束。ConfigEngine.kt 使用安全 YAML 构造器,限制文件大小、嵌套深度和集合别名数量,拒绝重复键。这不意味着可以放心导入任意来路的订阅,而是让明显有问题的输入尽早失败。

应用绕过,最容易把“身份”想简单#

应用绕过最初看起来像一张勾选名单,实际要处理的却是 Android 身份。

同一个包名,在主用户和应用分身里有不同的完整 UID;不同应用也可能共享同一个 UID。如果只记包名,就会在这些环境里做错决定。

Orbit 按“包名 + Android 用户”保存选择,提交时重新解析当前安装身份,把选中的完整 UID 合并进官方 tun.exclude-uid,同时保留 YAML 已有的筛选项。共享 UID 的应用会一起绕过,界面会把这种关系说明白。

这里有一个很重要的边界:绕过名单决定是否进入 TUN;进入 TUN 以后走 DIRECT 还是 PROXY,仍然由 Mihomo YAML 决定。 这不是另一套客户端分流规则。

勾选也不会每点一下就立即改后台。开关和选择先留在页面暂存里,最后统一保存;运行时可选择“保存并热重载”,未运行时只保存,不自动启动。搜索、排序、切换分类和滚动,都不应该把刚勾好的内容清掉。保存失败则保留修改,离开页面时再明确选择保存、放弃或取消。

我宁可多做一点状态管理,也不想让一次普通的列表筛选变成网络行为变化。

划掉应用之后,后台有自己的生命周期#

Root 后台放在 /data/adb/orbit,和应用界面分开。划掉应用后,已经运行的 Mihomo 继续工作;重新打开时,只查询和接管通过身份核验的既有实例,不顺带启动。

这套核验会结合 PID、启动时间、boot ID 和可执行路径。仅凭一个 PID 判断“这是我的进程”不够可靠,因为系统可能已经复用了这个数字。操作锁和独立 Root 会话,也是在减少重复启停、并发更新以及应用生命周期带来的干扰。

我没有把“独立后台”写成永不退出的保证。对已经确认运行、随后异常退出的内核,原 supervisor 最多按 2、4、8 秒退避恢复三次;主动停止、更新事务或监督身份不确定时,不应该擅自重启。

开机自启默认关闭。明确开启之后,才注册 Orbit 自己的 service.d 入口,在首次解锁后尝试恢复上次配置与已保存名单。失败、取消或本次开机已主动停止时,也不能无限重试。对网络工具来说,“我已经点了停止”必须比任何恢复机制都更有分量。

有 AI 入口,但每一步都得由人发起#

应用名单很长时,可以使用可选的 AI 智能选择。它支持配置 OpenAI 兼容接口和模型,API Key 用 Android Keystore 加密保存。

我把这个入口做得比较明确:保存接口设置本身不会发送名单,只有主动开始分析,才会把完整应用名单交给所配置的服务。返回结果要按包名和 Android 用户身份校验,所有批次完整成功后,才更新页面暂存勾选。

它不会直接保存名单、改 YAML 或操作内核。取消、失败或结果不完整,原选择都保留。是否愿意把应用名单交给外部模型,是使用者自己的决定;如果不需要,手动筛选和保存就已经是一条完整路径。

从哪里开始,以及现在的边界#

第一次用,可以从 Forgejo Releases 下载 ARM64 APK,授予 Root 后导入自己的 YAML,再主动启动。先确认状态和日志;需要面板时再检查自己的 HTTP(S) 控制接口与 external-ui 配置。

应用更新走本项目的 Forgejo 发行,内核更新则独立从 Mihomo 官方稳定发行获取 ARM64 资产并校验,失败时沿用回滚流程。

当前还有一个下游 IPv4 放行开关,但我不把它当作“完整热点代理”推荐。它只在核验后的系统共享转发链里添加精确放行规则,不改 YAML、路由或 DNS;UDP、DNS、IPv6 和原生恢复还没有完成验收。如果需求正好落在这里,请先看仓库的验证说明。

我希望 Orbit for Android 最后留下的体验,是界面和后台都不越界:配置没生效就别显示已经生效,保存失败就别丢掉修改,用户停掉了就别再自作主张地恢复。

这比多加几个开关更值得我继续做下去。


项目入口:源码与使用说明、更新记录、发行与签名说明。关键实现位于 ConfigEngine.kt、OrbitModel.kt、OrbitStore.kt、RootClient.kt 和 assets/manager.sh。2026 年 10 月 25 日将配图替换为实际运行截图,保留原始画面,仅作 WebP 格式压缩;Windows 版的思路另见这篇手记。

Orbit for Android:让 YAML 和后台各守边界
https://blog.inklazy.com/posts/orbit-android/
作者
Inklazy
发布于
2026-10-24
许可协议
CC BY-NC-SA 4.0