news 2026/2/26 15:59:18

如何让你的APP吃上鸿蒙PC端红利(二)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何让你的APP吃上鸿蒙PC端红利(二)

鸿蒙电脑 Harmony OS 6了,再不入局就晚了

尊贵的鸿蒙电脑用户,付费能力可以说是全球最强,遥遥...

Harmony OS 5的时候不入局是对的,装机少,系统bug多,适配的app也少。

自从升级Harmony OS 6,各方各面都完善起来,市场风向也从

下着玩玩-到了-用户刚需。

很多手机端优秀app的开发者发现,一旦兼容2IN1,(2IN1是华为对PC电脑设备的统称)

运营统计中曾经占比渺小的2IN1设备,

开始节节攀升。

摆在我们开发者面前有几个问题需要解决:

1.不重开发,如何兼容2IN1?(请看系列一)

2.有2IN1的用户通常有华为手机,甚至多数有全家桶,应该为APP加入哪些功能增强竞争力

3.买不起昂贵的PC,该怎么开发、测试

关注我,将在接下来的系列中,用实战中的经验,解决以上三个问题。

第二问:有2IN1的用户通常是全家桶用户,应该如何增强竞争力?

回答:跨设备协同体验

鸿蒙(HarmonyOS)的“一多开发”(一次开发,多端部署)不仅关注单设备内的多形态适配(如手机、平板、2in1),更核心的是支撑跨设备协同体验,包括:

  • 分布式能力
  • 应用接续(Continuity)
  • 碰一碰(NFC/蓝牙快速发现与连接)
  • 鼠标/键盘联动(外设协同)

一、分布式能力:鸿蒙一多的底层基石

划重点,下表一定要逐字看完,欲练神功,必先死记。

技术作用开发接口
分布式软总线(SoftBus)设备自动发现、安全连接、低延时通信@ohos.distributedHardware
deviceManager
分布式数据管理(DistributedDataManager)多设备间数据同步(如剪贴板、配置)@ohos.data.relationalStore
createDistributedTable()
分布式任务调度(DTSEngine)跨设备拉起服务、迁移任务@ohos.ability.featureAbility
startAbility()+want中指定设备

使用场景:

分布式让2IN1设备(2IN1是华为对各种PC设备的统称)能够和手机、平板、手表手环、智慧屏、车机、耳机等一切具有harmony OS 设备进行数据同步。

无论如何,都要让APP具备一些分布式能力,即便APP在之前没有考虑分布式,没关系,升级到分布式没有想象中那么困难。

分布式的三要素:权限、设备、分布式数据

要实现分布式就三步走:

1.获取权限,通过结构主动向用户索要“发现设备”的权限。

2.获取设备表。

3.对每一台设备进行分布式数据的拉取和推送。

权限代码片段:

/** * 1.判断是否已授权,未授权则拉起授权 * @param context * @returns */ async checkPermissions(context:common.UIAbilityContext): Promise<void> { try { let grantStatus1: boolean = await this.checkPermissionGrant('ohos.permission.DISTRIBUTED_DATASYNC') === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;// 获取同步权限状态。 if (!grantStatus1) { // 申请权限。 this.reqPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], context); } else { // 已经授权,可以继续访问目标操作。 emitter.emit('distributedSuccessFulEvt'); } } catch (e) { // 获取权限失败 emitter.emit('distributedErrorEvt'); throw e as SubError; } } /** * 1.1权限判断 * @param permission * @returns */ private async checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED; // 获取应用程序的accessTokenID。 let tokenId: number = 0; try { let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo; tokenId = appInfo.accessTokenId; } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to get bundle info for self, code: ${err.code}, message: ${err.message}`); } // 校验应用是否被授予权限。 try { grantStatus = await atManager.checkAccessToken(tokenId, permission); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to check access token, code: ${err.code}, message: ${err.message}`); } return grantStatus; } /** * 1.2拉起授权窗口 * @param permissions * @param context */ private reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗。 atManager.requestPermissionsFromUser(context, permissions).then((data) => { let grantStatus: Array<number> = data.authResults; let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] === 0) { // 用户授权,等待其他权限。 } else { // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限。 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); atManager.requestPermissionOnSetting(context, ['ohos.permission.DISTRIBUTED_DATASYNC']).then((data: Array<abilityAccessCtrl.GrantStatus>) => { if(data[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){ console.info(`2次同步授权完成, result: ${data}`); AppStorage.setOrCreate(IS_DATA_SYNC,true); emitter.emit('distributedSuccessFulEvt'); } else { emitter.emit('distributedErrorEvt'); emitter.emit('distributedReject'); //拒绝了权限申请。 console.info(`2次同步授权被拒绝, result: ${data}`); } }).catch((err: BusinessError) => { emitter.emit('distributedErrorEvt'); throw new SubError({code:err.code,message:err.message}); console.error(`requestPermissionOnSetting fail, code: ${err.code}, message: ${err.message}`); }); return; } } // 授权成功。 console.info(`同步授权完成, result: ${data}`); emitter.emit('distributedSuccessFulEvt'); }).catch((err: BusinessError) => { emitter.emit('distributedErrorEvt'); console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); }) }

获取了权限后,先把数据库设置为分布式。

const tables: string[] = [ 'table1', //表1 'table2', //表2 ]; try { //设置表们为分布式。 await store.setDistributedTables(tables); } catch (error) { console.error(`Set distributed tables failed: ${error.code}, ${error.message}`); }

查找设备的代码片段:

searchDevices(): distributedDeviceManager.DeviceBasicInfo[] { // 查询组网内的设备列表 const deviceManager = distributedDeviceManager.createDeviceManager('com.waitpanda.xiexiaoshuo'); try { const deviceList = deviceManager.getAvailableDeviceListSync(); if (!deviceList) { throw new SubError({ code: 998404, message: '没有设备' }); } return deviceList; //设备列表。 } catch (error) { const e = error as SubError; console.error('d2d sync with all devices',e.code,e.message) throw error as SubError; //这个SubError是我自定义的一个错误类,可以直接使用Error } }

推送本地数据到其他设备。这个操作一定是你使用insert、update、delete等数据库写操作后进行。

// 构造用于同步分布式表的谓词对象 const predicates = new relationalStore.RdbPredicates(DB_CONSTANTS.TABLE1); predicates.inDevices(deviceList); try { const result = await store.sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates); // 获取同步结果 for (let i = 0; i < result.length; i++) { const deviceId = result[i][0]; const syncResult = result[i][1]; if (syncResult === 0) { console.info('rdbDataSync', `device:${deviceId} table:${table} sync success`); } else { // 有些设备没有你的APP,它会报错。 console.error('rdbDataSync', `device:${deviceId} table:${table} sync failed, status:${syncResult}`); } } } catch (e){ console.error('pushToAllDevice同步失败'); }

关于分布式数据库(键值对、关系型、用户首选项)的不同:键值对分布式可以各设备之间相互读写,关系型则只能读其他设备,不能写。这个特性一定搞明白。

二、应用接续(Continuity):任务无缝流转

场景示例

  • 手机上写邮件 → 点击“接续到2IN1” → 2IN1继续编辑
  • 车机导航 → 到家后自动接续到手表步行导航

注意的是,这里要用到一个叫“分布式数据对象”的东西,它和分布式数据库中读出来的内容不是同一个东西,它是个独立的东西。

这个叫分布式数据对象的,主要作用是,将现在内存中进行了一半的内容记下来,另一台设备从这个分布式数据对象中把内容读出,继续进行操作。

除了分布式数据对象,还有个重要的东西叫SessionId,本端将生成的sessionId通过want传递到远端,供远端激活同步使用。

三、碰一碰(Tap to Share / Tap to Connect)

技术原理

  • 基于NFC + 蓝牙/WiFi P2P快速建立连接;
  • 触发系统级原子化服务(Atomic Service)FA 拉起

优势:无需用户打开 App,符合鸿蒙“服务找人”理念。

实战理解

将分享、截图、应用接续打包成碰一碰,主打让用户在人前装*,人后实用。这个功能很能提升用户使用率,有条件一定上一个。

四、鼠标联动(外设协同)

适用场景

  • 电脑连接蓝牙鼠标 → 应用需显示 hover 效果、右键菜单;
  • PC 模式下支持快捷键(Ctrl+C/V);
  • 多屏联动(鼠标能在手机、平板、电脑三者的屏幕上无缝移动)。

实战理解

只需要把图片、文本、列表等设置成能拖入拖出的方式复制、拷贝,就能让你的APP具备鼠标联动功能。这是全家桶用户高频使用的功能,性价比极高的功能。

五、整体架构建议:如何在一多工程中集成这些能力?

1Project/ 2├── common/ # 公共逻辑(含分布式工具类) 3│ └── DistributedHelper.ts 4├── features/ 5│ ├── continuity/ # 应用接续模块 6│ ├── nfc-share/ # 碰一碰分享 7│ └── input-enhance/ # 鼠标/键盘增强 8├── products/ 9│ ├── phone/ # 手机端:简化接续入口 10│ ├── tablet/ # 平板端:常驻侧边栏 + 分栏 + 鼠标支持 11│ └── pc/ # PC端:快捷键 + 右键菜单 12└── module.json5 # 声明分布式权限、NFC、外设能力

六、总结:鸿蒙一多 × 分布式 = 全场景体验

表格

特色能力技术方案一多开发要点
分布式软总线 + 分布式数据/任务一套逻辑,多设备协同执行
应用接续ContinuationRegisterManager状态序列化 + 目标设备UI适配
碰一碰NFC + 原子化服务/卡片无感触发,服务直达
鼠标联动PointerEvent + 快捷键大屏设备增强交互,小屏忽略

🔥核心思想
“一多”不仅是适配不同屏幕,更是构建“以人为中心的超级终端”
你的应用不再属于某一台设备,而是运行在用户所有的鸿蒙设备组成的“分布式网络”中。

这正是鸿蒙生态对开发者的最大红利。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/24 22:12:14

Qwen3-Embedding-4B多模态扩展:图文检索系统构建教程

Qwen3-Embedding-4B多模态扩展&#xff1a;图文检索系统构建教程 你是否遇到过这样的问题&#xff1a; 一堆商品图、设计稿、产品截图堆在服务器里&#xff0c;想快速找出“带蓝色背景的电商主图”或“含英文LOGO的包装设计”&#xff0c;却只能靠文件名硬猜&#xff1f; 或者…

作者头像 李华
网站建设 2026/2/19 0:38:14

Sambert语音文件格式要求:WAV/MP3输入输出处理部署规范

Sambert语音文件格式要求&#xff1a;WAV/MP3输入输出处理部署规范 1. 开箱即用的多情感中文语音合成体验 你有没有试过把一段文字变成声音&#xff0c;但结果听起来像机器人念稿&#xff1f;Sambert 多情感中文语音合成镜像就是为解决这个问题而生的——它不是“能出声”就行…

作者头像 李华
网站建设 2026/2/24 14:56:12

如何测试准确性?FSMN-VAD评估数据集使用指南

如何测试准确性&#xff1f;FSMN-VAD评估数据集使用指南 1. 为什么“能检测”不等于“测得准”&#xff1f; 你已经成功部署了 FSMN-VAD 离线控制台&#xff0c;上传一段录音&#xff0c;几秒后看到漂亮的表格&#xff1a;开始时间、结束时间、时长一应俱全。界面流畅&#x…

作者头像 李华
网站建设 2026/2/25 11:19:29

Llama3-8B显存优化:梯度检查点技术部署实战

Llama3-8B显存优化&#xff1a;梯度检查点技术部署实战 1. 为什么80亿参数模型也需要显存优化&#xff1f; 你可能已经看到过那句广为流传的选型建议&#xff1a;“预算一张3060&#xff0c;想做英文对话或轻量代码助手&#xff0c;直接拉 Meta-Llama-3-8B-Instruct 的 GPTQ-…

作者头像 李华
网站建设 2026/2/25 8:13:50

开源大模型企业落地指南:Qwen3-4B-Instruct多场景部署教程

开源大模型企业落地指南&#xff1a;Qwen3-4B-Instruct多场景部署教程 1. 为什么企业该关注Qwen3-4B-Instruct 很多技术负责人第一次听说Qwen3-4B-Instruct时&#xff0c;心里都会打个问号&#xff1a;又一个开源模型&#xff1f;它和我们正在用的模型比&#xff0c;到底强在…

作者头像 李华
网站建设 2026/2/25 21:15:37

MinerU低成本部署实践:中小企业PDF自动化方案成本分析

MinerU低成本部署实践&#xff1a;中小企业PDF自动化方案成本分析 1. 为什么中小企业需要PDF自动化提取工具 你有没有遇到过这样的情况&#xff1a;公司每天收到几十份供应商报价单、客户合同、技术白皮书&#xff0c;全是PDF格式。人工一页页复制粘贴到Word或Excel里&#x…

作者头像 李华