跨端性能、原生桥、离线同步、测试与发布
0. 元信息
- 主题路径:
docs/topics/mobile-cross-platform/subtopics/cross-platform-performance-and-platform-bridges/README.md - 父主题:
mobile-cross-platform - 主分类:前端与客户端
- 辅助分类:工程技术、安全与可靠性
- 适合对象:能写 RN / Flutter App、想交付生产质量移动端的开发者
- 建议周期:2~3 周(每周 8~12 小时,含真机性能测试、e2e、商店发布)
- 前置知识:
mobile-cross-platform父主题;native-mobile-foundations、react-native-architecture、flutter-and-dart-engine;Xcode / Android Studio 基础 - 最终目标:能在 RN / Flutter 中调用原生模块、解决离线冲突、用 Detox / Maestro 写 e2e 测试、用 TestFlight / Play 内部测试发布 App,并能用启动时间、帧时间、内存峰值三件套定位性能瓶颈
1. 学习路线
启动时间拆解(Native init / JS bundle / 首帧)
→ 帧时间与掉帧(Choreographer / VSync / Reanimated / Impeller)
→ 内存与 OOM(Leak / Bitmap / Image 缓存)
→ 原生桥深化(JSI / TurboModule / Platform Channel / Codegen)
→ 离线存储(Realm / SQLite / Drift / MMKV / Hive)
→ 离线同步与冲突解决(时间戳 / CRDT / OT)
→ E2E 测试(Detox / Maestro / XCTest / Espresso)
→ 签名、Provisioning / Keystore
→ App Store / Play Console 发布、审核、监控
2. 阶段周数分配(46 周,每天 1.52 小时)
精简子主题按”性能 → 原生桥 → 离线 → 发布”四段推进。
| 阶段 | 主题 | 周数 | 备注 |
|---|---|---|---|
| 1 | 启动与帧时间调优 | 1 周 | Native init / JS bundle / VSync |
| 2 | 内存与 OOM | 0.5 周 | Leak / Bitmap / Image 缓存 |
| 3 | 原生桥深化 | 1 周 | JSI / TurboModule / Platform Channel |
| 4 | 离线存储与同步 | 1.5 周 | Realm / SQLite / MMKV + CRDT |
| 5 | E2E 测试 | 0.5 周 | Detox / Maestro |
| 6 | 签名与发布 | 0.5 周 | Provisioning / Keystore / Store |
| 7 | 综合项目(性能优化实战) | 1 周 | 含 README + 复盘 |
选 4 周方案时把第 4/5 阶段压成 1 周。每周留 0.5 天复盘。
3. 九阶段表
| 阶段 | 核心知识 | 实践产出 | 可观察学会标准 |
|---|---|---|---|
| 1 | 启动时间拆解 | 一份冷启动时间报告(Native / JS bundle / 首帧) | 冷启动 < 1.5s |
| 2 | 帧时间与掉帧 | Reanimated / Impeller 调优 | P99 帧时间 < 16ms |
| 3 | 内存与 OOM | LeakCanary / Instruments Allocations | 内存峰值 < 300MB |
| 4 | 原生桥 | 一个自定义 TurboModule + 一个 Platform Channel | 跨端调原生相机 |
| 5 | 离线存储 | Realm / SQLite / Drift 任选 | 1 万条数据读写 < 50ms |
| 6 | 离线同步 | 时间戳 + LWW / CRDT | 冲突可解释 |
| 7 | E2E 测试 | Detox / Maestro 用例 ≥ 10 条 | 接入 CI |
| 8 | 签名 | Provisioning / Keystore / Play App Signing | 真机安装 |
| 9 | 发布与监控 | TestFlight / Play 内部测试 + Crashlytics / Sentry | 邀请链接 |
关键陷阱:只看平均延迟;同步阻塞跑大文件;忽略 ATS / cleartext;Privacy Manifest 不填;签名丢失;审核驳回不复盘。
4. 第一周任务
| 日 | 任务 | 当天交付 |
|---|---|---|
| Day 1 | 用 Flipper / DevTools 拆冷启动:Native init + JS bundle 加载 + 首帧 | 启动时间报告 |
| Day 2 | 写 Reanimated 3 worklet 60fps 列表 + 用 FlashList / ListView.builder | 帧时间对比 |
| Day 3 | 用 Instruments / LeakCanary 跑内存,找一张 1MB Bitmap 不释放 | 内存报告 |
| Day 4 | 用 Codegen 写 TurboModule 调 iOS / Android 原生相机 | 相机 Demo |
| Day 5 | 用 MethodChannel 调原生 Toast / Push | 双端桥 Demo |
| Day 6 | 用 Realm / SQLite 存 1 万条数据,测读写延迟 | 存储性能 |
| Day 7 | 步骤 A:跑通跨端最小可用(含离线 + 原生桥 + e2e);步骤 B:补齐 4 类边界(冷启动 > 2s、JS bundle > 5MB、内存峰值 > 500MB、e2e 失败) | 综合 Demo + 边界测试 |
5. 阶段通用验收
- 不看答案独立写出冷启动拆解报告与 e2e 用例;
- 用自己的话解释 Native init / JS bundle / 首帧的关系;
- 画一张图:冷启动到首帧的时序;
- 测试正常路径、冷启动慢、帧时间差、内存峰值高;
- 至少准备 3 组自定义数据并贴出实际渲染;
- 记录冷启动、P99 帧时间、内存峰值、JS bundle 大小 4 个核心指标;
- 能修改既有 App 加原生桥、加离线缓存、加 e2e。
6. 最终验收
- 独立画出冷启动到首帧的时序图;
- 完成至少 8 个跨端性能 / 桥 / 离线 / 发布实验;
- 用 Detox / Maestro 写 10 条 e2e 用例并接入 CI;
- 完成 1 次 TestFlight / Play 内部测试发布;
- 能用 10 分钟讲清「为什么这段逻辑必须走原生桥、签名怎么管理、审核驳回怎么应对」。
7. 综合项目
用 RN New Architecture 或 Flutter 3 实现一个含原生相机 + 离线同步 + e2e + 性能报告 + TestFlight / Play 发布的 Todo / 电商 / IM App。成果即父主题首选综合项目。
本主题贡献
跨端生产化意味着把帧时间、离线冲突、e2e、签名四件事都打通,单点优化不够。本子主题负责讲清”冷启动怎么拆”、“Realm/SQLite 怎么选”、“Detox vs Maestro 怎么取舍”、“Fastlane match 怎么管签名”四个跨端 QA 必答题,并把 JSI / TurboModule / Codegen 三件套在性能优化场景下如何替代 Bridge 串成完整证据链。
3 职责
- 把冷启动拆成 Native init / JS bundle / 首帧三段,给每个 SDK 加一次 P50/P99 时间预算(< 1.5 s)。
- 用 Realm / SQLite / Drift / MMKV / Hive 五种存储做 1 万条数据读写延迟对比,选出离线 + 同步场景的合理组合。
- 用 Detox(iOS / Android 原生)+ Maestro(跨 UI)写 e2e ≥ 10 条,用 Page Object + 显式等待接 CI。
4 交付物
- 一份冷启动时间报告(Native init / JS bundle / 首帧 / 看板 SDK 各占时长),含 P50 / P99 与 SDK 启动占比排行。
- 一份 Realm 离线同步 demo(时间戳 + deviceId + LWW 冲突解决),含两端同改同写的复现记录与 CRDT 升级路径。
- 一份 Detox + Maestro 双栈 e2e(login / list / detail / offline),接入 GitHub Actions / EAS Build,含 Page Object 模式用例。
- 一份 Fastlane match 签名 + TestFlight / Play 内部测试发布记录(含 dSYM / mapping 上传 + Privacy Manifest + ATS 配置)。
3 指标
- 冷启动 P99 < 1.5 s(Flipper / DevTools Timeline 三段总和)。
- 列表 P99 帧时间 < 16 ms(长列表 + FlashList / ListView.builder + Reanimated)。
- e2e 接入 CI 后 flake rate < 5%(重试一次通过 / 总用例数)。
App Store Review Gui、Play Console Policies、Detox Documentation、Maestro Documentation、Realm Documentation、Fastlane Documentation。复制前核对 LICENSE。
8. 推荐资料
App Store Review Gui、Play Console Policies、Detox Documentation、Maestro Documentation、Realm Documentation、Fastlane Documentation。复制前核对 LICENSE。
9. 学习资料汇聚(v0.3 自包含)
9.1 背景与动机
跨端 App 真正难的是「生产质量」。能跑通 Demo 不等于能上 App Store。冷启动、帧时间、内存峰值是体验三件套;离线冲突是分布式系统的简化版;签名与审核是平台红线;e2e 是回归防线。这四件事一起决定一个跨端 App 能不能活过第一个版本。
9.2 概念地图
flowchart TB
Perf[性能三件套] --> Cold[冷启动]
Perf --> Frame[帧时间]
Perf --> Mem[内存]
Bridge[原生桥] --> JSI[JSI / TurboModule]
Bridge --> Channel[Platform Channel]
Offline[离线] --> Store[Realm / SQLite / Drift / MMKV]
Offline --> Sync[同步协议: LWW / CRDT / OT]
Test[E2E 测试] --> Detox
Test --> Maestro
Test --> CI[CI 接入]
Publish[发布] --> Signing[签名: Provisioning / Keystore / Play App Signing]
Publish --> Store[App Store Connect / Play Console]
Publish --> Crash[Crashlytics / Sentry / Datadog Mobile]
9.3 基础知识讲解
9.3.1 论文 / 规范
| 资料 | 用法 |
|---|---|
| Apple, App Store Review Guidelines | 审核红线 |
| Google, Play Console Policies | 上架规则 |
| W3C, Trace Context Level 1 | 跨进程 trace |
| IETF RFC 8446, TLS 1.3 | ATS 对照 |
9.3.2 书
| 书 | 用法 |
|---|---|
| Continuous Mobile(Leanpub) | 移动 CI/CD |
| Charity Majors, Observability Engineering | 移动 APM |
9.3.3 博客 / 文档
| 资料 | 用法 |
|---|---|
| Detox Documentation | iOS / Android E2E |
| Maestro Documentation | 跨 UI E2E |
| Sentry Mobile | 错误监控 |
| Fastlane Documentation | 签名与发布 |
| Realm Documentation | 移动数据库 |
| Brendan Gregg Blog | iOS Instruments 调优 |
9.3.4 人物
| 人物 | 关注点 |
|---|---|
| Charity Majors | 可观测工程化 |
| Brandon Williams | EAS / Expo |
| Marc Roussel | Maestro / mobile.dev |
| Laurent Gasser | Detox / Wix |
9.3.5 方法
- Cold-start budget:定 < 1.5s 目标,每加一个 SDK 都测一次。
- Frame budget:P99 < 16ms,长列表用虚拟化。
- Repository + Conflict Resolver:单一数据源 + 显式冲突解决。
- Page Object for E2E:步骤可复用、可读。
- Sign once, ship anywhere:用 Fastlane match / Play App Signing。
- Crashlytics day-1:第一版就接。
9.3.6 重点训练材料
- Fastlane match / Supply
- Detox 入门教程
- Maestro 官方示例
- Realm / Drift 官方文档
9.4 经典问题与经典案例
| # | 问题 | 最简答案 |
|---|---|---|
| 1 | 冷启动 > 2s | 拆 Native init / JS bundle / 首帧;减 SDK |
| 2 | 列表掉帧 | 换 FlashList / ListView.builder;用 Reanimated |
| 3 | 内存峰值 > 500MB | Image 缓存上限;Bitmap 复用 |
| 4 | 原生桥大对象慢 | JSI 同步;Codegen 免序列化 |
| 5 | 离线冲突丢数据 | 时间戳 + 设备 ID + LWW;或 CRDT |
| 6 | e2e 不稳定 | Page Object + 显式等待 |
| 7 | Provisioning 过期 | 用 Fastlane match 统一管理 |
| 8 | Keystore 丢失 | 用 Play App Signing |
| 9 | Privacy Manifest 不填 | iOS 17+ 必填 |
| 10 | Crashlytics 不上报 | 看 dSYM / mapping 文件是否上传 |
9.5 学习难点
| 难点 | 为什么会卡 | 突破路径 |
|---|---|---|
| 冷启动拆解 | 多个 SDK 串联 | 用 Flipper / DevTools Timeline |
| JSI 大对象 | 序列化慢 | 用 SharedArrayBuffer 或直传引用 |
| 离线冲突 | 业务场景多样 | 用 LWW 起步,按业务换 CRDT |
| e2e 稳定性 | 时序敏感 | 用 Page Object + 显式等待 |
| 签名管理 | Provisioning 复杂 | 用 Fastlane match |
| 审核驳回 | 政策更新频繁 | 关注 Apple News + 提前自查 |
9.6 技术标准与接口
9.6.1 Entity
| 名称 | 版本 | 组织 | 状态 |
|---|---|---|---|
| Detox | 20.x | Wix | 活跃 |
| Maestro | 1.x | mobile.dev | 活跃 |
| Realm | 12.x | MongoDB | 活跃 |
| MMKV | 2.x | Tencent | 活跃 |
| Drift | 2.x | Simon Binder | 活跃 |
| Fastlane | 2.x | Fastlane | 活跃 |
| Crashlytics | — | Firebase | 活跃 |
| Sentry Mobile | — | Sentry | 活跃 |
9.6.2 Scope
原生桥负责跨端与原生 API 互调;离线存储与同步负责数据可用性;E2E 负责回归防线;签名与发布负责平台合规;监控负责故障响应。
9.6.3 Structure
必会接口:JSI jsi::Runtime、Platform Channel MethodChannel / EventChannel、Realm Realm.open、Detox by.id / tap()、Maestro tapOn、fastlane match / fastlane supply。
9.6.4 Ecosystem
CI:Bitrise、EAS Build、GitHub Actions + Fastlane;测试:Detox / Maestro / XCTest / Espresso / Patrol;监控:Sentry / Crashlytics / Datadog Mobile RUM / OpenTelemetry Mobile SDK。
9.6.5 Depth Tiers
| 层级 | 能力 |
|---|---|
| L0 | 知道性能 / 桥 / 离线 / 测试 / 发布 |
| L1 | 能读懂 Detox / Maestro 用例 |
| L2 | 能写跨端 App + 原生桥 + 离线 |
| L3 | 能写 e2e + 跑发布 + 接监控 |
| L4 | 能设计 CI/CD + 多地域灰度 + 隐私合规 |
本子主题目标:L3。
9.6.6 Source
- App Store Review Gui
- Play Console Policies
- Detox
- Maestro
- Fastlane
- 引用版本快照日期:2026-07-30。
10. 常见误区
- 把启动时间当作业务代码问题,忽略 SDK 启动;
- 用 AsyncStorage / SharedPreferences 当主存储;
- 不写 e2e 直接发版;
- 用 long-lived Keystore 提交 Play;
- 不配 ATS / cleartext;
- Privacy Manifest 不填;
- Crashlytics 接了不看 issue;
- Reanimated worklet 写错 closure;
- Platform Channel 序列化大对象;
- 把 Detox / Maestro 当单元测试;
- 模拟器测完就上商店;
- 审核驳回不复盘。
11. 所有知识点分类(统一规则)
- 编程语言;2. 数据结构与算法;3. 计算机基础;4. 工程技术;5. Web 与后端;6. 前端与客户端;7. 数据与人工智能;8. 项目与职业能力;9. 安全与可靠性。
本计划归属:前端与客户端 主 + 工程技术 / 安全与可靠性 辅。
代码块 1:冷启动到首帧时序
sequenceDiagram
participant Process
participant Native
participant Engine as RN Engine / Flutter Engine
participant Bridge as JS bundle / Dart code
participant UI as UI 渲染
Process->>Native: init
Native->>Engine: 启动引擎
Engine->>Bridge: 加载 JS bundle / Dart isolate
Bridge->>UI: 首帧渲染
UI-->>Process: 首帧完成
Note over Process,UI: 冷启动 = T0 → 首帧
代码块 2:Detox E2E
// e2e/login.test.ts
import { by, device, element, expect } from 'detox';
describe('Login', () => {
it('should login', async () => {
await element(by.id('email')).typeText('a@b.com');
await element(by.id('password')).typeText('hunter2');
await element(by.id('submit')).tap();
await expect(element(by.id('home'))).toBeVisible();
});
});
代码块 3:Maestro E2E
# .maestro/login.yaml
appId: com.example.demo
---
- tapOn:
id: email
- inputText: a@b.com
- tapOn:
id: password
- inputText: hunter2
- tapOn:
id: submit
- assertVisible:
id: home
代码块 4:Realm 离线同步(时间戳 + LWW)
// src/sync/realm.ts
import Realm from 'realm';
class Todo extends Realm.Object<Todo> {
_id!: string;
title!: string;
updatedAt!: number;
deviceId!: string;
static schema: Realm.ObjectSchema = {
name: 'Todo',
primaryKey: '_id',
properties: {
_id: 'string',
title: 'string',
updatedAt: 'int',
deviceId: 'string',
},
};
}
export function resolveConflict(local: Todo, remote: Todo): Todo {
return local.updatedAt >= remote.updatedAt ? local : remote;
}
代码块 5:Fastlane 发布
# fastlane/Fastfile
default_platform(:ios)
platform :ios do
lane :beta do
match(type: 'appstore')
build_app(workspace: 'ios/Demo.xcworkspace', scheme: 'Demo')
upload_to_testflight(skip_waiting_for_build_processing: true)
end
end