大家好,我是陈杨,8 年前端老兵转型鸿蒙开发,也是一名鸿蒙极客。从前端到鸿蒙,我靠的是 “三天上手 ArkTS” 的技术嗅觉,以及 “居安思危” 的转型魄力。这三年,我不玩虚的,封装了开源组件库「莓创图表」,拿过创新赛大奖,更带着团队上架了 11 款自研 APP,涵盖工具、效率、创意等多个领域。想体验我的作品?欢迎搜索体验:指令魔方、JLPT、REFLEX PRO、国潮纸刻、Wss 直连、ZenithDocs Pro、圣诞相册、CSS 特效

我们前面讲了最传统的分享知识,用户的操作往往需要经历“打开应用→选择文件→搜索设备→确认传输”的繁琐流程,甚至要打断当前操作(如暂停视频、退出游戏)。而HarmonyOS的Share Kit推出的“碰一碰分享”能力,通过“设备轻触”这一直观交互,将跨端分享简化为“一碰即传”,支持图片、文件、Wi-Fi、组队邀请等多场景,彻底重构了跨设备交互体验。我们将结合指令魔方与官方开发文档,从手机间分享手机与PC/2in1协同两大场景,详解开发流程、核心代码与避坑要点。

一、碰一碰分享核心基础:环境与核心概念

在开始开发前,需先明确功能依赖的环境要求与核心逻辑,避免因版本不兼容导致功能失效。

1.1 环境要求(必看!)

不同设备间的碰一碰分享,对系统版本和开发工具的要求不同,具体如下:

分享场景 设备系统要求 集成开发环境
手机与手机 双端均为HarmonyOS NEXT Release及以上(推荐5.0+) DevEco Studio NEXT Beta1+
手机与PC/2in1 手机:同上方;PC/2in1:HarmonyOS 6.0.0 Beta1+ DevEco Studio 6.0.0 Beta1+

关键检查:通过canIUse判断设备是否支持碰一碰能力,避免在不支持的设备上触发无效逻辑:

// 检查设备是否支持碰一碰分享
if (canIUse('SystemCapability.Collaboration.HarmonyShare')) {
  console.log("设备支持碰一碰分享,可初始化相关逻辑");
  // 后续注册碰一碰事件...
} else {
  console.log("设备不支持碰一碰分享,需隐藏相关引导");
}

1.2 核心业务流程

无论哪种场景,碰一碰分享的核心流程都遵循“注册→触发→传输→解除”四步,以手机与手机为例:

  1. 注册事件:宿主应用进入可分享界面(如选中图片、打开Wi-Fi详情)时,注册碰一碰分享监听;
  2. 触发交互:双端设备亮屏、解锁且开启华为分享(关闭则弹窗提示开启),顶端轻碰;
  3. 数据传输:源端构造分享数据(如图片URI、链接)并发送,目标端接收并拉起对应应用;
  4. 解除监听:离开可分享界面(如关闭图片、退出页面)时,解除碰一碰监听,避免内存泄漏。

二、手机与手机碰一碰开发:从预览到异常处理

手机间碰一碰是最基础的场景,需重点关注预览体验数据传输方式安全策略异常兜底,以下分模块详解。

2.1 第一步:引导用户触发——注册碰一碰事件

用户可能不清楚“何时可碰”,需在可分享界面添加引导(文本+动图),提升交互感知。

2.1.1 引导资源集成

华为提供统一的碰一碰引导动图,需:

  1. 下载资源包:碰一碰引导资源(完整下载knock_share_guide目录);
  2. 放置路径:将资源放入应用entry/src/main/resources/rawfile目录;
  3. 界面引导:在可分享界面(如图片预览页)显示文本提示+动图,示例文案:
    • “可碰一碰分享至HarmonyOS 5及以上版本手机”
    • “对方需在控制中心开启‘华为分享’”
2.1.2 注册监听事件

在页面aboutToAppear生命周期中注册碰一碰事件,aboutToDisappear中解除,确保资源释放:

import { uniformTypeDescriptor as utd } from '@kit.ArkData';
import { systemShare, harmonyShare } from '@kit.ShareKit';
import { fileUri } from '@kit.CoreFileKit';
import { UIContext, Context } from '@kit.ArkUI';

@Component
export default struct ImageSharePage {
  // 页面进入时注册碰一碰监听
  aboutToAppear(): void {
    this.registerKnockShare();
  }

  // 页面销毁时解除监听
  aboutToDisappear(): void {
    this.unregisterKnockShare();
  }

  // 注册碰一碰分享事件
  private registerKnockShare() {
    // 配置监听参数(windowId需替换为实际页面的窗口ID,不可用示例值)
    const capabilityRegistry: harmonyShare.SendCapabilityRegistry = {
      windowId: 1001, // 实际开发中通过UIContext获取真实windowId
    };

    // 监听碰一碰事件,触发时构造并发送数据
    harmonyShare.on('knockShare', capabilityRegistry, (sharableTarget: harmonyShare.SharableTarget) => {
      this.buildShareData(sharableTarget);
    });
  }

  // 解除碰一碰分享监听
  private unregisterKnockShare() {
    const capabilityRegistry: harmonyShare.SendCapabilityRegistry = {
      windowId: 1001, // 与注册时的windowId一致
    };
    harmonyShare.off('knockShare', capabilityRegistry);
  }

  // 构造分享数据(核心逻辑)
  private buildShareData(sharableTarget: harmonyShare.SharableTarget) {
    // 1. 获取预览图URI(示例:本地图片路径,实际需替换为业务图片路径)
    const uiContext: UIContext = this.getUIContext();
    const context: Context = uiContext.getHostContext() as Context;
    const imagePath = context.filesDir + '/shared_image.jpg';
    const thumbnailUri = fileUri.getUriFromPath(imagePath);

    // 2. 构造分享数据(utd类型根据内容调整,图片可设为IMAGE,链接设为HYPERLINK)
    const shareData: systemShare.SharedData = new systemShare.SharedData({
      utd: utd.UniformDataType.IMAGE, // 图片类型
      content: thumbnailUri, // 图片URI
      title: '旅行照片', // 卡片标题(可选,根据模板决定)
      description: '2025年夏日旅行', // 卡片描述(可选)
      thumbnailUri: thumbnailUri, // 预览图URI
    });

    // 3. 发起分享(必须在3秒内调用,否则超时失败)
    sharableTarget.share(shareData);
  }

  build() {
    // 页面布局(省略,需包含图片预览+碰一碰引导动图)
  }
}

2.2 第二步:优化视觉体验——设置分享预览

分享卡片的预览效果直接影响用户接受度,Share Kit提供3种卡片模板,需根据分享内容类型选择:

模板类型 适用场景 配置要求 布局约束
纯图片布局 文件、图片(无需文字说明) 仅传递thumbnailUri 预览图宽高比≥1:4,超出部分裁剪
沉浸式大卡布局 链接(需展示文字) 同时传递title+description+thumbnailUri,且预览图宽高比<1:1 标题最多2行(超出省略),描述1行,显应用图标
白卡上下布局 链接(需展示文字) 同时传递title+description+thumbnailUri,且预览图宽高比>1:1 同上,预览图仅占卡片上方,不铺满
2.2.1 预览图最佳实践

预览图分辨率影响加载速度与清晰度,推荐按以下标准设置(文档2):

预览图来源 推荐比例 推荐分辨率(px)
应用海报 3:4 最小600_800,最大3000_4000
用户上传图片 无限制 最大3000*4000(避免过大)
2.2.2 预览图延迟更新(解决云端图片问题)

若预览图存储在云端(如OSS),碰一碰触发时可能未下载完成,导致卡片空白。可先发送核心数据,待图片下载后更新预览:

private buildShareData(sharableTarget: harmonyShare.SharableTarget) {
  // 1. 先发送无预览图的核心数据(用系统默认图占位)
  const shareData: systemShare.SharedData = new systemShare.SharedData({
    utd: utd.UniformDataType.HYPERLINK,
    content: 'https://example.com/travel', // 分享链接
    title: '旅行攻略',
    description: '超全景点指南',
  });
  sharableTarget.share(shareData);

  // 2. 模拟云端图片下载(实际项目中替换为真实下载回调)
  setTimeout(async () => {
    const uiContext: UIContext = this.getUIContext();
    const context: Context = uiContext.getHostContext() as Context;
    // 假设下载完成,获取本地路径
    const downloadedImagePath = context.filesDir + '/downloaded_poster.jpg';
    const newThumbnailUri = fileUri.getUriFromPath(downloadedImagePath);

    // 3. 更新预览图
    sharableTarget.updateShareData({
      thumbnailUri: newThumbnailUri,
    });
  }, 3000); // 实际不建议用固定延迟,应监听下载完成事件
}

2.3 第三步:选择数据传输方式——App Linking vs Deep Linking

若分享内容为“链接”(如商品页、文章页),需通过以下两种方式实现应用跳转,核心区别在于“未安装应用时的处理逻辑”。

2.3.1 App Linking(推荐)

优势:无论目标端是否安装应用,均能直达内容——

  • 已安装:直接拉起应用并打开链接;
  • 未安装:跳转应用市场(需集成App Linking Kit);
  • 支持延迟链接:应用下载后仍能获取之前的分享链接。

示例代码

private buildShareData(sharableTarget: harmonyShare.SharableTarget) {
  const shareData: systemShare.SharedData = new systemShare.SharedData({
    utd: utd.UniformDataType.HYPERLINK, // 必须设为HYPERLINK
    content: 'https://sharekitdemo.drcn.agconnect.link/ZB3p', // App Linking链接
    title: '华为商城新品',
    description: 'nova 14系列限时优惠',
    thumbnailUri: 'file:///data/storage/.../poster.jpg', // 预览图URI
  });
  sharableTarget.share(shareData);
}
2.3.2 Deep Linking(简单但有局限)

局限:仅在目标端已安装应用时生效,未安装则弹窗提示“暂无可用打开方式”。

示例代码

// 与App Linking代码类似,仅content替换为Deep Linking链接
content: 'myapp://product/detail?id=123', // 自定义Deep Linking协议

2.4 第四步:保障安全与体验——安全策略与异常处理

2.4.1 安全策略:明确设备/账号身份

为避免用户接收陌生设备的分享,Share Kit会显示发送端/接收端的身份信息(需HarmonyOS NEXT 5.0.0.123 SP16及以上版本):

角色 对端已登录华为账号 对端未登录华为账号
发送端 显示接收端账号昵称+头像 显示接收端设备名(如“Mate 70”)
接收端 显示发送端账号昵称+头像 显示发送端设备名

注意:SP16之前的版本不显示任何身份信息,开发时需考虑版本兼容。

2.4.2 异常场景处理

当无法发起分享时,需主动提示用户,避免用户等待。

场景1:当前界面无可分享内容

harmonyShare.on('knockShare', (sharableTarget: harmonyShare.SharableTarget) => {
  // 调用clarifyNonShare终止分享,并提示用户
  sharableTarget.clarifyNonShare({
    message: '当前界面不支持碰一碰,请在图片预览或文件详情页尝试'
  });
});

效果:系统弹窗提示用户,引导至可分享界面。

场景2:分享内容下载失败(如云端图片下载超时)

harmonyShare.on('knockShare', (sharableTarget: harmonyShare.SharableTarget) => {
  // 模拟下载失败
  const downloadFailed = true;
  if (downloadFailed) {
    // 调用reject终止分享,指定错误码
    sharableTarget.reject(harmonyShare.SharableErrorCode.DOWNLOAD_ERROR);
  }
});

效果:系统提示“分享内容下载失败,请稍后重试”。

三、手机与PC/2in1碰一碰开发:跨端协同新体验

手机与PC/2in1的碰一碰进一步拓展场景(如手机传图到PC编辑、PC传文档到手机),需关注双向分享限制沙箱接收特性。

3.1 核心约束与环境

  1. 账号要求:手机与PC/2in1必须登录相同华为账号
  2. 设备形态:仅支持手机直板态/折叠手机直板态,不支持折叠态;
  3. 角度约束(触发成功率关键):
    • 手机与PC屏幕俯视夹角≤5°(尽量平行);
    • 侧视夹角>35°(避免过度倾斜);
    • 正视夹角≤25°(正对屏幕);
  4. 保护壳:仅支持官方轻薄保护壳,过厚壳会影响感应。

3.2 双向分享限制(避坑要点)

从HarmonyOS 6.0.0 Beta5开始,手机与PC/2in1不支持“双向同时分享”,遵循以下优先级:

  1. 若手机前台有可分享内容(如打开图片),则手机为发送端,PC为接收端
  2. 若手机前台无内容,但PC前台有可分享内容(如打开文档),则PC为发送端,手机为接收端
  3. 若两者均无内容,触发“无内容分享”提示。

注意:Beta5之前的版本支持双向分享(双方同时发送接收),开发时需根据版本适配逻辑。

3.3 分享内容直达应用:沙箱接收

PC/2in1支持“沙箱接收”,即手机碰PC后,文件直接传入PC应用的沙箱目录,无需用户选择保存路径,实现“直达编辑”(如手机传图到PC版PS,直接打开编辑)。

3.3.1 沙箱接收开发步骤
import { uniformTypeDescriptor as utd } from '@kit.ArkData';
import { systemShare, harmonyShare } from '@kit.ShareKit';
import { common } from '@kit.AbilityKit';

@Component
export default struct PCImageEditorPage {
  aboutToAppear(): void {
    this.registerSandboxReceive();
  }

  aboutToDisappear(): void {
    this.unregisterSandboxReceive();
  }

  // 注册沙箱接收事件(仅支持文件类型)
  private registerSandboxReceive() {
    const capabilityRegistry: harmonyShare.RecvCapabilityRegistry = {
      windowId: 2001, // PC应用窗口ID
      capabilities: [
        {
          utd: utd.UniformDataType.IMAGE, // 仅接收图片类型
          maxSupportedCount: 5 // 最多接收5张图片
        }
      ]
    };

    // 监听数据接收事件
    harmonyShare.on('dataReceive', capabilityRegistry, (receivableTarget: harmonyShare.ReceivableTarget) => {
      const uiContext = this.getUIContext();
      const context = uiContext.getHostContext() as common.UIAbilityContext;
      // 指定文件保存路径(应用沙箱目录)
      const savePath = context.filesDir + '/received_images/';

      // 接收文件
      receivableTarget.receive(savePath, {
        // 每接收一个文件触发
        onDataReceived: (sharedData: systemShare.SharedData) => {
          const records = sharedData.getRecords();
          records.forEach(record => {
            console.log(`接收文件:${record.uri}`);
            // 触发应用编辑逻辑(如打开图片编辑界面)
            this.openImageEditor(record.uri);
          });
        },
        // 接收完成/失败触发
        onResult: (resultCode: harmonyShare.ShareResultCode) => {
          if (resultCode === harmonyShare.ShareResultCode.SHARE_SUCCESS) {
            console.log("所有文件接收完成");
          } else {
            console.log(`接收失败,错误码:${resultCode}`);
          }
        }
      });
    });
  }

  // 解除沙箱接收监听
  private unregisterSandboxReceive() {
    const capabilityRegistry: harmonyShare.RecvCapabilityRegistry = {
      windowId: 2001,
      capabilities: [{ utd: utd.UniformDataType.IMAGE, maxSupportedCount: 5 }]
    };
    harmonyShare.off('dataReceive', capabilityRegistry);
  }

  // 打开图片编辑界面
  private openImageEditor(imageUri: string) {
    // 业务逻辑:跳转编辑页并加载图片
  }

  build() {
    // PC应用界面(省略)
  }
}
3.3.2 拒绝沙箱接收

若应用当前无法接收(如编辑中),可调用reject拒绝:

harmonyShare.on('dataReceive', (receivableTarget: harmonyShare.ReceivableTarget) => {
  // 业务判断:当前正在编辑,拒绝接收
  if (this.isEditing) {
    receivableTarget.reject(harmonyShare.ReceivableErrorCode.NO_RECEIVABLE_ERROR);
  }
});

四、特殊场景:组队邀请开发

在游戏、运动类应用中,可通过碰一碰发起“组队邀请”,需避免“双方同时邀请”导致的冲突,此时需使用单向分享能力

4.1 核心代码:注册单向分享

aboutToAppear(): void {
  const capabilityRegistry: harmonyShare.SendCapabilityRegistry = {
    windowId: 3001,
    sendOnly: true // 申明“仅发送邀请,不接收邀请”
  };

  harmonyShare.on('knockShare', capabilityRegistry, (sharableTarget) => {
    // 构造组队链接(含房间ID、邀请者信息)
    const teamLink = `mygame://team/join?id=12345&inviter=user1`;
    const shareData = new systemShare.SharedData({
      utd: utd.UniformDataType.HYPERLINK,
      content: teamLink,
      title: '邀请加入游戏战队',
      description: '点击立即组队开黑',
      thumbnailUri: 'file:///.../team_poster.jpg'
    });
    sharableTarget.share(shareData);
  });
}

4.2 冲突处理

  • 双方均设置sendOnly: true:系统终止分享,提示“请任意一方退出当前应用后再试”;
  • 若仅一方设置:分享成功,目标端拉起应用并通过onNewWant获取组队链接。

获取组队链接

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class GameAbility extends UIAbility {
  // 应用首次启动时获取链接
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want.uri) {
      console.log(`收到组队邀请:${want.uri}`);
      this.joinTeam(want.uri); // 解析链接并加入队伍
    }
  }

  // 应用已启动时获取链接
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want.uri) {
      this.joinTeam(want.uri);
    }
  }

  private joinTeam(teamUri: string) {
    // 解析URI中的房间ID、邀请者信息,发起加入请求
  }
}

五、开发最佳实践总结

  1. 环境检查优先:通过canIUse判断设备能力,避免无效逻辑;
  2. 资源及时释放:在aboutToDisappear中解除碰一碰监听,防止内存泄漏;
  3. 预览图适配:按推荐分辨率设置预览图,云端图片用延迟更新兜底;
  4. 异常全面覆盖:处理“无内容”“下载失败”等场景,避免用户无反馈;
  5. 跨端版本适配:手机与PC的系统版本要求不同,双向分享限制需区分版本;
  6. 用户引导清晰:在可分享界面添加文本+动图引导,降低使用门槛。

希望你们可以通过以上开发指南,可快速实现HarmonyOS碰一碰分享功能,为用户提供“一碰即传”的跨端体验。随着鸿蒙生态的扩展,未来碰一碰还将支持更多设备(如平板、智慧屏),进一步丰富全场景交互。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐