一、项目背景与定位
海外轻量级社交 IM 应用,支持单聊、消息漫游、离线推送,目标用户为欧美年轻群体。
从 0 到 1 独立开发,3 个月内完成并上线 App Store 与 Google Play。
核心挑战:
- 跨平台一致性(iOS/Android 表现一致)
- 海外网络环境复杂(弱网、高延迟)
二、架构设计演进
1. 初始阶段:MVC 分层架构搭建,完成项目基建设
初期采用GetX框架,搭建MVC分层架构,优先实现常用底层工具类:网络类(Dio)、本地存储类(MMKV)、通知类(EventBus)等。
分层架构采用模块化分层,自顶向下顺序依赖。
MVC实现UI与逻辑解耦。
2. 攻坚阶段:IM核心流程UML图
公司的规模很小,一个人干真是亚历山大。参考NIMSDK的IM Demo,针对需求完成核心的IM流程图。
实现im_core模块,分别包含message_manager,session_manager,管理消息和会话。
问题暴露:
由于采用了模块化分层的方案,导致A模块想要引用B模块的功能,无法直接引用或者说直接A->B的话,A\B属于同一层级的模块。A->B打破了模块化分层,自顶向下的依赖原则。
改进:
随着功能增多,引入 get_it + injectable 实现依赖注入:
解耦模块依赖
// 注册
GetIt.I.registerSingleton<MessageRepository>(MessageRepositoryImpl());
// 使用
final repo = GetIt.I<MessageRepository>();
三、核心技术难点与突破
难点 1:WebSocket 高可用与断线重连
问题:海外网络不稳定,频繁断连导致消息丢失。
原方案:简单定时重连 → 失败率高。
优化方案:
- 指数退避重连:
reconnectDelay = min(30s, base * 2^retryCount) - 心跳保活:每 30s 发送
ping,超时 3 次触发重连 - 消息补偿机制:重连后主动拉取
last_msg_id之后的消息 - 本地缓存兜底:未确认消息存数据库,恢复后重发
成果:
- 断线恢复成功率从 72% → 98.5%
- 消息丢失率 < 0.1%
难点 2:消息列表高性能渲染
问题:长列表滚动卡顿,尤其在低端 Android 机上。
分析:
ListView.builder虽然懒加载,但 Widget 结构复杂- 每条消息包含头像、时间、内容、状态,嵌套层级深
- 图片加载未节流
优化措施:
| 优化项 | 方案 | 效果 |
|---|---|---|
| Widget 结构 | 提取 const + const 构造函数 | 减少重建 |
| 图片加载 | cached_network_image + fadeInDuration: 0 | 避免闪烁 |
| 滚动性能 | addAutomaticKeepAlives: false + cacheExtent | FPS 提升 40% |
关键代码:
ListView.builder(
cacheExtent: 500, // 预加载范围
itemExtent: 60, // 固定高度,避免 layout 计算
...
)
难点 3:消息同步
每条消息携带唯一的msgID,需要确保用户离线状态下收到的消息不丢失。
方案:
NIMSDK的服务端已经提供了消息存DB的功能,我需要拿到last_msg_id,App启动后根据last_msg_id拉取未读消息,再对消息列表进行本地去重后渲染到消息列表。
难点 4: 设计礼物播放插件
在im聊天窗口,可以送对方礼物,购买礼物发送后,在聊天窗口会出现礼物播放的特效。
基于腾讯的flutter插件flutter_vap封装了一个礼物播放的插件。
插件源代码: github.com/mrginpadd/w…
结合了 视频播放 + 状态管理 + 播放队列 + 资源优化, 支持多个礼物按顺序播放。
四、性能与稳定性保障
Firebase Analytics
为项目集成FireBase实现埋点和上报异常。
五、部分界面展示
六、总结:我的成长和收获
自从Oppo的TeamTalk项目组辞职去义乌电商创业之后,中间离开it行业差不多8个月。
这是回归IT的第1个项目,也是第1个完全独立从0搭建并上线的项目。
是我转型的一个转折点,彻底摆脱了学生心理。
之前校招零基础进TeamTalk项目组,组内二十多个and + iOS开发人员,全是5~10年经验的中高级工程师,我和同事B同为应届生,面对着经验上的巨大差距,免不了心理上有巨大的落差,做事情畏畏缩缩。
直到遇到这个项目让我有机会尝试了全链路的开发流程,一切都在我脑中清晰了起来。
想到之前在TT组,每天下班都开代码评审会,开到七八点,同事们颇有微词。Leader说:“之所以开会,是希望我们组是一个成长型的组......”。当时不理解。直到自己独立开发,每一场开会的内容,同事讲的代码或者方案或者一个不经意的小细节,都在我脑中不断的浮现出来。
教育在此刻完成了闭环......