【HarmonyOS 6】UIAbility 跨设备连接详解(分布式软总线运用)
一、前言
我对于分布式软总线相当的亲切。2022 年搞开源鸿蒙的时候,就经常和分布式软总线打交道。在 HarmonyOS 中,UIAbility 跨设备连接,其实就是对底层开源鸿蒙,分布式软总线的能力封装。
二、首先理解跨设备链接的步骤
首先由 Distributed Service Kit 提供该能力的封装。
在进行跨设备链接之前,我们要对设备进行互信操作。这个在鸿蒙里叫做分布式设备管理,包含了设备的发现,配对,可信查询,解除配对等。详情可参见我之前写的文章:【HarmonyOS 5】鸿蒙分布式协同应用开发详解
进行完上面的可信操作,我们才能对于设备间的链接通信做处理。
三、专有名词理解:
DMS(Distributedsched Management Service):分布式组件管理框架,相当于跨设备协同的“中间人”,负责管理组件和建立连接
UIAbility:应用的界面交互核心,管生命周期、用户交互和界面渲染,跨设备协同本质就是两台设备的 UIAbility 在“对话”
四、环境准备步骤
工欲善其事,先把环境搭好:1、硬件:两台能登录华为账号的设备(A 和 B),需要支持 API 182、开发工具:DevEco Studio 4.1 及以上,public-SDK 更新到 API 18+3、设备连接:用 USB 线把两台设备连到 PC,打开蓝牙让设备互相识别组网 4、验证组网:PC 端执行 shell 命令,显示“remote device num = 1”就是组网成功
hdc shellhidumper -s 4700 -a "buscenter -l remote_device_info"
复制代码
也可通过前置步骤的分布式设备管理,来验证设备的可信。
五、源码步骤拆解:
1、 导入核心模块
首先要导入分布式服务相关的 Kit,不管是发起端还是接收端都需要:
import { abilityConnectionManager, distributedDeviceManager } from '@kit.DistributedServiceKit';import { common, AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';import { hilog } from '@kit.PerformanceAnalysisKit';
复制代码
2、 发现目标设备(设备 A 侧)
设备 A 要先找到设备 B 的 networkId,作为连接的关键参数:
let dmClass: distributedDeviceManager.DeviceManager;
// 初始化设备管理实例function initDmClass(): void { try { dmClass = distributedDeviceManager.createDeviceManager('com.example.remotephotodemo'); } catch (err) { hilog.error(0x0000, 'testTag', '创建设备管理实例失败: ' + JSON.stringify(err)); }}
// 获取设备B的networkIdfunction getRemoteDeviceId(): string | undefined { initDmClass(); if (!dmClass) return undefined; hilog.info(0x0000, 'testTag', '开始查找远程设备'); const deviceList = dmClass.getAvailableDeviceListSync(); if (!deviceList || deviceList.length === 0) { hilog.info(0x0000, 'testTag', '未找到可用设备'); return undefined; } // 这里取第一个设备,实际开发可做设备选择列表 return deviceList[0].networkId;}
复制代码
3、创建会话并连接(两端操作不同)
设备 A(发起端):创建会话+发起连接
@StorageLink('sessionId') sessionId: number = -1;
// 配置设备B的协同信息const peerInfo: abilityConnectionManager.PeerInfo = { deviceId: getRemoteDeviceId()!, // 设备B的networkId bundleName: 'com.example.remotephotodemo', // 必须和设备B应用一致 moduleName: 'entry', abilityName: 'EntryAbility', serviceName: 'collabTest' // 自定义服务名,两端要一致};
// 连接配置const connectOptions: abilityConnectionManager.ConnectOptions = { needSendData: true, startOptions: abilityConnectionManager.StartOptionParams.START_IN_FOREGROUND, parameters: { "newKey1": "value1" }};
// 发起连接async function connectRemoteAbility() { const context = this.getUIContext().getHostContext(); try { // 创建会话,获取sessionId this.sessionId = abilityConnectionManager.createAbilityConnectionSession( "collabTest", context, peerInfo, connectOptions ); hilog.info(0x0000, 'testTag', `创建会话成功,sessionId: ${this.sessionId}`); // 发起连接(会拉起设备B的应用) const connectResult = await abilityConnectionManager.connect(this.sessionId); if (!connectResult.isConnected) { hilog.info(0x0000, 'testTag', '连接失败'); return; } hilog.info(0x0000, 'testTag', '连接成功'); } catch (error) { hilog.error(0x0000, 'testTag', `连接异常: ${JSON.stringify(error)}`); }}
复制代码
设备 B(接收端):被拉起后接受连接
设备 A 发起连接后,设备 B 的应用会被协同拉起,触发onCollaborate生命周期函数:
export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(0x0000, 'testTag', '应用启动'); }
// 协同拉起时触发 onCollaborate(wantParam: Record<string, Object>): AbilityConstant.CollaborateResult { hilog.info(0x0000, 'testTag', '收到协同请求'); const collabParam = wantParam["ohos.extra.param.key.supportCollaborateIndex"] as Record<string, Object>; this.handleCollaborate(collabParam); return 0; }
// 处理协同连接 private async handleCollaborate(collabParam: Record<string, Object>) { const sessionId = this.createSessionFromParam(collabParam); if (sessionId === -1) { hilog.error(0x0000, 'testTag', '会话创建失败'); return; }
// 获取协同token,必须传入acceptConnect const collabToken = collabParam["ohos.dms.collabToken"] as string; try { await abilityConnectionManager.acceptConnect(sessionId, collabToken); hilog.info(0x0000, 'testTag', '接受连接成功'); AppStorage.setOrCreate('sessionId', sessionId); } catch (error) { hilog.error(0x0000, 'testTag', `接受连接失败: ${JSON.stringify(error)}`); } }
// 从协同参数创建会话 private createSessionFromParam(collabParam: Record<string, Object>): number { let sessionId = -1; const peerInfo = collabParam["PeerInfo"] as abilityConnectionManager.PeerInfo; const connectOptions = collabParam["ConnectOption"] as abilityConnectionManager.ConnectOptions;
if (!peerInfo || !connectOptions) return sessionId;
// 配置数据传输能力 connectOptions.needSendData = true; connectOptions.needSendStream = true;
try { sessionId = abilityConnectionManager.createAbilityConnectionSession( "collabTest", this.context, peerInfo, connectOptions ); } catch (error) { hilog.error(0x0000, 'testTag', `创建会话失败: ${JSON.stringify(error)}`); } return sessionId; }}
复制代码
4、 注册事件监听(两端都要加)
连接成功后,通过监听事件获取连接状态和消息:
function registerEventListeners(sessionId: number) { // 监听连接成功事件 abilityConnectionManager.on("connect", sessionId, (callbackInfo) => { hilog.info(0x0000, 'testTag', `会话${callbackInfo.sessionId}连接成功`); });
// 监听断开连接事件 abilityConnectionManager.on("disconnect", sessionId, (callbackInfo) => { hilog.info(0x0000, 'testTag', `会话${callbackInfo.sessionId}已断开`); });
// 监听接收消息事件 abilityConnectionManager.on("receiveMessage", sessionId, (callbackInfo) => { hilog.info(0x0000, 'testTag', `收到消息: ${callbackInfo.message}, 会话ID: ${callbackInfo.sessionId}`); // 这里可以处理业务逻辑,比如更新UI显示消息 });}
复制代码
5、 发送消息(两端都可发)
连接成功后,用sendMessage发送文本信息:
async function sendTestMessage(sessionId: number) { try { await abilityConnectionManager.sendMessage(sessionId, "这是来自设备A的测试消息"); hilog.info(0x0000, 'testTag', '消息发送成功'); } catch (error) { hilog.error(0x0000, 'testTag', `消息发送失败: ${JSON.stringify(error)}`); }}
复制代码
6、 结束协同(关键!避免资源泄露)
业务完成后一定要断开连接或销毁会话:
function endCollaboration(sessionId: number) { if (sessionId === -1) { hilog.info(0x0000, 'testTag', '无效的会话ID'); return; }
// 短期还需协同:只断开连接,保留sessionId abilityConnectionManager.disconnect(sessionId); hilog.info(0x0000, 'testTag', '已断开连接');
// 长期不用:销毁会话(自动断开连接) abilityConnectionManager.destroyAbilityConnectionSession(sessionId); hilog.info(0x0000, 'testTag', '已销毁会话');}
复制代码
五、注意事项:
仅支持 API 18 及以上版本,且设备必须登录相同华为账号
只有相同 bundleName 的 UIAbility 才能协同(比如都是“com.example.remotephotodemo”)
协同结束后一定要及时关闭,锁屏或退后台 5 秒未申请长时任务,协同会被系统强制结束
传输隐私数据时,记得加弹框提醒用户(系统不审查传输内容)
评论