HarmonyOS 跨设备互通实战:平板点一下,手机镜头帮你拍照

本文主线落在 HarmonyOS 6.1.0(23) 的调用策略扩展,并顺带讲清算 6.0.0(20) 新增的视频选择器。文中的 API 名称、枚举取值与版本号来自华为官方文档;结构、代码示例、调用方向矩阵与自检清单为本人整理编写,未在真机逐行验证,涉及真机表现的部分以真机实测为准。


引子:一句话需求,三个隐藏前提

产品需求单上常出现这么一句:

在平板上填表,需要一张证件照,平板没好镜头,直接调手机的相机拍一张传回来。

听起来像"弹个菜单 + 选设备 + 收图片"。V哥按这个直觉开干,第一天就被泼了冷水------菜单弹出来,设备列表是空的。不是代码写错,是三个前提少了一个。

这里有个画面感:你按了按钮,系统应该弹出一个设备选择菜单,里面躺着同一账号下、开了 WLAN 和蓝牙的远端正列表。结果V哥的菜单空空如也,只有一个孤零零的"无可用设备"。那一刻最容易犯的错,是回头怀疑 createCollaborationServiceMenuItems 写错了、怀疑 @Builder 没挂对。其实接口一个都没错,是网络那头的灯没亮。

这篇就把这三个前提、谁能调谁、怎么收数据一次讲清,顺带把最容易混淆的版本线划明白。


一、先校准版本:别把 5.x 的写法当成 6.x 的能力

这是本选题最容易踩的坑。官方《跨设备互通开发指导》的"约束与限制"里,调用策略分了两截:

调用策略:PC/2in1 设备可以调用 Tablet 和 Phone,Tablet 可以调用 Phone;从 API 6.1.0(23) 开始,TV、Phone、Tablet 或 PC/2in1 设备可调用具备拍照、扫描及图库能力的远端设备。(Service Collaboration Kit 简介

注意后半句------6.1.0(23) 之前,接口只能在 PC/2in1、Tablet 上正常调用,在 Phone、TV 上直接"无法展示设备列表、无法使用能力"。6.1.0(23) 之后,Phone 和 TV 也能当"主控"了。

V哥自己的判断:这套能力的"主控方"是被系统写死的,不是你能看见谁就调谁。 6.1.0(23) 是它第一次把主控方从"平板/2in1"扩到"电视/手机也能发起"。所以写 6.0+ 的文章,主线就该落在这个扩展上,而不是只讲"平板调手机"这条老链路。

另一个 6.0 切口在过滤器上:createCollaborationServiceMenuItemsbusinessFilter6.0.0(20) 起新增了视频选择器(VIDEO_PICKER)与图文混合选择器(IMAGE_VIDEO_PICKER),之前只有拍照、扫描、图库三种。

顺带说两句排错心得:设备列表为空时,先按"三道门槛"排查,再对照调用方向矩阵------同类型设备互调是全版本都不行的,不是你权限没配对。这两张表对不上,代码写得再好也是白跑。另外,StateDialog 回调拿到的 ArrayBuffer 要先判空再解码,远端设备中途取消时它就是空的,这种情况该给用户一个"对方已取消"的提示,别当异常直接抛出去。


二、三道门槛:同账号、WLAN、蓝牙,缺一列表就空

不管你用 ArkTS 还是 NDK,设备列表能不能出来,先看这三件:

门槛 要求 V哥的理解
华为账号 双端登录同一个华为账号 不是"都登录了"就行,是同一个。换账号列表直接空
网络 双端打开 WLAN 建议接同一局域网,唤醒相机更快;蓝牙也得开
蓝牙 双端打开蓝牙开关 和 WLAN 是并列条件,不是二选一

双端设备需要登录同一华为账号;双端设备需要打开 WLAN 和蓝牙开关。(Service Collaboration Kit 简介

第一个自创口诀:远端没登同一个账号,设备列表直接是空的------三件套缺一,列表就是空的。 调试时别先怀疑代码,先看这三盏灯。模拟器不支持本能力,验证必须在真机双端上做。


三、调用方向矩阵:谁能动谁,系统说了算

把官方策略拆成一张"本端 → 远端"的表,配上V哥的使用建议:

本端(主控) 可调用远端 版本边界 V哥的理解
PC/2in1 Tablet、Phone 5.0 起即支持 最稳的主控方,能力全开
Tablet Phone 5.0 起即支持 "平板调手机镜头"的经典链路
TV 有拍照/扫描/图库的 Phone、Tablet;有图库的 PC/2in1 6.1.0(23) 起 电视也能当主控,但被控方要带相机
Phone 同上(带相机/图库的远端) 6.1.0(23) 起 手机反向调别家镜头,这是 6.x 新开的口
同类型设备(如手机调手机) 不可调用 全版本 同形态互斥,别指望手机调手机

第二个自创判断:同类型设备不可互相调用,是一条硬规则,不是网络没连上。 你想"用另一台手机补拍",系统不会给你列出来------这跟账号、网络都没关系。


ArkTS 侧两个组件必须配合使用:createCollaborationServiceMenuItems(拿设备列表)和 CollaborationServiceStateDialog(收远端状态)。前者是 @Builder 自定义构建函数,必须在 Menu 内调用

typescript 复制代码
// RemoteShoot.ets ------ 自写的跨设备拍照封装(命名/注释均为本人整理)
import {
  createCollaborationServiceMenuItems,
  CollaborationServiceStateDialog,
  CollaborationServiceFilter
} from '@kit.ServiceCollaborationKit';
import { image } from '@kit.ImageKit';

// 复用官方约定的回传结果码:0 成功,其余为异常分支
const COLLAB_OK = 0;
const COLLAB_PEER_CANCEL = 1001202001;  // 远端取消
const COLLAB_FRAMEWORK_ERR = 1001202002; // 框架内部错误
const COLLAB_LOCAL_CANCEL = 1001202003;  // 本端取消

@Entry
@Component
struct RemoteShootPage {
  @State snapshot: image.PixelMap | undefined = undefined;

  // 设备选择菜单:挂在 Menu 里,弹出时由系统拉起设备列表
  @Builder
  deviceMenu() {
    Menu() {
      // 只匹配"跨端拍照"能力;要图库可换成 IMAGE_PICKER
      createCollaborationServiceMenuItems([CollaborationServiceFilter.TAKE_PHOTO]);
    }
  }

  // 把远端回传的 ArrayBuffer 解码成 PixelMap
  private async decodeToPixelMap(buf: ArrayBuffer): Promise<image.PixelMap | undefined> {
    if (buf.byteLength === 0) {
      return undefined;
    }
    try {
      const source = image.createImageSource(buf);
      return await source.createPixelMap();
    } catch (e) {
      console.error('解码远端图片失败');
      return undefined;
    }
  }

  build() {
    Column({ space: 16 }) {
      // 状态弹窗:全局组件,挂页面即可,不占布局
      CollaborationServiceStateDialog({
        onState: (stateCode: number, bufferType: string, buffer: ArrayBuffer): void => {
          if (stateCode === COLLAB_OK && buffer.byteLength > 0) {
            this.decodeToPixelMap(buffer).then((pm) => { this.snapshot = pm; });
            return;
          }
          console.info('跨设备互通结束,stateCode=' + stateCode);
        }
      })

      Button('用手机镜头拍一张')
        .bindMenu(this.deviceMenu)

      if (this.snapshot) {
        Image(this.snapshot)
          .width('80%')
          .height(300)
          .objectFit(ImageFit.Contain)
      }
    }
    .padding(20)
    .width('100%')
  }
}

两个要点:createCollaborationServiceMenuItems 还有个带 canReceiveNumber(1~50)的重载,用来限制图库可选图片张数;businessFilter 在 6.0.0(20) 起支持 VIDEO_PICKER / IMAGE_VIDEO_PICKER,老版本传这两个值不会报错,但能力不匹配。

为什么这个组件必须是"放进 Menu"而不是你自己在页面画一个列表?因为设备列表的拉起、可信设备校验、对端应用唤起,全是系统统一兜底的------你自己画列表既拿不到可信设备,也绕不开账号/WLAN/蓝牙那三盏灯。换句话说,createCollaborationServiceMenuItems 不是"给你一个 API 让你造界面",而是"把系统已经做好的设备选择器借你挂一下"。理解到这一层,你就不会想着去自定义列表样式了,那是系统不让动的。


五、收数据:onState 的三个参数别接反

CollaborationServiceStateDialog 的核心就是 onState 回调,三个参数顺序是固定的:

  • stateCode:完成状态,0 成功,其余见上方常量;
  • bufferType:回传数据类型,目前官方文档标注仅支持 general.image
  • buffer:回传的原始数据,ArrayBuffer 格式,失败时为空

第三个自创判断:把"解码"当成一道独立关卡,而不是在 onState 里顺手写。 远端回传的是裸 ArrayBuffer,你要么用 image.createImageSource 转成 PixelMap,要么自己落盘。数据坏了、为空、解码异常三种情况要分开处理------别一句 Toast 掩盖掉,否则用户只会说"拍了没反应"。


六、NDK 三件套:Get / Start / Stop

如果你的相机页是 C/C++ 渲染管线,用 NDK 更直接。头文件 service_collaboration/service_collaboration_api.h,链接库 libservice_collaboration_ndk.z.so,能力起点 5.0.0(12)。核心三件套:

  • HMS_ServiceCollaboration_GetCollaborationDeviceInfos:按能力类型拿设备列表;
  • HMS_ServiceCollaboration_StartCollaboration:拉起远端能力;
  • HMS_ServiceCollaboration_StopCollaboration:主动取消。
cpp 复制代码
// collab_entry.cpp ------ 自写的 NDK 调用骨架
#include "service_collaboration/service_collaboration_api.h"

static int32_t OnEventProc(ServiceCollaborationEventCode code, uint32_t extra) { return 0; }
static int32_t OnDataProc(ServiceCollaborationEventCode code,
                          ServiceCollaborationDataType type, uint32_t size, char* data) {
    return 0;
}

void shootFromPhone() {
    // 1. 先要设备列表:传 TAKE_PHOTO 能力,系统回匹配的远端
    ServiceCollaborationFilterType filters[] = { TAKE_PHOTO, SCAN_DOCUMENT, IMAGE_PICKER };
    ServiceCollaboration_CollaborationDeviceInfoSets* info =
        HMS_ServiceCollaboration_GetCollaborationDeviceInfos(3, filters);
    if (info == nullptr || info->size == 0) {
        return; // 列表空:回头查三道门槛
    }
    // 2. 选第一台设备,构造回调
    ServiceCollaboration_SelectInfo task = { TAKE_PHOTO, { 0 } };
    auto* dev = &(info->deviceInfoSets[0]);
    // deviceNetworkId 拷贝进 task,长度上限见 COLLABORATIONDEVICEINFO_DEVICENETWORKID_MAXLENGTH
    ServiceCollaborationCallback cb = { .OnEvent = OnEventProc, .OnDataCallback = OnDataProc };
    // 3. 拉起远端相机
    uint32_t id = HMS_ServiceCollaboration_StartCollaboration(&task, &cb);
    // 需要视频回传时,用 StartCollaborationV2(支持 IMAGE_VIDEO_PICKER 的远端)
    HMS_ServiceCollaboration_StopCollaboration(id); // 不再需要时取消
}

NDK 侧的 ServiceCollaborationFilterType 取值为 TAKE_PHOTO=1SCAN_DOCUMENT=2IMAGE_PICKER=3VIDEO_PICKER=5IMAGE_VIDEO_PICKER=6;回传数据类型 ServiceCollaborationDataTypeIMAGE=1VIDEO=2。要视频回传请用 StartCollaborationV2,它是 6.0.0(20) 视频能力在 NDK 上的对应入口。

ArkTS 还是 NDK,怎么选?如果业务逻辑就在 ArkTS 页面里、要的也只是"弹菜单选设备 + 收一张图",那 ArkTS 两个组件足够,省去 NDK 工程化成本。只有当你本来就是 C/C++ 渲染管线(比如相机预览、图像处理全在 native 侧),或者要精细控制设备网络 ID、做视频流回传,才值得上 NDK。二者底层走的是同一套协同框架,能力范围一致,区别只在你代码停在哪一层。


七、上线自检清单

  • 三道门槛:双端同一华为账号、WLAN、蓝牙都开了吗?模拟器不支持,必须在真机双端验证
  • 调用方向合规吗?本端是不是在 6.1.0(23) 允许的主控范围(TV/Phone/Tablet/PC-2in1)?同类型设备互调会被系统拒
  • 主版本够吗?要视频选择器(VIDEO_PICKER/IMAGE_VIDEO_PICKER)必须 6.0.0(20) 起;主控方含 TV/Phone 必须 6.1.0(23)
  • createCollaborationServiceMenuItems 放在 Menu 里了吗?@Builder 写对了吗?
  • CollaborationServiceStateDialog 挂在页面 build 里了吗?onState 三参数顺序对了吗?
  • buffer 为空和"解码失败"两种情况分开处理了吗?bufferType 只认 general.image 有兜底吗?
  • 图库多选场景,canReceiveNumber 落在 1~50 了吗?
  • NDK 侧链接 libservice_collaboration_ndk.z.so 了吗?deviceNetworkId 拷贝长度没越界吗?
  • 视频回传走了 StartCollaborationV2 吗?
  • 远端取消(1001202001)、本端取消(1001202003)都有用户提示吗?

参考与出处

本文涉及的事实性信息(API 名称、枚举取值、版本号、官方约束)来自以下官方文档,文中的结构、代码示例、调用方向矩阵与自检清单为本人整理编写:


最后一句 :跨设备互通真正难的不是"调接口",是承认它有一张写死的主控表、有一组非黑即白的门槛。把"三件套缺一列表就空"刻进调试习惯,剩下就是把 onState 三个参数接对、把 ArrayBuffer 解码成图。

相关推荐
贾伟康1 小时前
【HarmonyOS 7新能力|036】分布式数字身份工程封装:把接入逻辑放进可维护的分层结构
harmonyos·arkts·软件架构·隐私保护·数字身份
HwJack201 小时前
【HarmonyOS开发小实践】ArkUI 交互事件与手势:从触摸到组合手势
ui·华为·性能优化·harmonyos
星栖与芯2 小时前
LiteOS-M 切换汇编逐行图解(5):HalPendSV 任务切换的现场搬运(五阶段逐行)
汇编·stm32·嵌入式硬件·harmonyos
马剑威(威哥爱编程)2 小时前
【共创稿事节】HarmonyOS 7 数字身份 DID 实战:TEE 颁发、本人同意、最小化出示
华为·harmonyos
贾伟康2 小时前
【HarmonyOS 7新能力|039】冷启网络预建链工程封装:把接入逻辑放进可维护的分层结构
性能优化·harmonyos·arkts·软件架构·网络优化
星栖与芯12 小时前
LiteOS-M 切换汇编逐行图解(4):中断三件套与 HalTaskSchedule 触发
汇编·stm32·单片机·嵌入式硬件·harmonyos
李游Leo14 小时前
定位功能“偶尔失效“怎么查:HarmonyOS Location Kit 权限、订阅与地理围栏实践
harmonyos
庆登登登18 小时前
nvm 鸿蒙 PC 适配全记录:从 Shell Function 到 HNP 原生交付
华为·harmonyos
不羁的木木18 小时前
给鸿蒙 App 增加用系统应用打开文件的能力 —— open_app_file 的鸿蒙使用指南
flutter·harmonyos