chatapp3-flutter/需求进度.md
2026-04-16 15:07:47 +08:00

44 KiB
Raw Blame History

需求进度

当前总目标

  • 控制当前 Flutter Android 发包体积,持续定位冗余组件、超大资源和不合理构建配置,并把每一步处理结果落盘记录。

本轮启动优化(非网络)

  • 已移除启动页固定 6 秒倒计时,改为最短约 900ms 展示后继续路由判断,避免人为等待直接拉长入 app 时间。
  • 已将 Firebase / Crashlytics 从 runApp 前阻塞初始化改为首帧后后台预热,并补充“未就绪时仅本地打印”的异常兜底,减少首屏前平台初始化阻塞。
  • 已将 SocialChatAuthenticationManagerSocialChatUserProfileManager 改为按需创建,避免 splash 阶段就提前触发 Google Sign-In / Firebase 会话检查。
  • 已将 deep link、设备信息、缓存目录创建等非首帧必需动作延后到首帧后执行降低首屏竞争。
  • 已优化本地语言初始化:启动时不再重复写入 SharedPreferences 并触发一次无意义的 notifyListeners
  • 已优化首页容器与 Home 首屏:去掉首次进入时“先 loading 再建 tab”的本地跳变并将首页 page 列表、底部导航项从 build 阶段的重复重建改为复用。
  • 本轮按需求暂未处理网络链路上的启动等待,例如审核态检查或远端启动页配置请求。

已完成模块

  • 已按本轮动效替换需求完成首页底部 tab 动效接入,并同步将本轮新增本地 .svga 资源统一改成项目命名风格:当前 Home / Explore / Message / Me 已分别使用 sc_icon_home_anim.svga / sc_icon_explore_anim.svga / sc_icon_message_anim.svga / sc_icon_me_anim.svga,并继续保留原有 png 作为失败兜底;后续新增本地动效资源默认也按 sc_icon_*_anim 规则命名。
  • 已将语言房右侧 game 悬浮入口从 emoji 占位替换为本地动效资源,并按最新反馈移除外层圆形 container:当前直接使用 sc_icon_room_game_entry_anim.svga 本体作为入口展示,尺寸与原入口占位一致;若 SVGA 加载失败,会自动回退到项目原有 sc_icon_botton_game.png
  • 已完成语言房房间麦位声波资源替换并同步按项目规范重命名VIP3/4/5/6 麦位现分别改接 sc_icon_room_vip3/4/5/6_sonic_anim.svga,普通麦位新增区分“本人/他人”声波资源,默认分别使用 sc_icon_room_self_sonic_anim.svgasc_icon_room_other_sonic_anim.svga;原有说话触发阈值、错峰扩散逻辑和旧 webp/纯色圆形 fallback 仍保留。
  • 已修复语言房顶部左侧房间榜单入口出现 right overflowed 的问题:当前贡献值区域已改为约束宽度 + 自适应缩放文本,不再因为积分位数变长把右箭头顶出容器。
  • 已根据最新确认回退首页 Party 区的 recommend_rank_top 三个素材替换:财富榜、房间榜、魅力榜前三头像现已恢复为原先版本,不再在该位置叠加 recommend_rank_top 动态头像框;相关素材仍保留在工程内并已按规范重命名,后续待确认真正使用位置。
  • 已新增通用本地 SVGA 资源组件 lib/ui_kit/widgets/svga/sc_svga_asset_widget.dart,用于统一处理本地 assets 解码、内存缓存、单次播放/循环播放和失败兜底;本轮底部 tab、语言房 game 按钮和麦位声波均复用这套实现,避免后续重复写播放器逻辑。
  • 已按最新语言房底部栏视觉需求完成 UI 重构:底部 5 个入口现改为同一水平基线布局,移除礼物按钮原先的中间悬浮 Stack 结构并最终按“1、2、5、3、4”顺序排列也就是“输入入口、礼物、消息、麦克风、菜单”麦克风隐藏时继续保留占位保证整体间距平均、视觉对齐稳定。
  • 已将语言房左侧输入入口改为独立组件:当前已撤回自绘聊天气泡图标方案,直接恢复使用项目原有的 sc_images/room/icon_room_input_t.png 资源作为左侧输入 icon并继续放入约三分之一屏宽的圆角矩形中右侧保留多语言问候文案 roomBottomGreeting,避免继续在自绘气泡尾巴形态上反复调整。
  • 已将语言房礼物入口拆成独立组件:礼物图标缩小后当前尺寸只略大于右侧 3 个圆按钮,并保留常驻轻量摇晃动画;根据最新反馈,当前已移除礼物按钮的粒子特效,并进一步去掉礼物入口外层 container,直接使用与原先按钮壳同尺寸的礼物图片本体作为可点击 UI避免额外底壳干扰观感。
  • 已统一语言房其余四个入口的按钮壳样式:输入入口、消息、菜单、麦克风现统一使用淡色半透明背景 container消息红点计数、菜单弹窗、消息跳转和麦克风开关等原有功能逻辑保持不变本轮只调整 UI 展示与布局层。
  • 已按最新交互要求调整麦克风隐藏态布局:当第 4 个图标(麦克风)可见时,底部栏保持当前 5 个槽位的位置不变;当麦克风隐藏时,不再为其保留空槽位,礼物、消息、菜单 3 个入口会自动向右收拢成一组,避免右侧中间出现空位。
  • 已排查语言房“双设备、不同账号进入同一房间,上麦后无声”的直接原因:当前 RTC 进房时统一以 Agora Audience 身份加入频道,见 lib/services/audio/rtc_manager.dartjoinChannel()clientRoleType: clientRoleAudience;而本地麦克风开关 isMic 默认值为 true(当前语义实际是“闭麦”),上麦 shangMai() 后只有在 !isMic 时才会切到 Broadcaster 并取消本地静音,所以“只上麦、不点底部麦克风按钮”时不会发声。
  • 已补充语言房当前真实交互结论:现有实现里“上麦”和“开麦”是两个动作。用户点空麦位后只是占麦;还需要再点击底部麦克风按钮,触发 lib/ui_kit/widgets/room/room_bottom_widget.dart 中的角色切换,才能真正开始向房间发送音频。
  • 已同步补充该需求的后续决策点:如果产品预期是“上麦即能说话”,则需要单独改需求并调整实现为“上麦成功后自动切 Broadcaster 且自动开麦”;如果继续保留当前双步骤交互,则至少要补一条明确提示文案/引导,并把“双设备不同账号同房,上麦后需手动开麦才能互听”加入验收用例,避免测试误判为 RTC 故障。
  • 已重新收敛语言房无声修复方案:撤回上一轮会影响进房的较大改动后,当前改为只做最小行为修补,不触碰进房和 token/join 链路;已在“底部麦克风点开”和“麦位解除静音且本人处于开麦态”两条路径上补齐 muteLocalAudioStream(false),修正 UI 显示已开麦但 Agora 本地音频流仍保持静音的问题。
  • 已按测试收尾要求移除语言房 RTC 诊断面板:该组件仅用于当前无声问题的真机排查,现已从房间页和相关临时诊断代码中整体删除,不作为正式功能保留。
  • 已为语言房页面补充右下方 game 悬浮入口:入口位于聊天区右下侧、距右侧约 15.w、位于底部操作栏上方约 100.w,当前先使用代码绘制的 🎮 占位图标,并已在实现处标注后续替换为正式 UI 图片资源。
  • 已新增语言房游戏底部弹窗:点击 game 入口后会从底部弹出约三分之一屏高的面板,采用房间页现有半透明磨砂风格,便于后续继续沿用当前视觉体系。
  • 已补齐语言房游戏列表占位结构:弹窗内部先用静态游戏数据驱动,并按“ListView 可上下滑动 + 每行 5 个图标位”的方式实现,后续只需替换入口图、列表图标和真实接口数据即可继续开发。
  • 已排查语言房游戏 H5 无法正常加载的直接原因:当前房内 BAISHUN 游戏页把“加载完成”几乎完全绑定在 H5 主动回调 gameLoaded 上,同时 window.NativeBridge 又是在 WebView onPageFinished 后才注入;真实 H5 若在更早的启动阶段就尝试读取桥接配置或判定 NativeBridge 是否存在,就会错过这次注入,表现为页面一直停留在 Waiting for gameLoaded...因此问题核心更接近“App 与 H5 的桥接初始化时序不稳 + 加载态过度依赖 H5 回调”,而不只是单纯的页面 URL 无法访问。
  • 已对语言房游戏 H5 链路补齐客户端兜底:房内游戏页现在会在页面启动阶段提前并重复尝试注入 BAISHUN bridge不再只等首个 onPageFinished;同时加载遮罩已增加超时收口,不会因为 H5 没有及时回调 gameLoaded 就无限卡住。桥接侧也已补充 getConfigSync、全局配置缓存和 baishunBridgeReady/baishunConfig 事件分发,降低 H5 初始化阶段拿不到配置的概率,便于继续和 H5 联调确认其最终采用的接入方式。
  • 已按《BAISHUN 游戏对接文档 1.3.2》重新核对客户端桥接协议:文档里的核心要求并不只是 NativeBridge 存在,还包括 getConfig 入参携带 jsCallback、客户端回调指定 JS 函数、以及 Flutter 章节单独列出的 getConfig / destroy / gameRecharge / gameLoaded 四个通道名;当前房内游戏桥接已补充这些文档协议的兼容层,便于直接验证真实 H5 当前到底走的是哪一条调用路径。
  • 已新增 BAISHUN 临时调试面板,并封装为独立组件 lib/modules/room_game/views/baishun_debug_panel.dart:面板会展示 entryUrl、桥接注入次数、最近一次 jsCallback、最后一条桥接消息及日志列表,并提供 Reload / Inject / ReplayConfig / Wallet / ClearLogs 操作,专门用于这次房内游戏 H5 真机联调;该面板不是正式功能,后续调试完成后需要整块删除。
  • 已根据 BAISHUN 文档中“code 为一次性参数且唯一”的约束继续收紧客户端下发策略:当前房内游戏页在 bootstrap / pageFinished 阶段只注入桥接方法,不再主动把完整配置反复推给 H5真正含 code 的配置现在只会在 H5 明确调用 getConfig 后回一次,避免因为重复下发一次性 code 导致 BAISHUN 后续拉取 sstoken / user_info 失败。调试面板也已补充 ConfigSend 计数和最近一次配置摘要,便于真机确认是否存在重复下发。
  • 已继续增强 BAISHUN 临时调试面板的排障能力:当前已在 H5 侧临时注入 fetch / XMLHttpRequest / window.onerror / unhandledrejection 日志回传,相关失败请求、状态码和响应片段会直接显示在 Recent Logs,专门用于继续定位当前 get user info failed 是否发生在百顺后续拉取 sstoken / user_info 的网络链路;这同样只是联调用临时能力,后续调试完成后需要删除。
  • 已根据《BAISHUN 游戏对接文档 1.3.2》补齐 language 字段兼容:文档表 3 要求传给游戏的语言值是字符串数字枚举(如英文 2、阿语 7、土耳其语 10),而当前客户端真实下发值曾是 en;现已在房内游戏页发送配置前统一转换为 BAISHUN 约定值,并把 raw -> normalized 结果记录进调试日志,便于继续确认 get user info failed 是否由协议字段不匹配导致。
  • 已根据最新真机日志继续收敛 BAISHUN 问题边界:当前调试面板已确认 ConfigSend=1language=2(raw=en)getConfig_1_completegameLoaded_1_complete 都已正常走通,说明 Flutter 侧桥接、语言字段和“一次性 code 重复下发”目前都不是主要矛盾;同时 Recent Logs 里未出现浏览器侧 fetch/XHR 失败,但仍出现 get user info failed 弹窗与 H5 内部 InvalidStateError,因此当前更倾向于 BAISHUN 服务端继续调用商户侧 /v1/api/get_sstoken / /v1/api/get_user_info 的链路失败或其返回内容不符合文档要求H5 页面里的报错更像后续症状而非首因。
  • 已继续增强 BAISHUN 临时调试能力用于下一轮真机排障:当前除了原有 fetch/XHR/window.onerror/unhandledrejection 外,还会额外回传 H5 的 console.log/info/warn/error 与资源加载失败信息,并把本地日志保留条数放宽到 80 条、面板日志区适当加高;这些都只服务当前联调,不属于正式功能,问题定位完成后需要一并删除。
  • 已通过 2026-04-16 真机最新控制台日志拿到更直接的证据H5 侧明确打印了 {"msgId":"Connect","errCode":1015,"errMsg":"1015-SSToken接口错误","data":null},并伴随 WebSocket Error / WebSocket Close / Reconnect 日志,说明当前问题已可进一步收敛为百顺游戏在建立长连接前获取 SSToken 失败;这表明主要矛盾已经不在 Flutter 容器桥接,而在百顺服务端访问商户侧 /v1/api/get_sstoken 的链路、该接口的业务结果、或其返回格式与文档约定不一致。
  • 创建并持续维护进度跟踪文件。
  • 已继续排查语言房 gift 动画链路:确认送礼后会同时走本地房间消息、滚屏礼物条和大额礼物全局飘屏三条路径,并修复动画管理器在控制器尚未绑定完成时提前消费队列导致后续动画不再播放的问题。
  • 已定位并修复语言房礼物飘屏资源引用错误:代码里误写 sc_icon_gift_flosc_bg / sc_icon_luck_gift_flosc_*,实际资源文件名为 float,导致点击送礼后飘屏背景图加载失败,相关动画无法正常显示。
  • 已继续定位语言房礼物特效不播放的根因:后端礼物配置返回的特效标记为 ANIMATION,而前端历史代码只识别拼写错误的 ANIMSCION,导致送礼后直接跳过全屏/半屏特效播放;现已改为同时兼容两种标记。
  • 已继续优化语言房礼物特效播放性能:将礼物资源预热改为“后台串行预加载 + 选中礼物高优先级预加载 + 播放阶段复用同一解码/下载任务”,避免送礼时重复下载或重复解码同一个 .svga/.mp4;同时为全屏播放器增加 RepaintBoundary,并改为按原始比例渲染、关闭越界绘制,减少整页重绘和过度放大带来的卡顿。
  • 已继续优化礼物二级页首屏与翻页加载体验:礼物配置改为会话内复用,避免每次打开礼物面板都重新拉列表;首屏与翻页页卡增加整页骨架态,页面图片预热改为“当前页并发预热 + 下一页提前预热”,并增加“最短可感知骨架时长 + 最长骨架保护窗口”,避免整页骨架一闪而过;超过上限后再切换到真实卡片,并让单个礼物继续使用小 loading 占位补齐,整体交互对齐常见直播 app 的礼物面板体验。
  • 已修复首页房间封面显示链路:兼容接口 roomCover/cover 双字段,并补齐编辑房间成功后的封面内存回写。
  • 已继续修复房间设置保存封面后丢失的问题:房间保存接口改为兼容提交 id/roomIdroomCover/cover,并在保存成功后优先保留刚提交的封面、名称、公告,避免被空响应覆盖。
  • 已新增按房间 ID 的本地房间资料缓存兜底:首页、我的房间、历史/关注、搜索和再次进入房间都会优先回填最近一次本地保存的封面;同时已将房间封面空态图替换为新的 sc_no_data.png
  • 已确认房间封面上传接口与 PUT /room/profile 保存接口都返回了正确图片地址,并修正首页/重进房间的取值优先级,避免被列表接口短时间返回的旧封面或无效封面重新覆盖。
  • 已继续完善个人主页头像与滚动表现:顶部小头像保持纯头像,主头像恢复头像框叠加;同时将个人主页背景并入可滚动 header修复滑动时内容与背景不同步的问题。
  • 已进一步统一头像框方案:共享头像组件改为“头像居中 + 头像框包边”结构,并对这类静态头像框资源增加白底去除处理,不再简单把头像框整张盖在头像上;个人主页主头像与房间麦位头像现已同步采用这一展示方式。
  • 已修复静态头像框透明区域误变黑的问题:调整头像框白底透明化公式后,进入房间时不再出现只有头像附近有颜色、其余房间背景被黑块覆盖的现象。
  • 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。
  • 已将个人主页首屏占位升级为骨架屏进入个人页时会先按真实版式展示头像、昵称、计数区、Tab 和资料内容的骨架块,不再先露默认背景等待约 1 秒。
  • 已为首页 Party 页补齐首屏骨架屏:顶部 H5 榜单现按三列奖杯卡位展示头像/标题骨架,下方房间区改为双列房卡骨架,并采用深绿底 + 金色描边的项目内风格;同时拆分榜单与房间列表加载态,仅在“首屏无内容加载”时展示骨架,避免下拉刷新时整页闪烁。
  • 已为首页 My 板块补齐首屏骨架屏:My room 顶部房间卡片改为独立加载态,避免首屏把“正在加载我的房间”误显示成“去创建房间”;Recent/Followed 两个子页也已改成同风格双列房卡骨架,进入 tab 时会先展示完整结构占位,再按真实数据切换。
  • 已为 Me 入口下的 Store / Bag 页面补齐首屏骨架屏:四类 store 商品页和四类 bag 物品页在“首屏空数据加载”阶段都会先展示同风格的三列卡片骨架,不再只显示小菊花;同时已为商品/物品名称补齐宽度约束与省略号处理,避免超长文案顶出卡片。
  • 已将通用图片加载占位从静态 loading 图片升级为代码绘制的环形转点组件:礼物二级页单卡封面、Store / Bag 商品图以及复用共享图片组件的网络图,在“加载中”阶段都会显示动态转圈效果;而图片失败或 no data 时仍保持原有占位图,不混淆加载态与空态。
  • 已继续细化首页房卡体验:My 板块房卡骨架数量已从 4 个补齐到 6 个,与首页 Party 首屏保持一致;同时将房卡右下角原本的静态绿色“直播中”图片替换为代码驱动的三段式动态音频条,首页、我的、搜索结果和房间浮窗现都会展示跳动效果,不再依赖静态资源图;最新已把动效调整为“单周期完整峰谷 + A/B/C 错峰起伏”的连续波形,观感更接近直播音频电平。
  • 已继续微调首页房卡信息密度:房卡底部房名与在线人数字号已统一调小,和动态音频条更协调;同时已移除 My 板块顶部 my room 标题及其占位,让房间卡片整体上移,对齐更紧凑。
  • 已继续微调首页视觉细节房卡底栏房名再次缩小并轻微上移和国旗、动态音频条、人数的中线更一致首页顶部排行骨架也已简化为“3 个圆形头像 + 1 根文本条”,更贴近真实卡片结构。
  • 已补回首页 Party 顶部 banner 展示链路:顶部现优先显示原有 explore banner并在该分组为空时自动兜底使用首页 home banner避免整块区域消失同时首屏 banner 空数据加载阶段已补充同风格骨架占位,不再直接塌陷为零高度。
  • 已继续微调首页 Party 顶部 banner 体验banner 与下方排行组件之间已补齐接近房卡区的纵向间距;当 banner 只有 1 张时,会关闭无限轮播与横向回弹,左右滑动不再弹回中间。
  • 已继续收窄首页 Party 顶部 banner 骨架样式banner 骨架现改为更干净的整块占位,不再在中间叠加长方形白条,首屏观感更接近真实 banner 的整体加载。
  • 已优化语言房顶部成员入口的点击范围:右上角在线成员区域现已将左侧头像堆叠区与右侧成员图标统一为同一点击热区,用户点击头像区也可直接打开成员列表页。
  • 已继续修复房间封面跨用户不同步问题:当列表或进房返回空封面时,客户端会自动补拉 room/profile/specific 详情并缓存真实封面;同时房主保存房间资料后会向房间内其他用户派发资料刷新消息,避免只有自己能看到新封面、其他用户仍显示 no data
  • 已修复个人主页编辑页头像上传后无变化的问题:上传成功后会先本地回写新头像地址并立即提交;资料更新接口现同时兼容 userAvatar/avatar 字段,且不再被紧接着的旧资料回拉覆盖,返回个人主页后头像会即时更新。
  • 已进一步定位图片 no data 根因并修复客户端加载链:日志确认房间封面详情接口可返回正确公网 media... 地址,但用户头像接口会返回 jvapi.../external/oss/local/... 这类受保护资源地址;共享图片组件现已对这类地址自动补齐鉴权请求头,避免其它用户侧加载时直接失败为 no data
  • 已直接校验头像异常地址的服务端返回:jvapi.../external/oss/local/... 当前会返回 500 local disk file does not exist,并非单纯鉴权缺失;前端现已在编辑资料成功后与本人资料回拉时优先保留刚上传成功的公网头像,避免再次被这类坏地址覆盖,同时修复了房间消息用户资料缓存未真正失效的笔误。
  • 已继续修正个人资料更新接口的提交结构:排查发现注册接口使用嵌套 profile,而资料更新接口此前只发扁平字段;现已改为同时提交 id + 扁平字段 + profile,并在 profile 内同步带上 userAvatar/avatar,用于兼容后端可能按嵌套对象取值的情况。
  • 已按最新联调结果再次收窄个人资料更新参数:日志已证明后端能收到请求但会忽略新头像地址,当前已改为头像相关只提交单一 userAvatar 字段,并移除 avatar/profile 冗余结构,便于继续验证后端是否对字段名或重复参数敏感。
  • 已继续将个人页头像上传链路与通用资料提交拆开:上传成功后当前会单独触发一次仅含 userAvatar 的更新请求,不再混带昵称、性别、年龄、国家等字段,便于排除其它参数对头像更新接口的干扰。
  • 已继续收敛个人主页资料编辑页提交行为:当前已移除资料列表页中的 country 条目及对应跳转/提交逻辑并将昵称、性别、生日、Bio、Hobby、头像改为“改哪项只提交哪项”的增量更新同时修正生日弹窗只会在确认且日期真实变更后才触发提交避免取消或未改动时误发请求。
  • 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。
  • 移除 4 个当前未在业务层直接使用的插件依赖:loading_indicator_view_plussocial_sharing_plusflutter_foreground_taskon_audio_query,并清理相关平台声明。
  • 修补 image_cropper 5.0.1 Android 兼容问题,切换到本地 path 依赖以恢复构建。
  • 完成 Android release 签名回退配置,解决仓库缺少 yumi.jks / yumi_debug.jks 时无法继续本地构建分析的问题。
  • 完成 release 包体分析,确认 universal APK 过大主要由多 ABI 与 Agora/Tencent 原生 so 导致。
  • 去除 Gradle 中与 Flutter 冲突的硬编码 abiFilters,确认 --split-per-abi 可以正常产出分架构 APK。
  • 完成 TinyPNG 环境核查,并接入可执行的 Ruby 批量压缩脚本。
  • 已完成 TinyPNG 第一轮批量压缩,sc_images + assets 总量已从 47.25 MB 降到 22.54 MB
  • 已增强 TinyPNG 脚本,支持断点续跑、单文件目标和失败项重试。
  • 已完成 TinyPNG 网络失败项补跑6 张 SSL_connect 异常图片已全部压缩成功。
  • 已完成图片压缩后的 release 复测,确认分 ABI APK 体积继续显著下降。
  • 已切换 Android release 为真正的瘦身模式:开启 minifyshrinkResourceszipAlign
  • 已完成开启瘦身配置后的 Android release 复测,并接入 split-debug-info 保存 Dart symbols。
  • 已生成统一的 Python 打包脚本 scripts/build_release.py,开始接管 AAB、分 ABI APK 与 iOS 包流程。
  • 已完成 scripts/build_release.py 的 Android / iOS 真机冒烟验证,产物归档目录与 manifest 均已生成。
  • 已按当前需求再次完成正式 AAB 出包。
  • 已定位 Google Play 上传阻塞点:当前 AAB 使用的是 Android Debug 证书签名,不是正式 upload key。
  • 已确认当前项目属于“首次上架的新应用”,后续应走 Play App Signing 首发流程,而不是兼容老签名迁移流程。
  • 已梳理 Google Play 首次上架所需的 Play App Signingupload key、重新打包与提审步骤。
  • 已澄清首次生成 upload keystore 时的“密钥库口令”是本地自行设置的密码,与 Google 提供的 deployment_cert.der 证书文件无关。
  • 已为新应用生成正式 upload keystore,并接入工程的 release 签名配置。
  • 已重新打出正式 AAB,并确认其签名已经从 Android Debug 切换到新的 upload key。
  • 已定位 FOREGROUND_SERVICE_MEDIA_PROJECTION 的来源为 Agora full-screen-sharing 依赖,并开始从主 Manifest 覆盖移除。
  • 已移除 FOREGROUND_SERVICE_MEDIA_PROJECTION 相关声明并重新打出正式 AAB
  • 已按当前需求打出正式 arm64-v8a 单架构 APK
  • 已将新的 google-services.json 替换进工程,准备重新打包验证。

进行中模块

  • 评估 iOS 正式签名参数接入后导出 .ipa 的流程。
  • 继续评估 Agora 扩展 so 与 release shrink 策略是否还能进一步裁剪。
  • 评估 12 张 TinyPNG 不支持解码的特殊 webp 是否需要改格式或换工具处理。

关键技术决策

  • 先做高置信度瘦身优先清理未使用插件、ABI 配置和超大图片,暂不贸然删除核心业务能力。
  • 避开仓库中用户已有未提交改动,只改必要文件并持续把结果写入本文件。
  • 包体分析聚焦 release 产物,不被 build/、iOS 中间产物等本地缓存误导。
  • Android 架构裁剪交给 Flutter 构建命令驱动,不再在 Gradle 中硬编码 abiFilters
  • image_cropper 采用本地 path 依赖加最小补丁策略,避免升级带来的业务回归面。
  • Android 签名配置采用条件回退:正式 keystore 不存在时自动回退到 debug 签名,仅用于本地分析验证。
  • 图片压缩优先走 TinyPNG以尽量保持画质稳定失败项按原因分流处理。
  • TinyPNG 的 SSL_connect 视为可重试网络抖动;Image could not be decoded 视为 TinyPNG 对特殊/动图 WebP 的能力边界,先记录不阻塞其余压缩。

已改动文件

  • 需求进度.md
  • lib/shared/data_sources/models/enum/sc_gift_type.dart
  • lib/shared/tools/sc_network_image_utils.dart
  • lib/shared/tools/sc_gift_vap_svga_manager.dart
  • lib/services/general/sc_app_general_manager.dart
  • lib/ui_kit/widgets/room/floating/floating_gift_screen_widget.dart
  • lib/ui_kit/widgets/room/floating/floating_luck_gift_screen_widget.dart
  • lib/services/room/rc_room_manager.dart
  • lib/services/audio/rtc_manager.dart
  • lib/modules/room/edit/room_edit_page.dart
  • lib/modules/user/edit/edit_user_info_page2.dart
  • lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart
  • lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart
  • lib/shared/tools/sc_room_profile_cache.dart
  • lib/shared/business_logic/models/res/room_res.dart
  • lib/shared/business_logic/models/res/follow_room_res.dart
  • lib/shared/business_logic/models/res/my_room_res.dart
  • lib/shared/business_logic/models/res/sc_edit_room_info_res.dart
  • lib/shared/business_logic/models/res/join_room_res.dart
  • lib/ui_kit/components/sc_compontent.dart
  • lib/ui_kit/components/sc_float_ichart.dart
  • lib/modules/index/index_page.dart
  • lib/modules/room/seat/sc_seat_item.dart
  • lib/ui_kit/widgets/room/room_head_widget.dart
  • lib/ui_kit/widgets/room/room_game_bottom_sheet.dart
  • lib/ui_kit/widgets/svga/sc_svga_asset_widget.dart
  • lib/modules/room/detail/room_detail_page.dart
  • lib/modules/home/popular/party/sc_home_party_page.dart
  • lib/modules/home/popular/mine/sc_home_mine_skeleton.dart
  • lib/modules/home/popular/follow/sc_room_follow_page.dart
  • lib/modules/home/popular/history/sc_room_history_page.dart
  • lib/modules/home/popular/mine/sc_home_mine_page.dart
  • lib/ui_kit/widgets/room/room_live_audio_indicator.dart
  • lib/modules/search/sc_search_page.dart
  • lib/modules/store/headdress/store_headdress_page.dart
  • lib/modules/store/mountains/store_mountains_page.dart
  • lib/modules/store/theme/store_theme_page.dart
  • lib/modules/store/chatbox/store_chatbox_page.dart
  • lib/modules/user/my_items/headdress/bags_headdress_page.dart
  • lib/modules/user/my_items/mountains/bags_mountains_page.dart
  • lib/modules/user/my_items/chatbox/bags_chatbox_page.dart
  • lib/modules/user/my_items/theme/bags_theme_page.dart
  • lib/ui_kit/widgets/store/store_bag_page_helpers.dart
  • sc_images/general/sc_no_data.png
  • .gitignore
  • android/key.properties
  • pubspec.yaml
  • pubspec.lock
  • lib/ui_kit/components/dialog/dialog.dart
  • android/app/src/main/AndroidManifest.xml
  • android/app/google-services.json
  • ios/Runner/Info.plist
  • scripts/tinypng_batch.rb
  • scripts/build_release.py
  • tinypng-progress.json
  • tinypng-report.json
  • local_packages/image_cropper-5.0.1-patched/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java
  • local_packages/image_cropper-5.0.1-patched/
  • android/app/build.gradle.kts
  • android/app/upload-keystore.jks
  • android/app/upload_certificate.pem
  • build/symbols/android/
  • build/symbols/ios/
  • dist/release/smoke-android/
  • dist/release/smoke-ios/
  • sc_images/
  • assets/

已验证结果

  • 已通过代码与资源目录交叉核对确认:房内礼物飘屏当前报错 Unable to load asset: "sc_images/room/sc_icon_gift_flosc_bg.png" 的根因是资源名拼写错误,而不是消息未下发;工程内实际存在的资源文件为 sc_icon_gift_float_bg.pngsc_icon_luck_gift_float_bg1~5.pngsc_icon_luck_gift_float_n_bg.png
  • 已确认当前“点击赠送没有动画”至少包含一类前端资源问题:大额礼物/幸运礼物飘屏 widget 已进入渲染,但因为背景图 asset 路径写错而无法显示;全屏 SVGA/VAP 特效是否播放仍取决于具体礼物是否带有 giftSourceUrl,且 special 包含 ANIMSCION/ANIMATION/GLOBAL_GIFT 之一。
  • 已完成一轮针对卡顿的代码级优化:礼物列表现在只会对真正需要全屏特效的礼物做受控预热;用户选中礼物时会立即高优先级预热该礼物;播放器播放时优先复用内存里的 MovieEntity 或同一路径的在途任务,不再重复发起网络下载/重复解码;渲染层也已改为独立重绘边界以减少房间整页跟随重绘。
  • 已通过 GiftFX 调试日志确认:示例礼物 阿拉伯舞蹈 实际返回 giftSourceUrl=.svgaspecial=ANIMATION,此前前端因为不识别该标记而输出 skip local play because special does not include ANIMSCION/GLOBAL_GIFT;本轮已补齐 ANIMATION 兼容判断。
  • 礼物二级页当前已按“整页骨架 + 当前页并发预热 + 下一页提前预热 + 单卡小 loading 占位”的方式重构首屏加载策略:首次进入和翻页时不会再直接暴露 noData 图,且整页骨架会保留一个可感知时长,不再一闪而过;当前页礼物封面会并发预热而不是等每个格子各自串行体感加载。
  • 当前工作区存在用户已有未提交改动,后续处理均避开覆盖。
  • 首页 party/follow/history/mine 以及房间详情使用的房间封面模型已兼容 roomCovercover 两种返回字段,上传封面后不会再因为字段名不一致掉回空占位。
  • 房间设置保存封面时,前端现在会同时兼容提交和读取两套字段,并在 PUT /room/profile 响应或紧接着的房间详情刷新返回空封面时保留刚上传的封面,不再被空值冲掉。
  • 已新增本地房间封面兜底缓存:即使服务端短时间仍返回空封面,首页列表、我的房间、搜索结果、房间头部、房间详情和再次进入房间也会继续显示最近一次本地成功保存的封面。
  • 已通过实际请求日志确认:POST /external/oss/uploadPUT /room/profile 和随后 GET /room/profile/specific 都已返回正确封面 URL本轮问题的根因已收敛为首页/列表链路仍可能使用接口短时间回传的旧值,因此本地成功保存的封面现已提升为更高优先级。
  • 房间封面空态图已切换为新的 sc_images/general/sc_no_data.png,其资源尺寸为 288x288
  • 房间资料卡与个人主页使用的用户资料链路已重新对齐:个人页现兼容 avatar 别名;顶部小头像不叠加头像框,但主头像会继续叠加头像框资源,且个人页背景已随 header 一起滚动,不再出现内容先滑动、背景停留不动的现象。
  • 头像框显示逻辑已改为“框在外、头像在内”的常见样式;针对当前这类带白底的静态头像框资源,已增加白底透明化处理,因此个人主页主头像与房间麦位头像都不会再留下明显白边。
  • 静态头像框的透明区域现已保持透明,不会再被错误算成黑色;房间麦位头像、个人主页主头像和其它复用 head() 的头像组件进入房间后不会再出现大块黑底遮罩。
  • 个人主页现在在数据加载前也会先展示默认背景和底色不会再闪黑“About me / Giftwall” 内容区顶部边缘已改为直角,不再额外出现一个圆角缺口。
  • 个人主页当前已接入贴合页面结构的骨架屏,占位期间不会先看到背景图单独显示;资料接口返回后再无缝切到真实内容,首屏观感更平滑。
  • 首页 Party 页当前已接入贴合真实版式的骨架屏:进入首页首屏时会先看到三张排行卡位与双列房卡占位,骨架配色已跟随页面深绿+鎏金主视觉,不再只显示居中的白色 loading;且排行榜与房间区会按各自请求结果独立切换,先返回的区域会先显示真实内容。
  • 首页 My 板块当前已接入贴合真实版式的骨架屏:顶部 My room 现区分“首次加载中”和“确实未创建房间”,不会再先闪出创建房间卡;Recent/Followed 在首屏空数据加载时也会先展示双列房卡骨架,不再只显示小菊花或局部空白。
  • Me 入口下的 Store / Bag 页面当前也已接入深绿主题的三列卡片骨架:首次进入并且列表尚未返回时,会先显示与正式卡片比例一致的占位;接口返回后再切到真实商品/物品卡片。storebag 卡片标题现都带宽度约束和单行省略,类似截图里的长名称不会再横向撑破布局。
  • 图片加载态与空态当前已拆分:共享网络图组件和礼物二级页单卡封面加载时会显示代码绘制的环形转点,不再继续复用 sc_icon_loading.png;若图片失败或确实无数据,仍旧保留原先那张占位图,避免把 loadingno data 看成同一种状态。
  • 首页与搜索等房卡右下角的在线状态图标当前已改为代码绘制的动态音频条:三根绿条会按不同相位走完整的波峰波谷周期,错峰连续起伏,视觉上更接近直播/语聊房的实时状态;My 板块骨架数量也已与 Party 页对齐为 6 个。
  • 首页房卡底部信息当前已进一步收紧:房名和人数文字比上一版更小,底栏与动态音频条的视觉重心更一致;My 页顶部 my room 标题也已移除,下方卡片和 tab 会自然上移填充。
  • 首页顶部排行骨架当前已进一步收窄为单条标题占位,不再保留两条文本骨架;房卡底栏房名也已再次缩一号并做轻微上移,对齐更贴近设计稿观感。
  • 目录体积排查显示:build/4.7G.dart_tool/347Mios/250Msc_images/48Mlocal_packages/6.1M
  • 当前包体过大的主要原因已经确认:
  • universal APK 带入了 arm64-v8aarmeabi-v7ax86_64 三套 ABI。
  • Agora 与腾讯 IM 原生 so 很大,其中 libagora-rtc-sdk.so 合计约 79.4MBlibImSDK.so 合计约 10.2MB
  • 资源目录中 sc_images/indexsc_images/roomsc_images/personsc_images/splash 是最主要的大图来源。
  • flutter build apk --release --target-platform android-arm64 --analyze-size 已成功执行,当前 universal release APK 约 271MB
  • flutter build apk --release --split-per-abi 已成功执行,压缩前分架构 APK 约为:
  • app-arm64-v8a-release.apk161.4MB
  • app-armeabi-v7a-release.apk138.8MB
  • app-x86_64-release.apk148.6MB
  • loading_indicator_view_plussocial_sharing_plusflutter_foreground_taskon_audio_query 已从当前依赖链路中移除。
  • 当前图片资源统计为 412TinyPNG 两轮处理后总量已从 47.25 MB 降到 22.27 MB,累计节省约 24.99 MB
  • 当前 TinyPNG 已成功压缩 400 张图片,剩余失败 12 张。
  • 当前压缩收益最大的图片包括:
  • sc_images/person/sc_icon_edit_userinfo_bg.png2640776 -> 436823,节省约 2.10 MB
  • sc_images/splash/sc_splash.png2373335 -> 379804,节省约 1.90 MB
  • sc_images/room/sc_icon_room_defaut_bg.png2243222 -> 704696,节省约 1.47 MB
  • sc_images/index/sc_icon_index_bg.png1422169 -> 420635,节省约 0.96 MB
  • 失败项已分型:
  • 12 张为 TinyPNG 无法解码的特殊/动图 webp
  • 当前 TinyPNG 进度与统计文件已落盘:tinypng-progress.jsontinypng-report.json
  • 图片压缩后再次执行 flutter build apk --release --split-per-abi,当前分架构 APK 已下降到:
  • app-arm64-v8a-release.apk135.2MB,比压缩前少 26.2MB
  • app-armeabi-v7a-release.apk112.6MB,比压缩前少 26.2MB
  • app-x86_64-release.apk122.4MB,比压缩前少 26.2MB
  • 可以确认:这轮图片压缩带来的资源收益已经真实反映到最终 APK 体积中。
  • 在开启 minifyshrinkResourceszipAlign 并追加 --split-debug-info=build/symbols/androidAndroid release 继续下降为:
  • app-release.aab181.8MB
  • app-arm64-v8a-release.apk128.7MB,比上一轮 135.2MB 再少 6.5MB
  • app-armeabi-v7a-release.apk105.8MB,比上一轮 112.6MB 再少 6.8MB
  • app-x86_64-release.apk115.8MB,比上一轮 122.4MB 再少 6.6MB
  • build/symbols/android 已生成 3 份符号文件,总计约 14MB,后续可用于 Dart 堆栈还原。
  • scripts/build_release.py --platform android --output-dir dist/release/smoke-android 已验证通过,归档结果为:
  • android/google-play/ 下输出 yumi-v1.0.0-b1-google-play.aab 与 Dart symbols
  • android/local/ 下输出 arm64-v8a.apkarmeabi-v7a.apk
  • android/testing/ 下单独输出 x86_64-test.apk
  • 同时生成 build_manifest.json,已记录产物路径、大小和 sha256
  • scripts/build_release.py --platform ios --output-dir dist/release/smoke-ios 已验证通过,当前无签名模式下输出:
  • ios/archive/yumi-v1.0.0-b1.xcarchive
  • ios/symbols/app.ios-arm64.symbols
  • 当前 iOS 无签名构建会跳过 .ipa 导出;如需正式 .ipa,脚本需追加 --ios-codesign
  • 已将 dist/ 加入 .gitignore,避免打包产物持续污染工作区状态
  • 本轮按需执行 flutter build appbundle --release --split-debug-info=build/symbols/android 已成功,产物为:
  • build/app/outputs/bundle/release/app-release.aab
  • 当前 AAB 体积约 181.8MB
  • 本地校验 app-release.aab 证书后确认:
  • 证书所有者为 C=US, O=Android, CN=Android Debug
  • 当前 AAB 的 SHA16B:72:BF:6F:D1:7A:F2:99:CD:F3:14:EE:18:A6:29:67:F8:05:E6:B4
  • 这说明当前 release 构建仍在回退使用 debug signing无法直接作为 Google Play 正式发布包
  • 已生成新的 upload 证书文件:
  • android/app/upload-keystore.jks
  • android/app/upload_certificate.pem
  • 新 upload key 当前指纹为:
  • SHA1: C1:80:AF:BF:6E:1F:F2:F4:49:58:72:87:A2:BC:54:07:E7:D2:D9:A3
  • SHA256: 81:D6:3A:43:31:FA:83:87:0E:C3:5B:52:B8:C1:05:7C:3A:32:EC:24:37:0B:11:E4:2F:8C:62:4E:D4:AC:0C:3B
  • release 构建已改为从 android/key.properties 读取正式 upload keystore不再依赖当前的 debug 回退配置
  • flutter build appbundle --release --split-debug-info=build/symbols/android 已重新执行,当前正式产物仍为:
  • build/app/outputs/bundle/release/app-release.aab
  • 当前 AAB 体积约 181.8MB
  • 重新校验该 AAB 后,签名者已变为:
  • CN=Yumi Upload, OU=Mobile, O=Yumi, L=Shanghai, ST=Shanghai, C=CN
  • deployment_cert.der 已核实为 Google 侧 app signing 证书,指纹为:
  • SHA1: 3F:E1:78:DC:C3:29:4F:74:20:DF:27:54:CF:AF:46:46:D6:A1:94:78
  • SHA256: F9:BF:D0:89:F9:D7:FD:B2:53:24:A7:DB:59:E1:52:6F:54:A3:93:95:4E:88:7F:1C:4B:D6:03:66:E7:1A:1F:85
  • 已确认当前存在两套不同证书:
  • 本地 upload key:用于你上传 AAB 到 Google Play
  • Google app signing key:用于 Google Play 最终给用户分发签名包
  • FOREGROUND_SERVICE_MEDIA_PROJECTION 在当前 release 合并报告中的来源已确认是 io.agora.rtc:full-screen-sharing:4.5.2
  • 该依赖同时还带入了:
  • io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenCaptureAssistantActivity
  • io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenSharingService
  • 主 Manifest 已添加 tools:node="remove" 覆盖规则,准备重新打包验证最终合并结果
  • 重新打包后release manifest merger 报告已显示:
  • uses-permission#android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION
  • REJECTED from [io.agora.rtc:full-screen-sharing:4.5.2]
  • 当前 release 中已不再检测到以下声明:
  • android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION
  • io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenCaptureAssistantActivity
  • io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenSharingService
  • 新的正式产物已重新生成:
  • build/app/outputs/bundle/release/app-release.aab
  • 当前 AAB 体积仍约 181.8MB
  • 新 AAB 证书仍为本地 upload key
  • SHA1: C1:80:AF:BF:6E:1F:F2:F4:49:58:72:87:A2:BC:54:07:E7:D2:D9:A3
  • 本轮按需执行 flutter build apk --release --split-per-abi --target-platform android-arm64 --split-debug-info=build/symbols/android 已成功,产物为:
  • build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
  • 当前 arm64-v8a APK 体积约 128.7MB
  • 已核对新旧 google-services.json
  • 原文件与新文件都匹配包名 com.org.yumi
  • 下载文件已成功覆盖到 android/app/google-services.json
  • 当前工程内文件 SHA256 为:
  • a1706496a01f74d27e6c60598144cecc978934b02a87567e1ced887b5b0185d5

已知问题

  • ios/Podfile.lock 还保留旧插件记录,因为本轮未执行 pod install;但 Flutter 当前依赖链路已经不再包含已移除插件。
  • universal APK 仍然会非常大;当前正确发包方式应优先使用 --split-per-abi 或直接产出 aab
  • 仍有 12webp 被 TinyPNG 拒绝解码,推测是动画或特殊编码格式,不能继续直接走 TinyPNG 常规压缩。
  • split-debug-info 会把 Dart symbols 额外落到构建目录,发包时需要和正式包一起留档,不能丢。
  • iOS 当前验证走的是 --no-codesign,因此只产出 .xcarchive,不会自动导出可直接分发的 .ipa
  • Flutter/Xcode 当前给出两条 iOS 提醒:
  • 未来 iOS 版本将要求 UIScene 生命周期支持
  • Launch image 仍是默认占位资源,提交前建议替换
  • android/key.properties 保存了本地 upload keystore 口令,必须自行妥善备份,且不要提交到仓库
  • 由于已显式移除 Agora 本地屏幕共享相关组件,如果业务后续要启用“屏幕共享/录屏推流”,需要再单独恢复相关 manifest 声明

下一步要做什么

  • 将新的 AAB 上传到 Play Console并在首发流程中继续使用 Google 管理的 app signing key。
  • 在需要配置 Google 登录、Firebase、支付等生产环境指纹时优先登记 Google 的 deployment_cert.der 指纹;如平台要求上传者证书,再额外登记本地 upload key 指纹。
  • 如果需要正式 iOS 分发包,为 scripts/build_release.py 补入 --ios-codesign 所需签名参数并导出 .ipa
  • 继续评估 Agora 扩展库是否都需要,优先核查 lip syncspatial audioclear visionsegmentationface capture 等扩展 so 能否裁剪。
  • 决定是否处理剩余 12 张特殊 webp:改格式、换压缩工具,或维持现状。