鸿蒙造物者之路 09 · 分身术
续写章节|沿用「造物者」系列的武侠讲解体,面向电子信息 / 嵌入式方向的新手
=================前情提要===============
数据、网络、通知、后台任务、权限——应用终于"能用"了。
老陈说下一章是鸿蒙的杀手锏,也是这本书最该记住的一章。
分布式。
“让你的应用,同时跑在手机、手表、平板和开发板上——而它们看起来像一个设备。”
========================================
这一章你会得到什么
🎯 学完这一章,你应该能:
- ☐ 理解分布式软总线的四个阶段:发现 → 认证 → 组网 → 传输
- ☐ 理解设备虚拟化:为什么手表能调用手机的摄像头
- ☐ 会用 DeviceManager 发现并认证设备
- ☐ 会跨设备启动 Ability 和做跨端迁移(应用流转)
- ☐ 会用分布式 KVStore 让数据自动同步
**🧰 需要准备:**最好有两台鸿蒙设备(手机 + 平板,或者两台手机),同 Wi-Fi。没有也可以用模拟器或多开。
**⏱ 预计用时:**约 5 小时
📊 难度:★★★★★ 概念抽象,但这才是鸿蒙的命根子
一、先把我脑子里的"设备"敲碎
"在开始之前,"老陈说,“我要你先忘掉一件事。”
“忘掉什么?”
“忘掉’一个设备就是一台机器’这个观念。”
他把四台设备摆在桌上:手机、平板、手表、还有我那块 Hi3861。
“在鸿蒙的世界观里,这四样东西可以是一个设备。”
“……怎么可能,它们是四块独立的硬件。”
"硬件是独立的,但系统可以让它们’看起来’是一个。"老陈说,“这就是鸿蒙最核心的设计——”
他在白板上写下八个字:
硬件互助,资源共享
“具体表现成什么样?我给你举四个例子。”
1.1 四个"反常识"的场景
【场景一:借摄像头】
你的手表要扫码,但摄像头只有 30 万像素。
→ 手表直接【调用手机的摄像头】,拍完传回来
→ 手表上的代码:camera.open() —— 一行没改
【场景二:借屏幕】
你在手机上看到一半的视频,走到客厅。
→ 视频【流转到智慧屏】,手机变成遥控器
→ 视频进度不丢,因为状态也一起过去了
【场景三:借算力】
你的门锁需要跑一个人脸识别,但它只有 128KB 内存。
→ 门锁把图像传给手机,让【手机算】,只拿结果
→ 门锁依然只是个门锁,但有了 AI
【场景四:借传感器】
你的手机没有温湿度传感器。
→ 手机【直接读客厅那个开发板上的 SHT30】
→ 对手机上的 App 来说,这个传感器就像长在自己身上
我盯着最后一条。
“……第四种,我能做吗?”
"这就是你这周的目标。"老陈说。
二、分布式软总线:一切的底座
“实现上面四件事的技术,叫分布式软总线。”
2.1 它是什么
"先理解’总线’这个词。"老陈说,“你在第 05 章学过 I2C 总线——多个设备挂在同一组线上,靠地址互相通信。”
“嗯。”
“软总线就是把’总线’这个概念,从芯片内部搬到了设备之间。”
【硬件总线(I2C)】 【软总线(分布式)】
同一块 PCB 上 同一个局域网 / 蓝牙范围内
物理的 SDA/SCL 两根线 虚拟的"总线"
靠 7 位地址区分设备 靠 deviceId 区分设备
传几个字节 传任意数据、传能力、传硬件
“而且软总线是’软’的——它底下可能是 Wi-Fi、可能是蓝牙、可能是以太网,但它对上层是透明的。”
┌──────────────────────────────────────┐
│ 应用 / 系统服务 │
├──────────────────────────────────────┤
│ 分布式软总线(统一 API) │ ← 上层只看到"总线"
├──────────────────────────────────────┤
│ Wi-Fi P2P │ 蓝牙 BR/BLE │ 以太网 │ ← 底层自动选
└──────────────────────────────────────┘
"它会自动选最快的那条路。"老陈说,“两个设备都在同一个 Wi-Fi 下,走 Wi-Fi;只有蓝牙,走蓝牙;两者都有,自动挑。”
2.2 四个阶段
“软总线的工作,分四步。”
① 发现 ② 连接 ③ 组网 ④ 传输
发现谁在 建立信任 组成一个 在网内传数据
│ │ 超级终端 │
▼ ▼ ▼ ▼
设备发现 设备认证 逻辑组网 数据传输
(谁在附近) (你是谁) (我们是一伙的)(说正事)
① 发现:谁在附近
“设备上线后,会广播自己的存在,同时监听别人的广播。”
“用什么协议?”
"基于 mDNS、CoAP 这类局域网发现协议,配合蓝牙扫描。"老陈说,“技术细节你不用管,你只需要知道——打开 Wi-Fi 和蓝牙,同一局域网内的设备就能互相发现。”
② 连接:你是谁(认证)
"这一步最关键,也最容易被忽略。"老陈的语气严肃了。
“**发现不等于信任。**你家的门锁不能因为’隔壁老王的设备也在附近’就把门开了。”
“所以要认证?”
"对。设备必须’配对’之后才能通信。"老陈列了几种方式:
| 认证方式 | 场景 | 安全性 |
|---|---|---|
| 同一帐号 | 同一个华为帐号下的手机、平板、耳机 | 高(云端已确认身份) |
| PIN 码 | 手动输入对方显示的 6 位数字 | 高 |
| 碰一碰(NFC) | 手机碰一下设备 | 高(物理接触) |
| 扫码 | 扫设备上的二维码 | 中高 |
| 附近设备(免认证) | 同局域网、低敏感操作 | 低(受限使用) |
“**认证通过之后,双方交换证书。**之后所有通信都是加密的。”
③ 组网:组成"超级终端"
“认证完之后,这些设备就组成了一个逻辑上的网络,鸿蒙管它叫’超级终端’。”
┌─────────────────────────────────┐
│ 超级终端 │
│ │
│ 手机 ────┬──── 平板 │
│ │ │
│ 手表 ────┼──── 智慧屏 │
│ │ │
│ 开发板 ───┴──── 音箱 │
│ │
└─────────────────────────────────┘
每个设备都知道:
· 伙伴有哪些
· 每个伙伴有什么能力(摄像头?传感器?屏幕?)
· 自己怎么调用它们
“**在系统设置里,你能看到一个’超级终端’的界面,可以把设备拖在一起组网。**那不是 UI 特效,那是真实的逻辑网络。”
④ 传输:说正事
“组网之后,就是传数据。软总线提供几类能力:”
| 能力 | 用途 | 类比 |
|---|---|---|
| 消息(Message) | 小数据、即时 | 发微信 |
| 字节(Bytes) | 中等数据 | 传文件 |
| 流(Stream) | 音视频流 | 视频通话 |
| 文件(File) | 大文件传输 | 传视频 |
| RPC / 分布式对象 | 跨设备调用方法 | 就像调本地函数 |
"最后这个最神奇——跨设备 RPC。"老陈说,“你在手机上调一个方法,实际执行在平板上,但你写的代码跟调本地方法一模一样。”
三、设备虚拟化:把别人的硬件当自己的
“现在讲最容易让人震撼的部分。”
3.1 分布式硬件
“设备虚拟化,就是把一个设备的硬件能力,'映射’到另一个设备上。”
手机 开发板
┌──────────┐ ┌──────────┐
│ 应用 │ │ SHT30 │
│ │ │ 传感器 │
│ 调用 │ └────┬─────┘
│ 温湿度API │ │
└─────┬────┘ │
│ │
└──────── 软总线 ──────────────┘
↑
分布式硬件虚拟化层
“对手机上的应用来说,它调用的就是一个’本地传感器’。它不知道这个传感器其实在两米外的开发板上。”
“具体怎么实现的?”
"三个角色:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Source │ │ SoftBus │ │ Sink │
│ (提供硬件)│───►│ (软总线) │───►│ (使用硬件)│
│ 开发板 │ │ │ │ 手机 │
└─────────────┘ └─────────────┘ └─────────────┘
真实硬件在这 负责传输 虚拟硬件在这
“Source 端把自己真实的硬件注册到软总线上;Sink 端从软总线拿到一个’虚拟硬件’,用起来跟真的一样。”
3.2 常见的分布式硬件
| 分布式硬件 | 能力 | 典型场景 |
|---|---|---|
| 分布式相机 | 调用别的设备的摄像头 | 手表扫码、多机位拍摄 |
| 分布式麦克风 / 扬声器 | 借用音频输入输出 | 用手机麦克风给电视语音输入 |
| 分布式屏幕 | 把画面投到别的屏 | 手机 → 智慧屏 |
| 分布式传感器 | 读取远端的传感器 | 手机读开发板的温湿度 |
| 分布式存储 | 访问别设备的文件 | 手机直接打开平板上的文档 |
| 分布式 GPU / NPU | 借算力 | 小设备跑 AI 推理 |
“你的项目要用的,是’分布式传感器’这一项。”
四、动手:让手机读到开发板的温湿度
"现在开始干活。"老陈说,“分四步。”
4.1 第一步:发现并认证设备
import distributedDeviceManager from '@ohos.distributedDeviceManager';
import { BusinessError } from '@ohos.base';
class DeviceHelper {
private dm: distributedDeviceManager.DeviceManager | null = null;
// ---------- 创建设备管理器 ----------
init(bundleName: string): void {
try {
this.dm = distributedDeviceManager.createDeviceManager(bundleName);
// 监听设备状态变化(上线 / 下线 / 认证完成)
this.dm.on('deviceStateChange', (data) => {
console.info(`设备状态变化: ${JSON.stringify(data)}`);
// data.action:
// 0 = 上线(ONLINE) 1 = 就绪(READY)
// 2 = 下线(OFFLINE) 3 = 状态变化(CHANGE)
});
// 监听发现到的设备
this.dm.on('deviceFound', (data) => {
console.info(`发现设备: ${JSON.stringify(data)}`);
});
// 监听认证结果
this.dm.on('deviceAuthenticate', (data) => {
console.info(`认证结果: ${JSON.stringify(data)}`);
});
} catch (err) {
console.error(`创建失败: ${(err as BusinessError).message}`);
}
}
// ---------- 获取已认证(可信)设备列表 ----------
getTrustedDevices(): distributedDeviceManager.DeviceBasicInfo[] {
if (!this.dm) return [];
try {
return this.dm.getAvailableDeviceListSync(); // 同步接口
} catch (err) {
console.error(`获取列表失败: ${(err as BusinessError).message}`);
return [];
}
}
// ---------- 开始发现周边设备 ----------
startDiscover(): void {
if (!this.dm) return;
let discoverParam: Record<string, number> = {
'discoverTargetType': 1 // 1 = 发现所有类型
};
let filterOptions: Record<string, number> = {
'availableStatus': 1, // 只要在线的
'discoverDistance': 0, // 0 = 近距离
'authenticationStatus': 0 // 0 = 未认证(一起发现)
};
try {
this.dm.startDiscovering(discoverParam, filterOptions);
} catch (err) {
console.error(`发现失败: ${(err as BusinessError).message}`);
}
}
stopDiscover(): void {
this.dm?.stopDiscovering();
}
// ---------- 认证一个设备(PIN 码方式) ----------
authenticate(deviceId: string, onPin: (pin: string) => void): void {
if (!this.dm) return;
let extraInfo: Record<string, string> = {
'targetPkgName': 'com.example.myapp',
'appName': '温湿度监控',
'appDescription': '需要连接你的传感器设备',
'business': '0'
};
let authParam: Record<string, number | string> = {
'authType': 1, // 1 = PIN 码认证
'extraInfo': JSON.stringify(extraInfo)
};
try {
this.dm.authenticateDevice(deviceId, authParam, (err, data) => {
if (err) {
console.error(`认证失败: ${JSON.stringify(err)}`);
return;
}
// 对方设备上会显示一个 PIN 码,这里拿到了要展示给用户
let pin = data?.pin as string;
if (pin) { onPin(pin); }
});
} catch (err) {
console.error(`认证异常: ${(err as BusinessError).message}`);
}
}
destroy(): void {
if (this.dm) {
this.dm.off('deviceStateChange');
this.dm.off('deviceFound');
this.dm.off('deviceAuthenticate');
this.dm = null;
}
}
}
"几个要点:"老陈说。
1. createDeviceManager 要传 bundleName
2. 监听有三个:deviceStateChange / deviceFound / deviceAuthenticate
3. getAvailableDeviceListSync() 拿【已认证】的设备
4. startDiscovering() 发现【未认证】的周边设备
5. authenticateDevice() 发起认证,回调里拿到 PIN 码
6. 用完 destroy(),注销监听
4.2 第二步:跨设备启动 Ability
“认证之后,你可以直接在别的设备上启动一个 Ability。”
import common from '@ohos.app.ability.common';
import Want from '@ohos.app.ability.Want';
async startRemoteAbility(context: common.UIAbilityContext,
deviceId: string): Promise<void> {
let want: Want = {
deviceId: deviceId, // ← 关键:指定目标设备!
bundleName: 'com.example.myapp',
abilityName: 'SensorAbility',
parameters: {
action: 'start_collect',
interval: 5000
}
};
try {
await context.startAbility(want);
console.info('远端 Ability 已启动');
} catch (err) {
console.error(`启动失败: ${(err as BusinessError).code}`);
}
}
"就多了一个 deviceId 字段。"老陈说,“这就是分布式的威力——API 几乎没变,但目标从’本机’变成了’网内任意一台设备’。”
4.3 第三步:跨端迁移(应用流转)
“迁移是指:把正在运行的界面,从设备 A 搬到设备 B,状态不丢。”
手机上看到一半
│
│ 用户在"任务中心"选择"流转到平板"
▼
手机:onContinue() 保存状态 ──► 软总线 ──► 平板:onCreate()/onNewWant() 恢复状态
│ │
└── 手机上的页面关闭 ───────────────────────────└── 平板上的页面打开,进度一致
“代码长这样:”
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
import type AbilityConstant from '@ohos.app.ability.AbilityConstant';
export default class EntryAbility extends UIAbility {
// ===== 源端:我要被迁移走了,保存状态 =====
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
console.info('onContinue: 保存迁移状态');
// 把要带过去的数据塞进 wantParam
wantParam['currentPage'] = 'DetailPage';
wantParam['deviceId'] = 'dev_001';
wantParam['scrollOffset'] = AppStorage.Get<number>('scrollOffset') ?? 0;
wantParam['draftText'] = AppStorage.Get<string>('draftText') ?? '';
// 返回 AGREE 表示同意迁移;REJECT 表示拒绝
return AbilityConstant.OnContinueResult.AGREE;
}
// ===== 目标端:我接手了,恢复状态 =====
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('onCreate: 恢复迁移状态');
// launchParam.launchReason 会告诉你这是不是一次迁移
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
let page = want.parameters?.['currentPage'] as string;
let devId = want.parameters?.['deviceId'] as string;
let off = want.parameters?.['scrollOffset'] as number;
AppStorage.SetOrCreate('restoredPage', page);
AppStorage.SetOrCreate('deviceId', devId);
AppStorage.SetOrCreate('scrollOffset', off);
}
}
// 如果目标端已经有这个 Ability 在跑,会走 onNewWant
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('onNewWant: 接收新的迁移请求');
// 同样恢复状态
}
}
“wantParam 有大小限制(一般几十 KB)。“老陈提醒,”**只能装状态,不能装大数据。**要传大块数据,用分布式文件或分布式数据库。”
4.4 第四步:分布式数据同步
“最后一步,也是最常用的:让数据自动在多台设备间同步。”
import distributedKVStore from '@ohos.data.distributedKVStore';
class DistributedData {
private kvManager: distributedKVStore.KVManager | null = null;
private kvStore: distributedKVStore.SingleKVStore | null = null;
// ---------- 初始化 ----------
async init(bundleName: string, context: common.Context): Promise<void> {
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: bundleName
};
this.kvManager = distributedKVStore.createKVManager(kvManagerConfig);
const options: distributedKVStore.Options = {
createIfMissing: true, // 不存在就创建
encrypt: true, // 加密存储
backup: false,
autoSync: true, // ← 自动同步!
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
};
this.kvStore = await this.kvManager.getKVStore<distributedKVStore.SingleKVStore>(
'sensor_store', options
);
// 监听数据变化(别的设备改了,这边会收到)
this.kvStore.on('dataChange',
distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE,
(data) => {
console.info(`数据变化: ${JSON.stringify(data)}`);
// data.insertEntries / updateEntries / deleteEntries
// data.deviceId —— 哪个设备改的
}
);
}
// ---------- 写数据 ----------
async put(key: string, value: string | number | boolean | Uint8Array): Promise<void> {
await this.kvStore?.put(key, value);
}
// ---------- 读数据 ----------
async get(key: string): Promise<distributedKVStore.ValueType | undefined> {
return await this.kvStore?.get(key);
}
// ---------- 手动同步到指定设备 ----------
sync(deviceIds: string[], mode: distributedKVStore.SyncMode): void {
if (!this.kvStore) return;
this.kvStore.sync(deviceIds, mode, 1000); // 超时 1000ms
// SyncMode:
// PUSH_ONLY 只推(我改了,发给你们)
// PULL_ONLY 只拉(你们改了,我要过来)
// PUSH_PULL 又推又拉(最常用)
}
// ---------- 清理 ----------
destroy(): void {
this.kvStore?.off('dataChange');
this.kvManager?.closeKVStore('sensor_store');
}
}
// ===== 使用 =====
let dd = new DistributedData();
await dd.init('com.example.myapp', getContext(this));
// 本机写入,会自动同步到组网内的其他设备
await dd.put('livingroom_temp', 26.5);
await dd.put('livingroom_humi', 61);
// 也可以手动触发同步
dd.sync(['device_id_1', 'device_id_2'], distributedKVStore.SyncMode.PUSH_PULL);
"autoSync: true 是关键。"老陈说,“开了它,你 put 一次,组网内所有设备都能看到。”
“那如果两台设备同时改同一个 key 呢?”
"冲突。"老陈说,“分布式系统避不开这个问题。KVStore 的处理是——”
冲突解决策略:
1. 时间戳 —— 后写的覆盖先写的(默认)
2. 设备优先级 —— 某些设备优先级更高
3. 业务自定义 —— 你自己实现合并逻辑
“**我的建议:设计 key 的时候就避免冲突。**比如按设备分 key——dev001_temp、dev002_temp,而不是共用一个 temp。”
五、分布式任务调度
“还有最后一块:让系统在合适的设备上跑合适的任务。”
┌─────────────────────────────────────────────┐
│ 分布式任务调度 │
├─────────────────────────────────────────────┤
│ · 查询网内各设备的能力(CPU/内存/屏幕/传感器) │
│ · 根据任务需求,挑最合适的设备执行 │
│ · 支持远程启动、远程迁移、远程停止 │
└─────────────────────────────────────────────┘
“举个实际例子:你的人脸识别任务需要 NPU。系统扫一圈发现——门锁没有 NPU、手机有——自动把任务丢给手机算。”
“这对我们意味着什么?”
"意味着你写代码的时候,可以’声明需求’而不是’指定设备’。"老陈说,“你说’我需要一个带 NPU 的设备’,系统帮你找。这在异构硬件环境下非常重要。”
六、安全:分布式的命门
"讲完能力,必须讲安全。"老陈的语气变重了。
“**分布式 = 你的数据会跑到别的设备上。**这带来三个风险:”
风险一:假冒设备 —— 一个恶意设备伪装成你的传感器
风险二:窃听 —— 通信被中间人截获
风险三:越权 —— 别的应用读到你的分布式数据
6.1 鸿蒙怎么防
| 层次 | 机制 |
|---|---|
| 设备层 | 设备认证(PIN/碰一碰/同帐号)+ 证书交换 |
| 通信层 | 软总线通信全程加密(基于会话密钥) |
| 数据层 | 数据分级:设备级 / 用户级 / 应用级;KVStore 支持 encrypt: true 和 SecurityLevel S1~S4 |
| 应用层 | 权限管控:分布式操作需要 DISTRIBUTED_DATASYNC 等权限 |
| 身份层 | tokenID:每个应用有唯一身份标识,跨设备也认 |
“权限别忘了申请:”
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "$string:distributed_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
“这是 user_grant 权限,要弹窗让用户同意。”
6.2 一条铁律
老陈在白板上写:
分布式的能力越强大,出错的代价就越大。
永远假设网络会断、设备会离线、对端可能是旧版本。
“具体怎么做?”
1. 每次调用前检查对端是否在线
2. 所有跨设备调用都要有超时
3. 所有跨设备调用都要有失败降级
4. 不要假设对端的应用版本和你一样
5. 敏感数据不要放在未加密的分布式存储里
七、常见坑
| 现象 | 原因 | 解法 |
|---|---|---|
| 发现不到设备 | ① 不在同一 Wi-Fi ② 蓝牙没开 ③ 防火墙隔离了局域网 |
确认网络;开蓝牙;关掉 AP 隔离 |
| 认证一直失败 | ① PIN 码输错 ② 两端应用签名不一致 ③ 时间不同步 |
检查 PIN;确保两端用同一套证书签名 |
getAvailableDeviceListSync 返回空 |
设备还没认证 | 先 authenticateDevice |
| 跨设备启动 Ability 失败 | ① deviceId 错了② 目标设备没装这个应用 ③ 目标 Ability 没 exported: true |
检查 deviceId;检查 exported |
| 数据没同步 | ① autoSync 没开② 没调 sync()③ 设备不在线 |
开 autoSync;手动 sync;确认在线 |
| 连接上了但很慢 | 走了蓝牙而不是 Wi-Fi | 确认两端在同一 Wi-Fi;检查信号 |
"排错顺序:"老陈总结,“先网络 → 再认证 → 再权限 → 最后看代码。”
=================小剧场模式=================
我:老师,我有个根本性的疑问。
老陈:说。
我:这些事,用普通的网络编程也能做啊。 手机和开发板连同一个 Wi-Fi,开发板起个 HTTP 服务器,手机去轮询——不也能读到温湿度吗?
老陈:能。 而且很多人就是这么干的。
我:那分布式软总线的价值在哪?
老陈:我给你算笔账。 你自己实现一套,要做什么?
我:(数)设备发现、设备认证、连接管理、断线重连、数据加密、冲突解决、多链路选择、权限管控……
老陈:还有设备能力查询、状态同步、跨设备调用、版本兼容、功耗优化。
我:……
老陈:这些鸿蒙全给你做好了,你只需要调用 API。 而且它是系统级的——所有应用共享同一套认证,用户配对一次,所有应用都能用。
我:所以价值是省掉了全部基础设施?
老陈:对。而且是统一标准的。 你自己实现的方案,只能你的应用用;软总线的方案,所有鸿蒙设备、所有应用都认。
我:就像……大家都说同一种语言?
老陈:就像第 01 章说的——让一堆设备像一个设备那样工作。 你现在明白那句话了吧?
========================================
八、本章速查
【软总线四步】
发现(谁在附近)→ 认证(你是谁)→ 组网(超级终端)→ 传输(说正事)
【关键 API】
distributedDeviceManager.createDeviceManager() 创建管理器
.getAvailableDeviceListSync() 已认证设备
.startDiscovering() 发现周边
.authenticateDevice() 认证(拿 PIN)
startAbility({ deviceId, bundleName, abilityName }) 跨设备启动
onContinue() / onCreate(launchReason=CONTINUATION) 跨端迁移
distributedKVStore + autoSync 数据自动同步
【设备虚拟化】
Source(提供硬件)→ 软总线 → Sink(虚拟硬件)
分布式相机 / 麦克风 / 屏幕 / 传感器 / 存储 / 算力
【权限】
ohos.permission.DISTRIBUTED_DATASYNC(user_grant,要弹窗)
【铁律】
假设网络会断、设备会离线、对端是旧版本
跨设备调用:必查在线 + 必设超时 + 必有降级
"这一章,"老陈合上本子,“如果这本书你只能记住一章,就记这一章。”
“为什么?”
"因为 APK、ArkTS、Ability、路由这些,别的系统也有。"他说,“但’把一堆设备变成一个设备’——这是鸿蒙真正不一样的地方。”
“下一章呢?”
老陈把那块 Hi3861 又推到我面前。
“你的温湿度数据,现在还在开发板上,对吧?”
“对。”
"分布式能让它跑到手机上。但你有没有想过——"他敲了敲板子,“开发板上跑的是 C 代码,手机上跑的是 ArkTS。它们俩,怎么直接对话?”
我愣住了。
“……对啊。我一直在想设备之间怎么连,但没想过——设备内部的两种代码,怎么打通。”
"这就是下一章。"老陈说。
“NAPI。南北打通。”
本章自检
过一遍下面的问题。答不上来的,回去翻对应小节——那些就是你这个章的欠账。
- ☐ 软总线的四个阶段分别做什么?
- ☐ 「发现」和「认证」的区别?为什么必须先认证?
- ☐ Want 里加了
deviceId会发生什么? - ☐ 跨端迁移时,保存状态用哪个回调?恢复又在哪?
- ☐
wantParam有没有大小限制?大数据该怎么传? - ☐ 分布式环境下,安全靠哪几层来保证?
- ☐ 说出一句总结:分布式到底想让什么变得像什么?
**➡️ 下一站:**第 10 章 · 南北打通 —— 让 C 和 ArkTS 直接对话。
=================剧情片段=================
导演:卡。这一章是全书的第一个高潮。
助理:软总线、设备虚拟化、跨端迁移、分布式数据——信息量很大,但主线很清楚。
导演:主线就是那四个字:硬件互助。 所有技术都是为它服务的。
助理:下一章 NAPI,是不是有点"降温"?
导演:不,是另一个高潮。 前面九章,南向和北向是两条平行线——
助理:对,C 语言和 ArkTS 各干各的。
导演:NAPI 把它们接起来。 打通之后,主角才能做出一个真正的软硬结合产品。
助理:而且这正好是他专业背景的优势?
导演:对。 会 C 又会 ArkTS、还能把两者接起来的人——这就是他在这个行业里最值钱的地方。
助理:那下一章要讲透 NAPI 的机制。
导演:N-API 是什么、为什么用它、怎么注册模块、类型转换、异步回调、线程安全——一个都不能少。
助理:还有个完整的软硬结合案例吧?
导演:有。从驱动到应用,一条线走到底。
========================================
未完待续。。。


