OpenClaw Android APP 启动全流程大揭秘

当你手指戳向那个图标的瞬间,你可能以为只是打开了一个 APP。
错!大错特错!
你其实是按下了一个复杂机器的启动按钮,就像推开了迪士尼乐园的大门——接下来,一场精心编排的表演开始了。
第一幕:Application 的”晨间准备”
NodeApp:我是大管家
classNodeApp : Application() {
val prefs: SecurePrefs by lazy { SecurePrefs(this) }
}
NodeApp 这位老哥,相当于整个 APP 的”大内总管”。它干的第一件事就是掏出个小本本(SecurePrefs),开始记录各种配置信息。
这个小本本可不一般,它是加密版的!比你的日记本还安全,专门存放各种敏感信息:网关 Token、密码、设备 ID……
💡 冷知识:这货用的是 AES256 加密,比你微信聊天记录保护得还好。
NodeApp 还会在 Debug 模式下开启”严格模式”,像个班主任一样盯着:”谁敢在主线程搞 IO?站出来!”
第二幕:MainActivity 的”化妆时间”
MainActivity:我要见人了
overridefunonCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
permissionRequester = PermissionRequester(this)
setContent {
OpenClawTheme {
Surface(modifier = Modifier) {
RootScreen(viewModel = viewModel)
}
}
}
}
MainActivity 这位女主角登场了,她开始精心打扮:
- 去掉系统窗口装饰
– “我要全屏美艳!” - 准备权限请求器
– “等会儿要权限的,先准备好” - 设置 Compose UI
– “穿上 Jetpack Compose 这件名牌衣服”
然后她创建了一个重要的角色——MainViewModel。
第三幕:MainViewModel 的”灵魂拷问”
MainViewModel:你是谁?你去哪?
init {
if (prefs.onboardingCompleted.value) {
ensureRuntime() // 老用户,直接干活
}
}
MainViewModel 一上任就开始了灵魂拷问:
“兄弟,第一次来吗?”
这就引出了整个 APP 最重要的分叉路口——
第四幕:人生的岔路口
RootScreen:新人老人,区别对待
val onboardingCompleted by viewModel.onboardingCompleted.collectAsState()
if (!onboardingCompleted) {
OnboardingFlow(...) // 新手村
} else {
PostOnboardingTabs(...) // 老江湖
}
这一刻,RootScreen 像个火车站检票员:
- 第一次来的
→ 左边,引导流程走起! - 老司机
→ 右边,主界面随便玩!
第五幕:新手村的”四道关卡”
OnboardingFlow:闯关开始
如果你是个萌新,恭喜你,要经历四个步骤的”洗礼”:
Step 1:Welcome(画大饼)
WelcomeStep() {
FeatureCard("连接到你的网关")
FeatureCard("选择你的权限")
FeatureCard("聊天、语音、屏幕控制")
FeatureCard("验证连接")
}
这一步就是在给你画大饼:
-
“我们能连网关!” -
“我们能管权限!” -
“我们什么都能干!”
像不像健身房销售给你展示完美身材的照片?
Step 2:Gateway(填表格)
when (gatewayInputMode) {
GatewayInputMode.SetupCode -> "扫码吧亲"
GatewayInputMode.Manual -> "手动输入地址端口"
}
这里让你配置网关地址,有两种方式:
- 扫描二维码
– “滴~ 学生卡”(啊不对,是网关码) - 手动输入
– 适合极客,一行代码搞定那种
还要填 Token、密码、TLS 开关……
😂 吐槽:这架势,比你办银行卡填的表还多。
Step 3:Permissions(要权限)
PermissionToggle.entries.forEach {
CheckBox("相机", "位置", "麦克风", "短信", "通讯录"...)
}
到了最刺激的环节——要权限!
APP 像个小心翼翼的销售:
-
“亲,能给个相机权限吗?” -
“那……位置呢?” -
“麦克风也行啊……” -
“通讯录?就看看,不干嘛……”
用户内心 OS:你要不要干脆把我银行卡密码也要了?
Step 4:FinalCheck(验货)
if (isConnected) {
Button("Finish") // 成功了!
} else {
Button("Connect") // 再试试
}
最后一步,现场表演”胸口碎大石”——当场连接网关给你看!
成功了?恭喜毕业! 失败了?再来一次!
期间还可能弹出 TLS 证书确认框:
“这个网关的指纹是 XXX,你信不信?”
你:“我信我信,赶紧让我进去!”
第六幕:正式上岗——五大护法
PostOnboardingTabs:主界面的五虎上将
通过新手村后,你终于来到了主界面。这里有五个 Tab,各司其职:
Connect(连接护法)
HomeTab.Connect -> "找网关、连网关、显示状态"
负责找网关、连网关,显示当前连接状态。
状态包括:
-
Connected – 一切正常 -
Connecting – 正在努力 -
Warning – 有点小问题 -
Error – 出大事了 -
Offline – 躺平了
像极了你的 WiFi 信号指示器。
Chat(聊天护法)
HomeTab.Chat -> "发消息、收消息、调用工具"
这就是个AI 聊天界面,但不止能聊天:
-
发文字 -
发图片 -
AI 思考时还能看到”thinking level” -
需要调用工具时会弹窗问你
示例对话:
你:帮我查下天气
AI:[思考中...]
AI:需要调用天气 API,同意吗?
你:准了
AI:今天晴天,25 度
Voice(语音护法)
HomeTab.Voice -> "听你说、对你喊"
这是语音交互模式:
-
实时听写你说的话 -
显示声音波形 -
自动识别唤醒词(”openclaw”、”claude”) -
用 TTS 对你说话
开启后,你的手机就变成了《钢铁侠》里的贾维斯。
Screen(屏幕护法)
HomeTab.Screen -> "WebView 渲染远程 UI"
这是最黑科技的功能——A2UI 画布:
简单说,就是能在 APP 里显示一个远程控制的网页,你在手机上点点点,远程服务器就知道你在干嘛。
类似于 TeamViewer,但更轻量级。
Settings(设置护法)
HomeTab.Settings -> "改名字、开关功能、调参数"
这里是控制面板:
-
改设备名字 -
开关相机/位置 -
防止屏幕休眠 -
配置唤醒词 -
开关麦克风和扬声器
第七幕:后台的”秘密行动”
NodeRuntime:真正的打工人
当你沉浸在 UI 中时,NodeRuntime 这位幕后大佬已经在后台忙活了:
classNodeRuntime {
val canvas = CanvasController() // 管屏幕的
val camera = CameraCaptureManager() // 管相机的
val location = LocationCaptureManager() // 管位置的
val sms = SmsManager() // 管短信的
// 还有十几个 Handler...
}
它初始化了一堆Handler:
CameraHandler
– 拍照录像 LocationHandler
– 定位追踪 SmsHandler
– 收发短信 ContactsHandler
– 读联系人 CalendarHandler
– 访问日历 MotionHandler
– 检测运动 -
……
感觉像是在组建复仇者联盟。
GatewayDiscovery:星探
privateval discovery = GatewayDiscovery(...)
val gateways: StateFlow<List<GatewayEndpoint>> = discovery.gateways
这家伙像个星探,一直在后台扫描:
“发现网关!IP: 192.168.1.100” “又发现一个!IP: 10.0.2.15”
然后把这些信息告诉 UI,让你选择连接哪个。
ConnectionManager:外交官
privateval connectionManager = ConnectionManager(...)
这位是外交官,负责跟网关建立外交关系:
-
发送认证信息 -
协商 TLS 加密 -
建立 WebSocket 连接 -
保持心跳
比你谈恋爱还用心。
第八幕:前台服务——”我不睡了”
NodeForegroundService:永动机
NodeForegroundService.start(this)
一旦你完成引导,APP 就会启动一个前台服务,并在通知栏显示:
“OpenClaw 正在运行”
这意味着啥?它不睡了!
即使你切换到其他 APP,它依然在后台:
-
监听网关消息 -
准备接收语音指令 -
保持连接不断线
像个 24 小时待命的私人助理。
第九幕:数据流的”八卦图”
状态同步:牵一发而动全身
整个 APP 的数据流是这样的:
用户操作 → ViewModel → NodeRuntime → 网关
↓ ↓
UI 更新 ← StateFlow ← 状态变化
举个例子:
-
你点了”连接”按钮 -
ViewModel 调用 connect() -
NodeRuntime 开始连接 -
连接成功后更新 _isConnected.value = true -
StateFlow 通知所有订阅者 -
UI 自动刷新,显示”已连接”
这就是 Kotlin Flow 的威力——全自动流水线。
总结:你以为的 vs 实际上的
你以为的启动:
点击图标 → 出现界面 → 完事
实际上的启动:
Application 创建
↓
读取加密配置
↓
创建 ViewModel
↓
判断是否首次
↓
[首次] → 引导流程 4 步
↓
[非首次] → 主界面 5 个 Tab
↓
初始化运行时
↓
创建 10+ 个 Handler
↓
启动网关发现
↓
建立 WebSocket 连接
↓
启动前台服务
↓
Canvas A2UI 水合
↓
终于可以跟你打招呼了
整个过程,比你早上起床刷牙洗脸吃早餐还复杂。
彩蛋:那些代码里的小心思
1. 懒加载的智慧
val prefs: SecurePrefs by lazy { SecurePrefs(this) }
lazy 关键字:能拖则拖,不到用时不创建。
像极了 deadline 前的你。
2. 状态管理的艺术
val isConnected: StateFlow<Boolean>
val statusText: StateFlow<String>
全是 StateFlow,UI 想不更新都难。
这就是响应式编程的魅力。
3. 协程的力量
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// 自动跟随生命周期
}
}
协程 + 生命周期感知,内存泄漏?不存在的。
比你对象还贴心。
夜雨聆风