chatapp3-flutter/需求进度.md
2026-04-14 17:21:05 +08:00

243 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 需求进度
## 当前总目标
- 控制当前 Flutter Android 发包体积,持续定位冗余组件、超大资源和不合理构建配置,并把每一步处理结果落盘记录。
## 已完成模块
- 创建并持续维护进度跟踪文件。
- 已继续排查语言房 gift 动画链路:确认送礼后会同时走本地房间消息、滚屏礼物条和大额礼物全局飘屏三条路径,并修复动画管理器在控制器尚未绑定完成时提前消费队列导致后续动画不再播放的问题。
- 已定位并修复语言房礼物飘屏资源引用错误:代码里误写 `sc_icon_gift_flosc_bg` / `sc_icon_luck_gift_flosc_*`,实际资源文件名为 `float`,导致点击送礼后飘屏背景图加载失败,相关动画无法正常显示。
- 已继续定位语言房礼物特效不播放的根因:后端礼物配置返回的特效标记为 `ANIMATION`,而前端历史代码只识别拼写错误的 `ANIMSCION`,导致送礼后直接跳过全屏/半屏特效播放;现已改为同时兼容两种标记。
- 已继续优化语言房礼物特效播放性能:将礼物资源预热改为“后台串行预加载 + 选中礼物高优先级预加载 + 播放阶段复用同一解码/下载任务”,避免送礼时重复下载或重复解码同一个 `.svga/.mp4`;同时为全屏播放器增加 `RepaintBoundary`,并改为按原始比例渲染、关闭越界绘制,减少整页重绘和过度放大带来的卡顿。
- 已修复首页房间封面显示链路:兼容接口 `roomCover/cover` 双字段,并补齐编辑房间成功后的封面内存回写。
- 已继续修复房间设置保存封面后丢失的问题:房间保存接口改为兼容提交 `id/roomId``roomCover/cover`,并在保存成功后优先保留刚提交的封面、名称、公告,避免被空响应覆盖。
- 已新增按房间 ID 的本地房间资料缓存兜底:首页、我的房间、历史/关注、搜索和再次进入房间都会优先回填最近一次本地保存的封面;同时已将房间封面空态图替换为新的 `sc_no_data.png`
- 已确认房间封面上传接口与 `PUT /room/profile` 保存接口都返回了正确图片地址,并修正首页/重进房间的取值优先级,避免被列表接口短时间返回的旧封面或无效封面重新覆盖。
- 已继续完善个人主页头像与滚动表现:顶部小头像保持纯头像,主头像恢复头像框叠加;同时将个人主页背景并入可滚动 header修复滑动时内容与背景不同步的问题。
- 已进一步统一头像框方案:共享头像组件改为“头像居中 + 头像框包边”结构,并对这类静态头像框资源增加白底去除处理,不再简单把头像框整张盖在头像上;个人主页主头像与房间麦位头像现已同步采用这一展示方式。
- 已修复静态头像框透明区域误变黑的问题:调整头像框白底透明化公式后,进入房间时不再出现只有头像附近有颜色、其余房间背景被黑块覆盖的现象。
- 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。
- 已将个人主页首屏占位升级为骨架屏进入个人页时会先按真实版式展示头像、昵称、计数区、Tab 和资料内容的骨架块,不再先露默认背景等待约 1 秒。
- 已优化语言房顶部成员入口的点击范围:右上角在线成员区域现已将左侧头像堆叠区与右侧成员图标统一为同一点击热区,用户点击头像区也可直接打开成员列表页。
- 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。
- 移除 4 个当前未在业务层直接使用的插件依赖:`loading_indicator_view_plus``social_sharing_plus``flutter_foreground_task``on_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` 为真正的瘦身模式:开启 `minify``shrinkResources``zipAlign`
- 已完成开启瘦身配置后的 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 Signing``upload 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_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/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/ui_kit/widgets/room/room_head_widget.dart`
- `lib/modules/room/detail/room_detail_page.dart`
- `lib/modules/home/popular/party/sc_home_party_page.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/modules/search/sc_search_page.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.png``sc_icon_luck_gift_float_bg1~5.png``sc_icon_luck_gift_float_n_bg.png`
- 已确认当前“点击赠送没有动画”至少包含一类前端资源问题:大额礼物/幸运礼物飘屏 widget 已进入渲染,但因为背景图 asset 路径写错而无法显示;全屏 `SVGA/VAP` 特效是否播放仍取决于具体礼物是否带有 `giftSourceUrl`,且 `special` 包含 `ANIMSCION/ANIMATION/GLOBAL_GIFT` 之一。
- 已完成一轮针对卡顿的代码级优化:礼物列表现在只会对真正需要全屏特效的礼物做受控预热;用户选中礼物时会立即高优先级预热该礼物;播放器播放时优先复用内存里的 `MovieEntity` 或同一路径的在途任务,不再重复发起网络下载/重复解码;渲染层也已改为独立重绘边界以减少房间整页跟随重绘。
- 已通过 GiftFX 调试日志确认:示例礼物 `阿拉伯舞蹈` 实际返回 `giftSourceUrl=.svga``special=ANIMATION`,此前前端因为不识别该标记而输出 `skip local play because special does not include ANIMSCION/GLOBAL_GIFT`;本轮已补齐 `ANIMATION` 兼容判断。
- 当前工作区存在用户已有未提交改动,后续处理均避开覆盖。
- 首页 `party/follow/history/mine` 以及房间详情使用的房间封面模型已兼容 `roomCover``cover` 两种返回字段,上传封面后不会再因为字段名不一致掉回空占位。
- 房间设置保存封面时,前端现在会同时兼容提交和读取两套字段,并在 `PUT /room/profile` 响应或紧接着的房间详情刷新返回空封面时保留刚上传的封面,不再被空值冲掉。
- 已新增本地房间封面兜底缓存:即使服务端短时间仍返回空封面,首页列表、我的房间、搜索结果、房间头部、房间详情和再次进入房间也会继续显示最近一次本地成功保存的封面。
- 已通过实际请求日志确认:`POST /external/oss/upload``PUT /room/profile` 和随后 `GET /room/profile/specific` 都已返回正确封面 URL本轮问题的根因已收敛为首页/列表链路仍可能使用接口短时间回传的旧值,因此本地成功保存的封面现已提升为更高优先级。
- 房间封面空态图已切换为新的 `sc_images/general/sc_no_data.png`,其资源尺寸为 `288x288`
- 房间资料卡与个人主页使用的用户资料链路已重新对齐:个人页现兼容 `avatar` 别名;顶部小头像不叠加头像框,但主头像会继续叠加头像框资源,且个人页背景已随 header 一起滚动,不再出现内容先滑动、背景停留不动的现象。
- 头像框显示逻辑已改为“框在外、头像在内”的常见样式;针对当前这类带白底的静态头像框资源,已增加白底透明化处理,因此个人主页主头像与房间麦位头像都不会再留下明显白边。
- 静态头像框的透明区域现已保持透明,不会再被错误算成黑色;房间麦位头像、个人主页主头像和其它复用 `head()` 的头像组件进入房间后不会再出现大块黑底遮罩。
- 个人主页现在在数据加载前也会先展示默认背景和底色不会再闪黑“About me / Giftwall” 内容区顶部边缘已改为直角,不再额外出现一个圆角缺口。
- 个人主页当前已接入贴合页面结构的骨架屏,占位期间不会先看到背景图单独显示;资料接口返回后再无缝切到真实内容,首屏观感更平滑。
- 目录体积排查显示:`build/``4.7G``.dart_tool/``347M``ios/``250M``sc_images/``48M``local_packages/``6.1M`
- 当前包体过大的主要原因已经确认:
- universal APK 带入了 `arm64-v8a``armeabi-v7a``x86_64` 三套 ABI。
- Agora 与腾讯 IM 原生 so 很大,其中 `libagora-rtc-sdk.so` 合计约 `79.4MB``libImSDK.so` 合计约 `10.2MB`
- 资源目录中 `sc_images/index``sc_images/room``sc_images/person``sc_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.apk``161.4MB`
- `app-armeabi-v7a-release.apk``138.8MB`
- `app-x86_64-release.apk``148.6MB`
- `loading_indicator_view_plus``social_sharing_plus``flutter_foreground_task``on_audio_query` 已从当前依赖链路中移除。
- 当前图片资源统计为 `412`TinyPNG 两轮处理后总量已从 `47.25 MB` 降到 `22.27 MB`,累计节省约 `24.99 MB`
- 当前 TinyPNG 已成功压缩 `400` 张图片,剩余失败 `12` 张。
- 当前压缩收益最大的图片包括:
- `sc_images/person/sc_icon_edit_userinfo_bg.png``2640776 -> 436823`,节省约 `2.10 MB`
- `sc_images/splash/sc_splash.png``2373335 -> 379804`,节省约 `1.90 MB`
- `sc_images/room/sc_icon_room_defaut_bg.png``2243222 -> 704696`,节省约 `1.47 MB`
- `sc_images/index/sc_icon_index_bg.png``1422169 -> 420635`,节省约 `0.96 MB`
- 失败项已分型:
- `12` 张为 TinyPNG 无法解码的特殊/动图 `webp`
- 当前 TinyPNG 进度与统计文件已落盘:`tinypng-progress.json``tinypng-report.json`
- 图片压缩后再次执行 `flutter build apk --release --split-per-abi`,当前分架构 APK 已下降到:
- `app-arm64-v8a-release.apk``135.2MB`,比压缩前少 `26.2MB`
- `app-armeabi-v7a-release.apk``112.6MB`,比压缩前少 `26.2MB`
- `app-x86_64-release.apk``122.4MB`,比压缩前少 `26.2MB`
- 可以确认:这轮图片压缩带来的资源收益已经真实反映到最终 APK 体积中。
- 在开启 `minify``shrinkResources``zipAlign` 并追加 `--split-debug-info=build/symbols/android`Android release 继续下降为:
- `app-release.aab``181.8MB`
- `app-arm64-v8a-release.apk``128.7MB`,比上一轮 `135.2MB` 再少 `6.5MB`
- `app-armeabi-v7a-release.apk``105.8MB`,比上一轮 `112.6MB` 再少 `6.8MB`
- `app-x86_64-release.apk``115.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.apk``armeabi-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 的 `SHA1``6B: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`
- 仍有 `12``webp` 被 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 sync``spatial audio``clear vision``segmentation``face capture` 等扩展 so 能否裁剪。
- 决定是否处理剩余 12 张特殊 `webp`:改格式、换压缩工具,或维持现状。