消息跨端架构演进:基于 C++ 的多端一致性研发框架实践
消息跨端架构演进:基于 C++ 的多端一致性研发框架实践
在移动端三端并行的时代,消息模块——用户每天打开频率最高的页面——如何从"三套代码各写一遍"进化到"一次开发、三端一致"?本文分享了一套经过千万级用户验证的跨端消息架构方案,将代码复用率从 15% 推到 80% 以上。
一、那个让人头疼的起点
从移动端兴起开始,各种跨端方案就一直层出不穷,有些方案更强调一致性及性能,有些则更强调方案的动态性和可拓展性,这里不去评判方案的好坏,因为在不同的业务场景下,本身就会催生出不同的跨端方案,只要能契合当下及未来业务的发展趋势,那就不失为一个好方案。
然而在即时通讯类的业务领域,却很难找到一种完美的跨端方案,原因有多个:
一是聊天场景下的业务逻辑较为复杂,即便是一个看似简单的列表页面,其背后可能也包含了多 Tab 容器嵌套、列表布局、卡片渲染、筛选浮层等视图组件,以及首屏加载、下拉刷新、分页加载、筛选操作等业务逻辑,如果一个跨端方案的性能有较大的瓶颈,那在IM场景下的体验多半会被用户所吐槽,对于大流量的app来说几乎是不可接受的。
二是IM底层通常需要更稳定的长连SDK、更高效的网络库等中间件,这些能力对于跨端框架来说实现都较为苦难,往往更依赖原本各平台所建设的原生能力。
三是聊天场景下,业务会提出众多动态化的卡片诉求,尤其是在电商领域,纯原生的方案已经很难能满足业务快速迭代的需求了
这不是某个团队独有的困境,而是移动端多平台开发的通病。1688的消息模块在架构演进过程中,也逐步暴露出五个系统性问题。
多端体验割裂。 各端技术选型各自为政——Android 消息列表页采用 MVC 架构,聊天页为 MVP,鸿蒙端则用 MVVM。组件实现强依赖各端研发人员的个人理解,长期迭代后内部实现偏差越来越大,用户体验的一致性无从保障。
开发效率被平台数量绑架。 每个需求三端独立开发,研发人员还要理解多套框架的调试方法,简单高效的迭代变成了奢望。
代码冗余与维护困境。 历史包袱严重,大量功能并非当前业务所需,却对包体产生了显著影响。平台能力与业务代码耦合度高,逻辑层和视图层互相纠缠。
动态性与性能瓶颈。 只有消息卡片支持动态化开发,其余修改都得跟随版本发布。消息页需要聚合多个数据源,频繁处理红点刷新、消息收发等高频场景,既有架构容易触发性能瓶颈。
监控能力薄弱。 核心技术指标缺乏报表,打点零散,问题感知能力弱。
下图展示了既有架构的全貌——可以直观看到,当前研发模式中仅有部分数据层使用跨端代码实现,UI 和逻辑层几乎完全由各端独立构建:

从量化角度看,原有架构的跨端能力数据是这样的:
| 代码分层 | 代码占比 | 跨端代码占比 |
|---|---|---|
| UI 层 | 40% | 5% |
| 逻辑层 | 40% | 10% |
| 数据层 | 20% | 15% |
整体跨端代码占比不足 15%——这意味着绝大部分工作都在重复劳动。
二、病因诊断:到底是什么阻碍了跨端?
深入分析后,我们将阻碍跨端化的核心因素归结为三点。
视图层与逻辑层强耦合。 各端视图组件直接操作 UI 属性,业务逻辑面向平台特定的 View/ViewController/Component 编程。比如 Android 端根据数据请求结果直接操作页面组件更新属性,iOS 端面向 VC 生命周期实现页面逻辑——两端代码结构完全不同,无法提取公共实现。
逻辑层代码与平台特性深度绑定。 业务逻辑面向平台生命周期(Android 的 Fragment/Activity、iOS 的 ViewController、鸿蒙的 Page)实现。即便尝试用 C++ 做跨端逻辑,每个平台仍需开发胶水层代码(API 声明、类型转换、模型定义等),额外的桥接和联调工作量几乎抵消了跨端收益。
动态化组件覆盖率有限。 浮层、弹窗、嵌套页面、加载态等高频视图功能无法通过动态化方案直接实现,仍需各端独立开发 Native 层代码。
三、架构设计:三大核心思路
核心其实就是将UI层和逻辑层的代码中的跨端比例部分大幅提升,同时建立标准统一的消息页面架构以及开发规范。
UI层中,协议部分采用1688自研的Cyber框架,本质就是用一份JSON协议编排描述布局的内容,每个布局包含组件的模版信息和数据;组件开发部分采用阿里内部的高性能跨端框架DinamicX,这种方案在电商领域久经验证,本质其实是类RN或Weex的原生渲染框架,但阉割了其JS引擎的部分(虽然DX某些版本也支持过这块能力),转而通过自定义事件及事件链的形式支持简单的逻辑交互。
逻辑层则尽可能下沉各端的逻辑代码至跨终端C++ SDK中,通过一套代码强保证多端的一致性。
基于此我们确定了清晰的目标:跨端代码复用率 80% 以上,三端新需求开发效率提升 50%,同时包体大幅瘦身、监控全覆盖、性能提升 20%。
新框架遵循"低理解成本、高隔离性、高性能、强一致性、可动态化"的设计原则,通过三大核心能力来实现多端一致性表达。
3.1 数据驱动渲染——从"操作视图"到"操作数据"
这是整个框架最关键的范式转变。我们将业务视图抽象为两个核心概念:
- VO(View Object):视图模型对象,描述组件应该"长什么样"。
- Event:事件对象,描述用户"做了什么"。
工作流程形成了一个清晰的单向数据流闭环:视图组件通过 Event 上报交互行为 → 跨端逻辑层处理事件并更新 VO → 容器框架将 VO 变更分发给视图层 → 视图层根据 VO 差异完成渲染更新。

这个设计的精妙之处在于:逻辑层再也不用关心"视图是怎么画的",只需要告诉渲染层"布局应该怎么排布"以及“数据变成什么样了"。既然逻辑层不碰平台特定的 UI API,它自然就具备了跨端的可能性。
页面的 UI 编排信息通过协议化的 JSON 表达,由渲染框架解析并构建对应的组件树。以消息会话列表为例:
{
"structure": {
"root": {
"type": "List",
"children": ["loginComponent", "conv_1", "conv_2", "recommend"]
}
},
"component": {
"conv_1": {
"template": {
"componentType": "消息模板名",
"renderType": "dynamic",
"version": "模板版本",
"templateUrl": "模板资源URL"
},
"data": {
"message": "消息模板数据"
}
}
}
}
这种协议化的方式使得 C++ 逻辑层可以灵活控制页面结构,同时保持了与平台渲染层的解耦。
3.2 统一业务逻辑运行环境——让 C++ 逻辑"不知道"自己跑在哪个平台
框架抽象出一套屏蔽端特性的统一生命周期接口:
class BizLogicProtocol {
public:
// 创建页面编排信息
virtual JSON onCreatePageInfo(const std::string& bizId) = 0;
// 首屏数据加载
virtual void onLoadFirstPageData() = 0;
// 容器出现(对应 Android onResume / iOS viewDidAppear / 鸿蒙 onPageShow)
virtual void onContainerAppear() = 0;
// 容器消失
virtual void onContainerDisappear() = 0;
// 容器销毁
virtual void onContainerDestroy() = 0;
// 统一事件处理入口
virtual void onHandleEvent(const std::shared_ptr<CPEvent>& event) = 0;
};
各平台仅需将自身生命周期映射到上述接口即可完成对接。业务开发者面向这套统一接口编程,完全不用关心代码最终跑在哪个平台。
容器框架还提供了完整的运行时能力集——VO 的增量更新、列表子节点的增删改、滚动控制、跨端能力调用等。所有这些能力都通过统一的 Context 接口暴露,各平台通过桥接层完成注入。
#pragma once
#include <map>
#include <Wrapper4AH/PlatformAbility/NativePlatformAbilityDefine.h>
#include "pie/macros.hpp"
#include "pie/json.hpp"
class SGUContextProtocol {
public:
/**
* 同步调用native泛化接口
*/
virtual ResponseResult syncCallAbility(
const std::string& ability,
const std::string& api,
const std::map<std::string, pie::JSON>& param = {}) {
return {};
}
/**
* 异步调用native泛化接口
*/
virtual void asyncCallAbility(
const std::string& ability,
const std::string& api,
const std::map<std::string, pie::JSON>& param = {},
const std::function<void(ResponseResult)>& complete = nullptr) {
}
// 获取用户ID
virtual std::string getUserId(){ return ""; }
// 获取设置参数
virtual std::map<std::string, pie::JSON> getEntryParams() { return std::map<std::string, pie::JSON>(); }
// 获取容器 VO 对象
virtual pie::JSON getVO(const std::string& containerId){ return pie::JSON();}
// 更新容器 VO 对象(容器级别更新)
virtual void updateVO(const std::string& containerId, const pie::JSON& vo) {}
// MARK: - 容器操作,子节点的增删改查
// 批量操作接口,使用 vector<pie::JSON> (UpdateData) 保证顺序且便于跨语言处理
/// 在容器末尾追加子节点
/// @param containerId 容器 ID
/// @param children 子节点列表,每个元素为序列化后的 UpdateData JSON
/// @param animated 是否使用动画
/// tips:如果滚动条在底部附近(100px以内),append 后自动滚动到底部
virtual void appendChildren(const std::string& containerId,
const std::vector<pie::JSON>& children,
bool animated = true) {}
/// 在指定节点之前插入子节点
/// @param containerId 容器 ID
/// @param anchorId 锚定节点 ID
/// @param children 子节点列表,每个元素为序列化后的 UpdateData JSON
/// @param animated 是否使用动画
/// tips:加载更多历史消息时,保持滚动位置不变
virtual void insertChildrenBefore(const std::string& containerId,
const std::string& anchorId,
const std::vector<pie::JSON>& children,
bool animated = true) {}
/// 在指定节点之后插入子节点
/// @param containerId 容器 ID
/// @param anchorId 锚定节点 ID
/// @param children 子节点列表,每个元素为序列化后的 UpdateData JSON
/// @param animated 是否使用动画
virtual void insertChildrenAfter(const std::string& containerId,
const std::string& anchorId,
const std::vector<pie::JSON>& children,
bool animated = true) {}
/// 删除子节点
/// @param containerId 容器 ID
/// @param childIds 要删除的子节点 ID 数组
/// @param animated 是否使用动画
virtual void deleteChildren(const std::string& containerId,
const std::vector<std::string>& childIds,
bool animated = true) {}
/// 增量更新子节点
/// @param containerId 容器 ID
/// @param children 子节点列表,每个元素为序列化后的 UpdateData JSON
/// @param animated 是否使用动画
/// tips:如果reload元素导致容器高度和滚动条位置变化,需保证滚动条位置不变
virtual void reloadChildren(const std::string& containerId,
const std::vector<pie::JSON>& children,
bool animated = true) {}
};
3.3 跨平台组件统一
针对动态化方案Dinamix原生组件无法覆盖的视图场景(长按浮层、Markdown 渲染、进度条等),框架在各平台实现了一套具备高度一致性的自定义DinamicX组件。动态化组件覆盖日常 95% 以上的 UI 需求,剩余场景通过标准化的 Native 组件补齐。
3.4 整体分层架构
新版消息架构的整体分层设计如下图所示:

新版消息架构自上而下分为三层:
- 视图层(Rendering Layer):基于协议驱动的渲染框架 + 动态化组件构建,实现 UI 渲染与业务逻辑解耦,支持多种渲染引擎。
- 视图逻辑层(View Logic Layer):使用 C++ 实现,这是跨端的核心层。业务逻辑面向框架接口编程,通过 VO 更新驱动视图刷新,通过 Event 机制接收视图事件。
- 数据融合层(Data Service Layer):对接底层消息 SDK 的原子能力,提供会话服务、消息服务、Profile 服务、关系服务等标准化数据接口。
四、核心技术实现细节
4.1 事件分发:所有交互走同一条路
事件是视图层向逻辑层通信的标准方式。框架定义了统一的事件模型:
public class CPEvent {
String type; // 事件来源类型:dynamic / native
String action; // 行为标识,如 stickyConversation(置顶会话)
JSONObject data; // 事件参数
}
无论事件来源是动态化组件还是 Native 组件,最终都流转到 C++ 逻辑层的 onHandleEvent 方法统一处理。这保证了业务逻辑的处理方式完全一致,消除了"同一个功能在不同端走不同代码路径"的隐患。
4.2 消息流列表——最复杂的组件如何跨端
消息流是聊天页面中最复杂的部分。我们将整个聊天页拆分为五个子模块:标题栏、顶部卡片、消息流列表、输入区域、输入辅助面板。所有子模块继承统一基类,统一管理生命周期和事件分发。
消息拉取与分页。 通过游标(Cursor)实现双向分页加载。首屏加载时向下拉取历史消息并自动滚到底部;用户上滑时以旧游标继续向前拉取并将历史消息插入列表头部。首屏加载期间通过门控标志拦截实时推送,待加载完成后统一刷新。
消息解析管线。 原始消息到渲染 VO 的转换经过一条统一的解析管线——先根据相邻消息的时间间隔(阈值 5 分钟)自动插入时间分隔符,然后通过策略模式将每条消息分发给对应类型的子解析器(文本、图片、视频、语音、文件、系统消息等 11 种类型)。
双层渲染结构。 所有消息共享一个外层基布局模板(头像、昵称、时间、状态等公共框架),每种消息类型通过内容模板字段嵌入各自的渲染模板。这种"基布局 + 内容模板"的设计既保证了视觉一致性,又支持灵活扩展新消息类型。
4.3 数据融合层:15 个服务模块的统一管理
数据融合层通过一个全局服务管理器统一管理所有数据服务的生命周期。其整体架构如下:
其核心设计是一个二级 Map 结构 serviceMap[account][serviceName],支持多账号场景下的服务隔离。
15 个服务模块按功能域划分为五组:
- 核心通信:会话管理、消息收发与存储、未读计数
- 社交关系:资料与头像、群组管理、联系人管理
- 辅助功能:搜索、设置、数据同步、推送管理
- 富媒体与扩展:多媒体处理、系统通知、红包、表情、翻译
- 账号无关:登录管理
每个核心服务内部通过 Adapter 模式屏蔽底层 SDK 接口差异——这层隔离确保了当底层 SDK 升级时,变更不会扩散到业务逻辑层。所有数据变更通过轻量级事件系统对外暴露,框架保证回调在主线程执行。
4.4 页面加载流程
以会话列表页为例,从用户触发到数据上屏的完整加载时序如下:

整个流程分为三个阶段:容器初始化(Native 路由创建 BizLogic 实例并注入 Context)、生命周期驱动(逻辑层在首屏加载时机调用数据服务获取会话列表)、数据上屏(通过 Context 能力调用 updateVO 将 VO 推送给渲染层完成 UI 上屏)。整个链路中,C++ 逻辑层保持跨端一致,各端仅在容器层做平台适配。
五、各端接入有多简单?
说再多架构设计,不如看看实际接入代码有多精简。
Android 端:
class ConversationListPageContainer : BaseMessagePageContainer() {
override fun getBizName() = "ConversationList"
}
HarmonyOS 端:
build() {
NavDestination() {
Column() {
PageContainerWrapper("ConversationList")
}
}
}
iOS 端:
open class ConversationListViewController: BaseContainerViewController {
open override func getPageName() -> String {
return "ConversationList"
}
}
三端接入代码各只有几行——核心业务逻辑全部在 C++ 层实现,端侧仅负责容器创建和生命周期绑定。这不是 Demo 级别的简化,而是真实生产环境中的代码量。
六、AI Coding 与跨端架构的化学反应
一个有趣的发现是:当项目架构抽象得足够合理、开发范式足够固定时,AI 辅助代码生成的准确率会显著提升。
我们在项目中深度实践了 AI Coding,为项目沉淀了两类知识资产:
全局编码规范(Rules)。 涵盖 C++ 内存管理、并发实践、命名约定、函数设计约束等。AI 开发助手在生成任何代码时自动遵循这些规范——新同学使用 AI 写代码时,产出直接符合团队规范,Code Review 沟通成本大幅下降。
场景化技能(Skills)。 针对高频开发场景沉淀了一系列结构化知识:新建跨端页面、调用原生能力、注册 Native Ability、埋点开发、缓存管理、日志排查等。AI 助手在识别到对应意图时自动激活。
实践效果:项目中 50% 以上代码由 AI 辅助生成,近两个月这个比例稳定在 80% 以上。我们的策略是"框架约束 + AI 生成 + 人工审核"三段式——框架提供清晰的生成边界,Rules/Skills 提供领域知识,人工审核聚焦业务正确性和边界场景。
一个核心洞察是:跨端框架 vs AI 代码翻译并不是非此即彼的选择。AI 翻译的代码仍需人为拍平三方库 API 差异,而跨端框架层级清晰、范式固定,反而是 AI 辅助生成最擅长的场景。两者结合的效果远大于单独使用。
七、技术选型的关键决策
为什么选择 C++ 作为逻辑层语言?
选择 C++ 而非 Kotlin Multiplatform、Dart 等方案,基于以下考量:
- 技术栈延续性:底层消息 SDK 本身基于 C++ 构建,选择 C++ 最大程度减少了新增桥接层的影响面。
- 团队能力匹配:客户端团队中 C++ 开发者占比较高,无需额外的技能培养周期。
- 语言性能优势:C++ 的运行时性能在消息流的高频操作场景中具备天然优势。
- 基础库验证度:核心跨端桥接库已在亿级 DAU 应用中经过充分验证,稳定性达到 0 崩溃水平。
渲染方案的选择逻辑
选择协议驱动的渲染框架,核心原因有四:天然契合数据驱动的要求;团队技术栈统一;支持复杂嵌套场景的统一管理;支持 diff 更新和单组件刷新,满足消息流频繁更新的性能需求。
跨端方案 vs AI 代码翻译
该跨端方案的核心优势在于一致性保障。由于各端平台的原生特性并不完全一致,三方库的接口 API 存在差异性,AI 翻译生成的代码往往仍需人为拍平不一致的部分,反而降低了开发效率。
另一方面,消息的跨端架构层级清晰、开发范式固定,AI 辅助代码生成在此场景下准确率较高。因此我们的策略是"跨端框架为主,AI 辅助为辅"——结合 DX 自动生码能力和跨端逻辑代码自动生成能力,进一步提升开发效率 50%。
八、全链路质量保障
SOP 节点化监控
针对核心链路,我们定义了标准化的 SOP 节点,形成结构化日志和完整的链路追踪:
- 消息发送链路:触发发送 → 快速上屏(发送中)→ SDK 发送 → 成功 → 二次渲染(成功状态)
- 会话列表拉取链路:容器加载 → 登录态检查 → 并行拉取(单聊/群聊/系统消息)→ 排序 → 渲染上屏
- 新消息到达链路:长连接推送 → 本地存储 → 事件分发 → 前台渲染或后台通知
每个节点记录 STEP 编号、时间戳和消息标识,对接日志服务后可实现自动化问题排查。
度量体系
建立了三维评价体系:
- 开发效率:跨端代码占比、单需求三端工时对比、包体变化
- 质量指标:模块 Crash 率、消息发送成功率、页面打开成功率、白屏率
- 性能指标:聊天页面 FCP、消息流加载时间、操作响应时间
所有监控项三端统一口径——跨端一致性不仅体现在代码层面,也贯穿到质量度量中。
九、落地实践与成果
方案遵循渐进式上线策略:
- 第一阶段(验证期):在设置页、创建群聊页等独立页面试点,验证框架可行性和稳定性。
- 第二阶段(核心入口):会话列表页迁移,覆盖最高频的入口页面。
- 第三阶段(全面迁移):聊天会话页整体迁移,按 5% → 10% → 20% → 50% → 100% 的节奏逐步放量。
聊天页迁移采用设备维度的 AB 切分策略,逐周切量观察核心指标。
实际成效:
- 新版会话列表页平均 FCP 从 700ms+ 降至 600ms
- 新版聊天页平均 FCP 从 400ms+ 降至 300ms
- 跨端代码复用率从 15% 提升至 80%+
- 代码量从百万行级降至十万行级
- 双端包体减少 5MB+
- 三端新需求开发效率提升约 50%
十、经验总结与未来展望
在实践中,我们总结出几点经验:
跨端不是追求 100% 代码复用。 核心是在"一致性"与"平台最优体验"之间找到平衡点。对于强交互场景(如键盘区域),保留 Native 实现往往是更好的选择。
容器框架的抽象层级要足够精简。 过度设计会导致理解成本反增。我们的 BizLogicProtocol 只有 7 个核心方法,足以覆盖所有业务场景。
全链路质量监控是成功落地的关键保障。 跨端方案最怕的不是技术问题,而是出了问题找不到原因。
架构合理性决定 AI Coding 的上限。 当项目架构抽象得足够合理时,AI 辅助的成本直线下降。好的架构不仅降低了人的认知负担,也降低了 AI 的"认知负担"。
未来,我们将持续演进该框架——探索完全依赖底层原子能力开发的轻量路径,建设 AI Agent 辅助的自动化问题排查,完善跨端单元测试体系,最终打造一套工业级可复制的移动端跨端研发范式。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)