场景设计:跨端SDK架构深度解析
跨端SDK是跨端开发中最具挑战性的工程模块之一——它需要在多个平台维持一致的API体验,又要最大化复用业务逻辑,同时保证各平台的Native能力不被阉割。本文围绕一个真实场景(跨端统计分析SDK)完整讲述其架构设计过程。
一、背景与意义
现实场景
假设你在一家拥有千万级日活App的公司,同时运营着iOS App、Android App、微信小程序、支付宝小程序、以及Web端H5。公司在所有端上都需要一套用户行为统计分析SDK,用于:
- 用户行为的跨端统一归因
- 漏斗转化的全链路追踪
- A/B实验的跨端分层
- 崩溃与性能监控的统一上报
一个直观但糟糕的方案:每个端单独开发一套SDK,暴露一致的外层API。这样虽然API看起来一致,但:
- 三倍的Bug修复成本
- 三倍的功能迭代成本
- 三倍的代码审查成本
- 各端行为不一致几乎是必然的
跨端SDK架构的核心矛盾:如何在一套代码基础上支持多端?
SDK与普通业务代码的区别
理解SDK的特殊性——它不像普通业务代码在可控环境中运行:
| 维度 | 普通业务代码 | SDK |
|---|---|---|
| 宿主环境 | 自己控制 | 不确定(第三方App) |
| 初始化时机 | 应用启动 | 可能任意时刻 |
| 依赖管理 | 可控制版本 | 不能与宿主冲突 |
| 崩溃影响 | 影响自家App | 可能导致宿主App崩溃 |
| 包体积 | 不计较 | 必须轻量(<100KB) |
| API稳定性 | 可随时改 | 必须向后兼容 |
二、概念与定义
2.1 跨端SDK的整体架构模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌────────────────────────────────────────────┐
│ 业务接口层 (API) │
├────────────────────────────────────────────┤
│ 核心逻辑层 (Core) │
│ ┌──────┐ ┌──────┐ ┌────────┐ │
│ │事件路由│ │队列管理│ │ 缓存策略 │ │
│ └──────┘ └──────┘ └────────┘ │
├────────────────────────────────────────────┤
│ 适配器层 (Adapters) │
│ ┌────────┐ ┌────────┐ ┌──────┐ ┌────────┐ │
│ │iOS Adpt│ │Android │ │小程序│ │Web Adpt│ │
│ └────────┘ └────────┘ └──────┘ └────────┘ │
├────────────────────────────────────────────┤
│ 平台层 (Platform Interface) │
│ (网络请求 / 本地存储 / 设备信息 / 线程) │
└────────────────────────────────────────────┘
分层职责:
- API层:SDK对外暴露的接口,确保各端API一致
- 核心层:平台无关的业务逻辑——事件聚合、缓冲、压缩、重试
- 适配器层:将平台接口转换为核心层需要的抽象接口
- 平台层:各平台的基础能力(网络、存储、线程)
2.2 核心设计原则
1
2
3
4
5
6
7
8
9
10
11
原则1: 核心无关化 (Core Agnostic)
Core层不引用任何平台特定代码(没有#import <UIKit>、没有wx.xxx)
原则2: API同构化 (API Isomorphism)
各端暴露的API签名保持一致
原则3: 插件可插拔 (Pluggable)
SDK功能通过插件扩展,核心除外
原则4: 瘦核心,胖插件 (Thin Core, Fat Plugins)
核心只做三件事:事件路由、队列管理、生命周期
三、最小示例
3.1 SDK接口定义 (API层)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 跨端SDK的公共TypeScript接口定义
// 这是"契约"——各平台的实现都必须遵循
export interface ITrackerSDK {
// 初始化
init(config: SDKConfig): Promise<void>;
// 事件追踪
track(event: TrackEvent): void;
trackBatch(events: TrackEvent[]): void;
// 用户标识
setUserId(userId: string): void;
setUserProperties(props: Record<string, any>): void;
// 页面追踪
trackPageView(pageName: string, properties?: Record<string, any>): void;
// 计时器
startTimer(key: string): void;
stopTimer(key: string, properties?: Record<string, any>): void;
// 生命周期
flush(): Promise<void>;
shutdown(): void;
// 插件系统
use(plugin: ITrackerPlugin): void;
eject(pluginName: string): void;
}
export interface SDKConfig {
appKey: string;
serverUrl: string;
uploadInterval?: number; // 上报间隔(ms),默认30000
batchSize?: number; // 批量上报条数,默认50
maxCacheSize?: number; // 最大缓存条数,默认10000
sampleRate?: number; // 采样率,0-1,默认1.0
autoTrack?: AutoTrackConfig;
debug?: boolean;
}
export interface TrackEvent {
event: string;
properties?: Record<string, any>;
timestamp?: number;
uuid?: string;
user?: UserInfo;
}
3.2 Core层的平台无关实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// TrackerCore.ts — 核心逻辑层,不依赖任何平台
// 这是SDK中唯一需要通过单元测试全面覆盖的模块
import { compress } from './compress';
import { TrackerQueue } from './queue';
export class TrackerCore {
private queue: TrackerQueue;
private config: SDKConfig;
private uploadTimer: ReturnType<typeof setInterval> | null = null;
private plugins: Map<string, ITrackerPlugin> = new Map();
private userInfo: UserInfo = {};
private timers: Map<string, number> = new Map();
// 注入的是"平台接口"而非具体实现
constructor(
private platform: IPlatformAdapter,
config: SDKConfig
) {
this.config = config;
this.queue = new TrackerQueue({
maxSize: config.maxCacheSize || 10000,
persist: platform.storage, // 依赖注入
});
}
// 初始化:加载缓存 + 启动定时器
async init(): Promise<void> {
await this.queue.loadFromStorage();
// 定时上报
this.uploadTimer = setInterval(
() => this.flush(),
this.config.uploadInterval || 30000
);
// 注册生命周期回调(各平台适配器注入)
this.platform.lifecycle.onForeground(() => {
// 从后台切到前台时立即上报已缓存事件
this.flush();
});
this.platform.lifecycle.onBackground(() => {
// 进入后台时立即上报
this.flush();
});
// 自动采集(如适用)
if (this.config.autoTrack?.appLaunch) {
this.track({
event: '$AppLaunch',
properties: {
timestamp: Date.now(),
launchType: this.platform.device.getLaunchType(),
},
});
}
}
track(event: TrackEvent): void {
if (Math.random() > (this.config.sampleRate || 1.0)) return;
const enrichedEvent = this.enrichEvent(event);
// 插件拦截
for (const plugin of this.plugins.values()) {
const shouldProceed = plugin.beforeTrack?.(enrichedEvent);
if (shouldProceed === false) return; // 插件决定放弃
}
this.queue.push(enrichedEvent);
// 达到批量上报阈值立即触发
if (this.queue.size() >= (this.config.batchSize || 50)) {
this.flush();
}
}
private enrichEvent(event: TrackEvent): TrackEvent {
return {
...event,
uuid: this.generateUUID(),
timestamp: event.timestamp || Date.now(),
user: {
...this.userInfo,
...event.user,
},
properties: {
...event.properties,
'$platform': this.platform.device.getPlatform(),
'$os_version': this.platform.device.getOSVersion(),
'$network_type': this.platform.device.getNetworkType(),
'$screen_width': this.platform.device.getScreenWidth(),
'$screen_height': this.platform.device.getScreenHeight(),
'$tz_offset': new Date().getTimezoneOffset(),
},
};
}
async flush(): Promise<void> {
if (this.queue.isEmpty()) return;
const batch = this.queue.drain(this.config.batchSize || 50);
// 压缩
const compressed = await compress(batch);
// 使用平台网络接口发送
const success = await this.platform.network.post(
this.config.serverUrl,
compressed
);
if (!success) {
// 发送失败,重新入队
this.queue.reEnqueue(batch);
}
}
use(plugin: ITrackerPlugin): void {
if (this.plugins.has(plugin.name)) {
console.warn(`[TrackerCore] Plugin ${plugin.name} already registered`);
return;
}
this.plugins.set(plugin.name, plugin);
plugin.install?.(this);
}
}
四、核心知识点拆解
4.1 平台抽象层 (Platform Abstraction)
平台抽象层是跨端SDK中最关键的代码。它定义了一组接口,各平台分别实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// IPlatformAdapter.ts — 平台适配器接口定义
// 这是Core层唯一能接触的"平台相关"抽象
export interface IPlatformAdapter {
// 网络
network: {
post(url: string, data: any): Promise<boolean>;
get(url: string): Promise<any>;
isOnline(): boolean;
};
// 存储
storage: {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<void>;
removeItem(key: string): Promise<void>;
getSize(): Promise<number>;
};
// 设备信息
device: {
getPlatform(): string;
getOSVersion(): string;
getDeviceModel(): string;
getNetworkType(): 'wifi' | '4g' | '5g' | '3g' | 'offline';
getScreenWidth(): number;
getScreenHeight(): number;
getLaunchType(): 'cold' | 'warm';
};
// 生命周期
lifecycle: {
onForeground(callback: () => void): void;
onBackground(callback: () => void): void;
onLowMemory(callback: () => void): void;
};
// 线程
thread: {
runInBackground<T>(fn: () => Promise<T>): Promise<T>;
runOnMainThread(fn: () => void): void;
};
}
各平台的存储适配器实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 小程序适配器
export class WeAppStorageAdapter implements IStorage {
async getItem(key: string): Promise<string | null> {
try {
const { data } = await wx.getStorage({ key });
return data as string;
} catch {
return null;
}
}
async setItem(key: string, value: string): Promise<void> {
await wx.setStorage({ key, data: value });
}
async removeItem(key: string): Promise<void> {
await wx.removeStorage({ key });
}
async getSize(): Promise<number> {
const info = await wx.getStorageInfo();
return info.currentSize;
}
}
// React Native适配器
export class RNStorageAdapter implements IStorage {
async getItem(key: string): Promise<string | null> {
return await AsyncStorage.getItem(key);
}
async setItem(key: string, value: string): Promise<void> {
await AsyncStorage.setItem(key, value);
}
async removeItem(key: string): Promise<void> {
await AsyncStorage.removeItem(key);
}
async getSize(): Promise<number> {
const keys = await AsyncStorage.getAllKeys();
let size = 0;
for (const key of keys) {
const val = await AsyncStorage.getItem(key);
size += (key.length + (val?.length || 0)) * 2; // UTF-16
}
return Math.round(size / 1024); // 返回KB
}
}
4.2 插件系统架构
插件系统是SDK扩展性的保障。一个好的插件设计应该是”三明治结构”:
graph LR
A[宿主App] -->|track()| B[插件A.beforeTrack]
B --> C[插件B.beforeTrack]
C --> D[Core.track]
D --> E[插件A.afterTrack]
E --> F[插件C.afterTrack]
F --> G[网络上传]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 插件接口
export interface ITrackerPlugin {
readonly name: string;
readonly version?: string;
// 安装时调用
install?(core: TrackerCore): void;
// 事件追踪钩子——在Core处理前/后调用
beforeTrack?(event: TrackEvent): boolean | void;
afterTrack?(event: TrackEvent): void;
// 上报钩子
beforeUpload?(batch: TrackEvent[]): TrackEvent[] | void;
afterUpload?(success: boolean, batch: TrackEvent[]): void;
}
// 插件示例:自动采集应用内购事件插件
export class InAppPurchasePlugin implements ITrackerPlugin {
name = 'in_app_purchase';
version = '1.0.0';
private core: TrackerCore | null = null;
install(core: TrackerCore): void {
this.core = core;
// 注册平台支付回调(通过platform adapter实现)
// 这里的平台适配器已由Core初始化时注入了生命周期
const platform = (core as any).platform;
if (platform.purchase) {
platform.purchase.onPurchaseComplete((product: any) => {
core.track({
event: '$Purchase',
properties: {
product_id: product.id,
price: product.price,
currency: product.currency,
transaction_id: product.transactionId,
},
});
});
}
}
beforeTrack(event: TrackEvent): boolean | void {
// 对购买事件做数据校验
if (event.event === '$Purchase' && !event.properties?.transaction_id) {
console.warn('[PurchasePlugin] Missing transaction_id');
return false; // 拦截无效数据
}
}
}
4.3 事件队列管理
队列管理是SDK可靠性的核心——确保事件不会因为网络异常或App闪退而丢失。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// TrackerQueue.ts — 线程安全的事件缓冲队列
export class TrackerQueue {
private events: TrackEvent[] = [];
private isDraining: boolean = false;
private persistPromise: Promise<void> = Promise.resolve();
constructor(
private options: {
maxSize: number;
persist: IStorage;
}
) {}
push(event: TrackEvent): void {
this.events.push(event);
// 限制队列大小,防止内存溢出
if (this.events.length > this.options.maxSize) {
const removed = this.events.shift();
console.warn(`[TrackerQueue] Queue full, dropped oldest event: ${removed?.event}`);
}
// 异步持久化(不阻塞主流程)
this.persistPromise = this.persistQueue();
}
drain(count: number): TrackEvent[] {
if (this.isDraining) return [];
this.isDraining = true;
const batch = this.events.splice(0, count);
this.isDraining = false;
return batch;
}
reEnqueue(events: TrackEvent[]): void {
// 失败的事件放回队列前端(保证顺序)
this.events.unshift(...events);
// 更新持久化
this.persistPromise = this.persistQueue();
}
size(): number {
return this.events.length;
}
isEmpty(): boolean {
return this.events.length === 0;
}
// 关键方法:App被杀死后恢复
async loadFromStorage(): Promise<void> {
try {
const data = await this.options.persist.getItem('@tracker/queue');
if (data) {
const parsed = JSON.parse(data);
if (Array.isArray(parsed)) {
this.events = parsed;
}
}
} catch {
// 解析失败则丢弃
this.events = [];
}
}
private async persistQueue(): Promise<void> {
if (this.events.length === 0) return;
// 截取最近的500条持久化(避免写入过多)
const toPersist = this.events.slice(-500);
await this.options.persist.setItem(
'@tracker/queue',
JSON.stringify(toPersist)
);
}
}
4.4 数据压缩策略
在网络上传前做压缩,可以减少带宽消耗和电池损耗:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// compress.ts — 多级压缩策略
export async function compress(events: TrackEvent[]): Promise<Uint8Array> {
// 阶段1: 结构压缩(去除冗余字段名)
const compacted = compactStructure(events);
// 阶段2: JSON序列化
const jsonStr = JSON.stringify(compacted);
// 阶段3: 如果超过阈值,启用gzip压缩
if (jsonStr.length > 1024) {
return await gzipCompress(jsonStr);
}
return new TextEncoder().encode(jsonStr);
}
// 字段名短化——减少每个事件约40%的体积
function compactStructure(events: TrackEvent[]): any[] {
const fieldMapping: Record<string, string> = {
'event': 'e',
'properties': 'p',
'timestamp': 't',
'uuid': 'u',
'user': 'us',
'userId': 'ui',
'eventName': 'en',
'duration': 'd',
'$platform': 'plat',
'$os_version': 'os',
'$network_type': 'net',
};
return events.map(event => {
const compacted: any = {};
for (const [key, value] of Object.entries(event)) {
if (fieldMapping[key]) {
compacted[fieldMapping[key]] = value;
} else {
compacted[key] = value; // 不认识的key原样保留
}
}
// 递归处理properties
if (compacted.p) {
compacted.p = compactProperties(compacted.p, fieldMapping);
}
return compacted;
});
}
五、实战案例:构建完整的跨端统计SDK
5.1 项目结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
tracker-sdk/
├── core/ # 核心逻辑(TypeScript,所有平台共享)
│ ├── tracker-core.ts # 核心类
│ ├── queue.ts # 事件队列
│ ├── compress.ts # 压缩策略
│ ├── plugins/ # 内置插件
│ │ ├── auto-track.ts # 自动采集
│ │ ├── session.ts # 会话管理
│ │ └── debug.ts # 调试模式
│ └── types/ # 类型定义
│ ├── sdk-config.ts
│ ├── track-event.ts
│ └── plugin.ts
│
├── adapters/ # 各平台适配器
│ ├── ios/ # iOS(Swift)
│ ├── android/ # Android(Kotlin)
│ ├── weapp/ # 小程序
│ │ ├── adapter.ts
│ │ ├── storage.ts
│ │ ├── network.ts
│ │ └── lifecycle.ts
│ └── web/ # Web
│
├── packages/ # 各平台SDK包
│ ├── tracker-ios/ # CocoaPods
│ ├── tracker-android/ # Maven
│ ├── tracker-weapp/ # npm / miniprogram
│ └── tracker-web/ # npm
│
└── scripts/
├── build-core.ts # 构建核心层
└── publish.ts # 发布脚本
5.2 在宿主App中集成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// iOS端集成(Swift)
// AppDelegate.swift
import TrackerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Tracker.shared.initialize(
config: SDKConfig(
appKey: "your_app_key",
serverUrl: "https://tracker.example.com/v1/batch",
autoTrack: AutoTrackConfig(
appLaunch: true,
pageViews: true,
clicks: true
)
)
)
// 注册插件
Tracker.shared.use(InAppPurchasePlugin())
return true
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 小程序端集成
// app.js
import { TrackerSDK } from '@company/tracker-weapp';
App({
onLaunch() {
const tracker = new TrackerSDK({
appKey: 'your_app_key',
serverUrl: 'https://tracker.example.com/v1/batch',
autoTrack: {
appLaunch: true,
pageViews: true,
clicks: true,
},
});
tracker.use(new InAppPurchasePlugin());
this.tracker = tracker;
},
});
5.3 数据上报链路验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 端到端测试:验证事件从采集到上报的全链路
// 在CI中运行
async function testSDKDataFlow() {
// 1. 模拟平台环境
const mockAdapter = createMockPlatformAdapter();
const core = new TrackerCore(mockAdapter, testConfig);
await core.init();
// 2. 触发事件
core.track({
event: 'test_event',
properties: { value: 42, label: 'test' },
});
// 3. 触发上报
await core.flush();
// 4. 验证网络层收到数据
expect(mockAdapter.network.lastPostUrl).toBe(
'https://tracker.example.com/v1/batch'
);
const sentData = JSON.parse(
new TextDecoder().decode(mockAdapter.network.lastPostBody)
);
// 5. 验证数据完整性
expect(sentData.events).toHaveLength(1);
expect(sentData.events[0].event).toBe('test_event');
expect(sentData.events[0].properties.value).toBe(42);
expect(sentData.events[0].user.userId).toBeDefined();
// 6. 验证压缩
expect(sentData.events[0].properties['$platform']).toBeDefined();
}
六、底层原理
6.1 依赖注入与IoC
跨端SDK的核心层使用控制反转实现平台无关性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 核心层不创建任何平台相关对象,全部由外部注入
class TrackerCore {
// 通过构造函数注入平台适配器
constructor(
private platform: IPlatformAdapter,
private config: SDKConfig
) {
// 不会出现 new UserDefaults() / wx.getSystemInfo() 等平台代码
}
}
// 各平台的SDK入口完成注入
// 小程序端
class WeAppTrackerSDK {
constructor(config: SDKConfig) {
const adapter = new WeAppPlatformAdapter();
this.core = new TrackerCore(adapter, config);
}
}
// iOS端
class IOSTrackerSDK {
constructor(config: SDKConfig) {
const adapter = new iOSPlatformAdapter();
this.core = new TrackerCore(adapter, config);
}
}
这样做的好处:
- Core层所有代码都可以写单元测试(传入Mock适配器)
- 新增平台只需实现适配器接口,Core层零修改
- 适配器可以针对平台做性能优化(比如小程序用wx.request而非fetch)
6.2 线程模型的跨端抽象
不同平台的线程模型差异很大:
| 操作 | iOS | Android | 小程序 | Flutter |
|---|---|---|---|---|
| UI操作 | 主线程 | 主线程 | 渲染线程 | GPU Runner |
| 网络请求 | 任何线程 | 任何线程 | 逻辑线程 | IO Runner |
| 文件读写 | 不建议主线程 | 不建议主线程 | 逻辑线程 | IO Runner |
| 大量计算 | 后台线程 | AsyncTask | 逻辑线程 | Worker Isolate |
SDK层需要抽象这一切:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// ThreadAdapter.ts — 线程抽象
interface IThreadAdapter {
background<T>(fn: () => T): Promise<T>;
main(fn: () => void): void;
}
// 小程序线程适配器
class WeAppThreadAdapter implements IThreadAdapter {
async background<T>(fn: () => T): Promise<T> {
// 小程序中没有真正的多线程,但可以用Worker
// 简单场景下直接在逻辑线程执行
return fn();
}
main(fn: () => void): void {
// setData等UI操作在逻辑线程完成,不用切线程
fn();
}
}
// Android线程适配器
class AndroidThreadAdapter implements IThreadAdapter {
async background<T>(fn: () => T): Promise<T> {
return new Promise((resolve, reject) => {
threadPool.execute(() => {
try { resolve(fn()); }
catch (e) { reject(e); }
});
});
}
main(fn: () => void): void {
new Handler(Looper.getMainLooper()).post(fn);
}
}
七、高频面试题解析
Q1: 跨端SDK的版本策略如何设计?
A:采用语义化版本(SemVer)+ 平台后缀的模式:
- Core层独立版本号(如 2.1.0)
- 各平台包版本号 = Core版本 + 平台定制后缀(如 2.1.0.ios1)
- Changelog必须标明每个版本对应的Core版本
- 版本兼容性测试矩阵:3个Core版本 × 每个平台2个版本
Q2: SDK如何保证向后兼容?
A:1) 核心层接口(Core的公开方法)一旦发布永不改签名,新增用重载;2) 适配器接口禁止删除方法,新增方法给默认实现;3) 事件数据模型用Protobuf,字段新增而不是废弃;4) 版本号中用deprecated注解标注废弃方法,保留至少2个主版本。
Q3: SDK包体积如何优化?
A:1) 使用Tree-shaking(构建时移除未使用的插件);2) 压缩字段名(前面介绍的compact策略);3) 非核心功能拆为可选插件,按需加载;4) 小程序端利用分包,SDK放入独立分包;5) Flutter插件使用FFI减少Dart代码量。
Q4: 多个SDK共存时的冲突怎么解决?
A:建立SDK沙箱机制:1) 所有全局名称加前缀(ttTracker_);2) 使用单独的命名空间/包名;3) 网络请求使用独立的URLSession配置;4) 存储使用专属Key前缀;5) 依赖的第三方库必须shade/relocate包名。
八、总结与扩展
跨端SDK架构的设计本质是”在变化中寻找不变”——把平台差异封装到适配器层,把业务逻辑固化在核心层。
架构演进的生命周期:
- 单体阶段:每个平台一套代码——迭代快但维护成本高
- 抽象阶段:提取Core层,多平台共享——效率提升但仍有重复
- 插件阶段:Core + 插件系统——扩展能力强
- 自动生成阶段:基于接口定义自动生成各平台适配器代码——完全消除重复
不要做的:
- ❌ Core层与平台代码混在一起(无法测试,无法迁移)
- ❌ 适配器做成全量接口暴露出平台细节(破坏抽象)
- ❌ 插件系统过于灵活导致Core层不稳定(插件不要能修改Core内部状态)
- ❌ 追求100%的功能共享(有些平台特异的功能就该留给平台)
优秀的跨端SDK架构让开发者感觉”在不同平台上写同一份代码”,而不是”在不同平台上为同一件事反复打工”。