Android 登录与聊天底栏修复

知与行 / iOS App 开发复盘 ·

Android 密码登录失败、设密不进主页、聊天底栏 Dock 异常。根因:WebView 凭据持久化缺失、键盘弹起遮挡设密按钮、Dock 安全区未适配。

by 于尘 ·

记录日期: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 specifiedNetworkingModule.kt),signIn 请求体未带 Content-Type
  • why2:RN 包装 oauth2client.baseRequest 在 Android XHR 上对 JSON body 不自动补 application/json
  • why3:部分路径仍走 SDK User.refresh / accessKey 会话,叠加 INVALID_ACCESS_TOKEN 时登录被误判为「会话过期」

方案

  • 短期方案:patchRnOAuthBaseRequestContentType + Android 专用 signInWithPasswordOnAndroidauthApi.signInpersistOAuthCredentialscreateLoginState
  • 优点:针对已观测失败路径,iOS 仍走 SDK
  • 缺点:cloudbase.ts 平台分支增加
  • 长远方案:上游 @cloudbase/auth / adapter-rn 修复后删除 workaround
  • 优点:单一路径
  • 缺点:依赖发版周期

采纳方案:短期方案;非 Android 登录前 clearAuthSessionBeforeCredentialLoginINVALID_ACCESS_TOKEN 清会话重试一次;timeout.ts / useAlerts.ts 避免「登录失败」映射成「登录已失效」。

补充用例

  • Android:邮箱/11 位手机号 + 密码 → syncUserFromSDK → authenticated → 进 /(tabs)
  • iOS:仍走 auth.signInWithPassword,行为与改前一致
  • 错误文案:凭证错误显示业务句,非泛化「登录已失效」

涉及文件zhiyuxing-app/src/lib/cloudbase.tstimeout.tsuseAlerts.tsapp/(auth)/email-login.tsx


2.2 验证码设密假成功、不进主页

根因

  • why1:设密后仍 status=unauthenticatedAuthNavigationSync 不跳主页
  • why2:updateUser 实际失败:invalid ResetPasswordRequest.PhoneNumber(须 +86 158…,SDK 闭包传入裸 11 位)
  • why3:phone-set-password.tsx 未检查 updateResult.error,仍 scheduleSyncAfterLogin + showInfo('设置成功')

方案

  • 短期方案:resetPasswordForPhone 发码前 formatPhonethrowIfSdkAuthResultFailed 失败即抛错
  • 优点:改动小,与发码格式一致
  • 缺点:依赖 SDK 闭包仍用入参原文(须传格式化号码)
  • 长远方案:自管 verify + resetPassword HTTP,绕过 SDK resetPasswordForEmail 闭包
  • 优点:完全控格式
  • 缺点:维护成本高

采纳方案:短期方案;AuthContext 匿名会话不标 authenticated(发码链路与真实登录分离)。

补充用例(Android 已验证日志):

  • after-updateUserhasError: falseuserUid 为真实用户 id,isAnonymousAuthSession: false
  • syncUserFromSDK → authenticatedredirect to /(tabs)
  • 失败时:弹具体错误,不再假成功

涉及文件cloudbase.tsphone-set-password.tsxemail-set-password.tsxAuthContext.tsx


2.3 聊天页上三角无反应、语音不可用(Android)

根因

  • why1:上三角与麦克风共用 disabledsending || !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-handler Pressable
  • 优点:触摸语义更清晰
  • 缺点:重构面大

采纳方案:短期方案。

补充用例

  • 上三角:未同步完也可展开「文字输入」
  • 语音:长按麦克风 → 申请 RECORD_AUDIO → 录音遮罩与波形(需 dev build,Expo Go 可能无原生 Voice 模块)
  • 左上角历史图标:点击开侧栏;明显右滑亦可开栏

涉及文件AgentChatVoiceDock.tsxAgentChatVoiceDockContext.tsxVoiceInputEngine.android.tsAgentChatHistoryDrawer.tsxagent-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.tszhiyuxing-app/src/lib/voice-input/zhiyuxing-app/src/components/agent-chat/skills/dev-report/SKILL.md

← 所有文章