Android 登录与聊天底栏修复
知与行 / iOS App 开发复盘 ·
Android 密码登录失败、设密不进主页、聊天底栏 Dock 异常。根因:WebView 凭据持久化缺失、键盘弹起遮挡设密按钮、Dock 安全区未适配。
记录日期:2026-05-29
格式参考:docs/开发记录博客/agents.md
1. 问题背景
Android 真机本地开发(Expo / dev build)联调时,登录与神秘人聊天底栏与 iOS 表现不一致:
- 邮箱/手机号 密码登录失败(iOS 正常)
- 手机 验证码设密 弹「设置成功」但 不进主页
- 聊天页 上三角(输入方式菜单)无响应、按住说话不可用
根因横跨 CloudBase Auth SDK 在 RN Android 上的 HTTP/号码格式问题,以及聊天 Dock 把「会话未就绪」误绑到模式切换按钮、语音引擎调错 API。
2. 问题列表
2.1 Android 密码登录失败(iOS 正常)
根因:
- why1:Metro 日志出现
Payload is set but no content-type header specified(NetworkingModule.kt),signIn请求体未带Content-Type - why2:RN 包装
oauth2client.baseRequest在 Android XHR 上对 JSON body 不自动补application/json - why3:部分路径仍走 SDK
User.refresh/ accessKey 会话,叠加INVALID_ACCESS_TOKEN时登录被误判为「会话过期」
方案:
- 短期方案:
patchRnOAuthBaseRequestContentType+ Android 专用signInWithPasswordOnAndroid(authApi.signIn→persistOAuthCredentials→createLoginState) - 优点:针对已观测失败路径,iOS 仍走 SDK
- 缺点:
cloudbase.ts平台分支增加 - 长远方案:上游
@cloudbase/auth/ adapter-rn 修复后删除 workaround - 优点:单一路径
- 缺点:依赖发版周期
采纳方案:短期方案;非 Android 登录前 clearAuthSessionBeforeCredentialLogin,INVALID_ACCESS_TOKEN 清会话重试一次;timeout.ts / useAlerts.ts 避免「登录失败」映射成「登录已失效」。
补充用例:
- Android:邮箱/11 位手机号 + 密码 →
syncUserFromSDK → authenticated→ 进/(tabs) - iOS:仍走
auth.signInWithPassword,行为与改前一致 - 错误文案:凭证错误显示业务句,非泛化「登录已失效」
涉及文件:zhiyuxing-app/src/lib/cloudbase.ts、timeout.ts、useAlerts.ts、app/(auth)/email-login.tsx
2.2 验证码设密假成功、不进主页
根因:
- why1:设密后仍
status=unauthenticated,AuthNavigationSync不跳主页 - why2:
updateUser实际失败:invalid ResetPasswordRequest.PhoneNumber(须+86 158…,SDK 闭包传入裸 11 位) - why3:
phone-set-password.tsx未检查updateResult.error,仍scheduleSyncAfterLogin+showInfo('设置成功')
方案:
- 短期方案:
resetPasswordForPhone发码前formatPhone;throwIfSdkAuthResultFailed失败即抛错 - 优点:改动小,与发码格式一致
- 缺点:依赖 SDK 闭包仍用入参原文(须传格式化号码)
- 长远方案:自管 verify + resetPassword HTTP,绕过 SDK
resetPasswordForEmail闭包 - 优点:完全控格式
- 缺点:维护成本高
采纳方案:短期方案;AuthContext 匿名会话不标 authenticated(发码链路与真实登录分离)。
补充用例(Android 已验证日志):
after-updateUser:hasError: false,userUid为真实用户 id,isAnonymousAuthSession: falsesyncUserFromSDK → authenticated→redirect to /(tabs)- 失败时:弹具体错误,不再假成功
涉及文件:cloudbase.ts、phone-set-password.tsx、email-set-password.tsx、AuthContext.tsx
2.3 聊天页上三角无反应、语音不可用(Android)
根因:
- why1:上三角与麦克风共用
disabled(sending || !user || !chatDbHydrated),同步未完成时 整颗 Dock 禁用,菜单点不动 - why2:
VoiceInputEngine.android.ts调用不存在的Voice.startRecognizing(),落入 catch →「语音识别功能不可用」 - why3(次要):历史侧栏主区
Pan曾设minDistance(0),在 Android 上易与头部/可点击区抢触摸
方案:
- 短期方案:Dock 拆
inputDisabled(仅禁麦克风/键盘);Android 语音引擎对齐 iOS(Voice.start、partial 结果、Voice.stop);去掉主区minDistance(0) - 优点:立刻恢复菜单与语音
- 缺点:未登录时仍可切文字模式(可接受)
- 长远方案:语音与模式切换拆成独立组件;RN 统一用
react-native-gesture-handlerPressable - 优点:触摸语义更清晰
- 缺点:重构面大
采纳方案:短期方案。
补充用例:
- 上三角:未同步完也可展开「文字输入」
- 语音:长按麦克风 → 申请
RECORD_AUDIO→ 录音遮罩与波形(需 dev build,Expo Go 可能无原生 Voice 模块) - 左上角历史图标:点击开侧栏;明显右滑亦可开栏
涉及文件:AgentChatVoiceDock.tsx、AgentChatVoiceDockContext.tsx、VoiceInputEngine.android.ts、AgentChatHistoryDrawer.tsx、agent-chat.tsx
3. 小结
| 域 | 要点 | |----|------| | Android 登录 | Content-Type 补丁 + 专用密码登录写 token;错误文案与 iOS 分流 | | 验证码设密 | 手机号 +86 格式 + SDK 返回值校验;匿名会话不进主页 | | 聊天底栏 | 模式菜单与输入禁用解耦;Voice API 修正 | | iOS | 密码登录路径未改;设密/错误校验为双端共用;RN Content-Type 补丁为无害兜底 | | 清理 | 已删除 [verify-reg-diag] 探针与设密后多余 Android 补登(日志证明 SDK 内 signIn 已切真实用户) |
后续若语音在 Expo Go 仍不可用,应使用带 @react-native-voice/voice 的本地构建,而非仅 Metro Reload。
参考:zhiyuxing-app/src/lib/cloudbase.ts、zhiyuxing-app/src/lib/voice-input/、zhiyuxing-app/src/components/agent-chat/、skills/dev-report/SKILL.md