CASE STUDY / MOBILE MEDIA
MusicFy
01 / MOBILE PRODUCT SCOPE
为什么从播放链路开始设计移动应用
MusicFy 的重点不是把一个音乐列表搬到手机上,而是从原生 React Native 环境开始,处理播放器、导航、认证、用户内容和设备文件系统之间的边界。项目需要让用户完成注册、邮箱验证、上传音乐、管理播放列表、收藏、查看收听历史和关注用户,同时保持播放控件能够跨页面持续工作。
- 在不依赖 Expo 的前提下完成 React Native 原生模块接入
- 让播放器、队列、迷你播放器和系统媒体控制跨页面协同工作
- 区分 Redux 全局状态、React Query 服务端状态与设备文件缓存
- 覆盖认证、音乐上传、播放列表、收藏、历史和关注等用户内容流程
02 / PLAYBACK FLOW
一首音乐如何从内容 API 走到设备播放
- 01
Authenticate
应用从 AsyncStorage 恢复 Token,请求 /auth/is-auth 校验会话,再由 auth slice 决定进入认证导航还是主 Tab 导航。
- 02
Discover
Home 通过 React Query 获取最新上传、推荐音频、播放列表和最近播放内容,并将服务端状态与页面渲染解耦。
- 03
Cache
播放前由 useAudioController 检查 RNFS 缓存目录中的本地文件;缺失时从 API 返回的音频 URL 发起后台下载,再交给播放器使用。
- 04
Queue
AudioData 被转换为 Track Player 队列,统一承载当前曲目、上下首切换、播放速率、进度和海报信息。
- 05
Control
MiniAudioPlayer、AudioPlayer 和 playbackService 共享同一个 Track Player,页面按钮与 Android 系统媒体控制都能操作当前播放。
- 06
History
播放服务监听进度事件,将音频、进度和时间写入 /history,让最近播放和收听历史回到服务端数据链路。
03 / ENGINEERING DECISIONS
代码中真实存在的移动端设计
- 01
用原生 Track Player 建立持续播放边界
实现InitPlayer 配置 Track Player 能力与 Android 媒体通知,useAudioController 负责队列和播放操作,playbackService 处理远程播放、暂停、上下首等系统事件。
价值播放器不再依附某一个页面,用户切换 Home、Profile 或详情内容时仍可以保持同一条播放链路。
- 02
用 Redux 与 React Query 分开两类状态
实现auth、player 和 playlistModal 进入 Redux;最新上传、推荐、收藏、历史、公开资料和关注状态通过 React Query Hook 请求与缓存。
价值当前播放曲目和认证状态等跨页面状态拥有稳定入口,而会随服务端变化的列表不需要复制进全局 store。
- 03
把设备缓存放在文件系统而不是状态容器
实现RNFS 使用 publicId 生成缓存文件路径,播放前检查文件是否存在,未命中时下载远程音频;AsyncStorage 只保存认证 Token。
价值音频二进制不会进入 Redux 或 AsyncStorage,减少内存和序列化压力,也为离线重播留下了清晰的本地存储边界。
- 04
用嵌套导航表达移动端信息层级
实现认证流程、Home 内容流、Profile 内容和上传功能分别由 AuthNavigator、HomeNavigator、ProfileNavigator 与 TabNavigator 组合。
价值导航结构与产品区域保持一致,公共播放器则通过 AppView 在页面外层持续出现,避免每个页面各自实现播放控件。
- 05
把用户生成内容串成完整业务闭环
实现AudioForm 结合 Yup、原生文件/图片选择器和 multipart 上传;后端用 Express、Formidable、Cloudinary、MongoDB 处理音频、封面、播放列表和用户关系。
价值项目不只是一个播放器 Demo,而是把认证、创作、媒体发布、收藏、历史和社交关系连接成可操作的产品流程。
- 06
推荐逻辑先保持可解释和可验证
实现后端根据用户近 30 天历史中出现的分类筛选音频,并通过定时任务生成自动播放列表;前端以 React Query 请求推荐结果。
价值在没有引入机器学习基础设施的情况下,先用用户行为和内容分类建立可理解的推荐入口,便于后续用真实数据评估效果。
04 / CURRENT BOUNDARIES
原型已经完整,但仍有几条工程边界
移动端播放与内容流程已经形成可运行的骨架;下载时序、Token 生命周期、历史写入和后端约束是下一轮迭代更值得投入的地方。
- 高优先级
下载辅助函数没有完整等待下载完成
downLoadFile 发起 RNFS.downloadFile 后没有返回或等待 promise,而 onAudioPress 仍然继续后续播放流程。弱网或首次播放时可能出现文件尚未准备好就进入 Track Player 的竞态,需要补齐 await、失败回退和重复下载去重。
- 高优先级
JWT 生命周期和移动端存储仍需加强
Token 当前保存在 AsyncStorage,服务端签发时没有看到明确的过期时间、刷新轮换或有限的设备 Token 清理策略。它适合原型验证,但不能直接包装成完整的生产级会话安全方案。
- 中优先级
播放进度写入与历史模型需要收敛
playbackService 会根据进度事件写入 /history,历史文档同时维护 last 与嵌入式 all 数组。事件频率、写入幂等、数组增长和离线补偿都需要在音频规模扩大前重新设计。
- 中优先级
媒体上传和关系写入缺少更强的服务端约束
Formidable 文件类型与大小限制、收藏与音频 likes 的跨文档一致性、关注关系的事务边界以及私有播放列表的访问校验仍有进一步收敛空间。
- 中优先级
多语言、测试和可观测性尚未形成完整能力
当前代码中没有看到已接入的 react-i18next、Redis/CDN、推送通知或完整测试套件。推荐逻辑也是基于历史与分类的规则实现,而不是机器学习推荐系统。
05 / NEXT ITERATION
下一轮应该怎么做
- 01
让音频下载真正可等待、可取消、可重试,并增加缓存清理和并发去重
- 02
为 JWT 增加短期过期、刷新轮换、撤销和设备数量控制,评估更安全的凭证存储方案
- 03
将播放历史改为批量或节流写入,限制单文档增长并设计离线事件补偿
- 04
补齐上传大小/类型校验、唯一索引和 MongoDB 事务,统一私有内容授权
- 05
为播放、上传、认证和推荐增加单元/集成测试,再评估 Redis、CDN 和推送通知