手把手教你开发鸿蒙碰一碰加好友功能:告别扫码,贴贴就加友!

还在为加好友扫半天二维码、输一串数字抓狂?鸿蒙的“碰一碰”功能直接把加好友简化到“贴贴就行”!不用搜、不用输,两台手机轻轻一碰,好友信息自动飞过去,主打一个“近在咫尺,一键加友”。今天就手把手带你把这个神仙功能搬进自己的App,以喵屿App的碰一碰加宠物好友为例,从原理到实战,包教包会~

● 碰一碰功能介绍

碰一碰分享本质上是鸿蒙Share Kit的“隐藏彩蛋”,支持用户通过设备轻碰发起跨端分享,不管是传图片、共享Wi-Fi,还是咱今天要搞的加好友,都能搞定。更爽的是:只要你的应用支持系统分享,理论上不用额外魔改,就能解锁碰一碰能力,简直是懒人开发者的福音!

流程说明:
Alt

  1. 先给自家应用“注册”碰一碰分享事件,然后让两部亮屏解锁的手机来个“贴贴”(就像朋友握手一样);
  2. 应用发现“贴贴”触发后,会调用碰一碰分享回调,咱在回调里把要分享的好友数据打包发出去;
  3. 对方手机接住数据,乖乖处理;
  4. 事儿办完了,记得解除注册,别让应用“占着茅坑不拉屎”~

使用约束(划重点!不然贴贴也白贴)
想让俩手机顺利贴贴传好友,得满足几个小条件:双端设备都得亮屏、解锁(总不能黑屏贴贴吧,手机也得“醒着”才知道要干活),而且华为分享服务得开着(系统默认开的,除非你手欠手动关了)。贴的时候得用手机顶部碰,不是随便怼哪里都管用哈~如果华为分享关了,碰的时候系统会贴心提醒你打开,不用慌。

链接: 碰一碰功能介绍

● 简单的碰一碰实现

先从最简单的图片分享入手,带你尝尝碰一碰的甜头!其实鸿蒙的碰一碰超友好,只要你的应用支持系统分享,几乎不用额外折腾,几行代码就能搞定,看

 private async handelShare(): Promise<void> {
    const uiContext: UIContext = this.getUIContext();
    //需设置本地图片链接
    let filePath = this.ImgData[this.curIndex];
    let utdTypeId = utd.getUniformDataTypeByFilenameExtension('.png', utd.UniformDataType.IMAGE);
    let shareData: systemShare.SharedData = new systemShare.SharedData({
      utd: utdTypeId,
      uri: fileUri.getUriFromPath(filePath),
      title: '喵屿图片分享',
    });
    let controller: systemShare.ShareController = new systemShare.ShareController(shareData);
    const context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;
    controller.show(context, {
      previewMode: systemShare.SharePreviewMode.DETAIL,
      selectionMode: systemShare.SelectionMode.BATCH,
    }).then(() => {
      logger.info('HuaweiShare_ show');
    }).catch((error: BusinessError) => {
      logger.error(`HuaweiShare_ show error. Code: ${error?.code}, message: ${error?.message}`);
    });
  }

就这么几行,把本地图片路径填好,调用系统分享Kit,分享界面里就自带碰一碰功能了,是不是比凑半天二维码轻松多了?

● 碰一碰拉起指定应用(Deeplink的使用)

但是问题来了:默认分享的图片、文本这些,会被图库、浏览器这些“路人甲”应用打开,咱要的是直接拉起自己的App加好友啊!总不能让用户碰完,结果跳去浏览器,那也太掉链子了。

系统早就想到这点了,给了俩解决方案:一是分享Applinking/Deeplink形式的链接,二是指定应用直达(6.0.0(20) Beta3新功能,咱今天先不唠这个)。Applinking和Deeplink的区别简单说:Applinking如果对方没装你家App,会跳浏览器或应用市场,Deeplink只会告诉你没有对应应用;Deeplink的配置更简单,咱今天就拿捏它!

想用Deeplink让分享内容精准跳自家App,第一步得给App“贴标签”——在module.json5里配置skills标签,相当于给系统说:“这个链接只能我家App接,别乱分给别人!”示例如下:

{
  "module": {
    // ···
    "abilities": [
    // ···
      {
        // ···
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "ohos.want.action.home"
            ]
          },
          {
            "actions": [
              // actions不能为空,actions为空会造成目标方匹配失败。
              "ohos.want.action.viewData"
            ],
            "uris": [
              {
                // scheme必选,可以自定义,以link为例,需要替换为实际的scheme
                "scheme": "link",
                "host": "www.example.com"
              }
            ]
          } // 新增一个skill对象,用于跳转场景。如果存在多个跳转场景,需配置多个skill对象。
        ]
      },
    // ···
    ],
    // ···
  }
}

配置完标签,还得让App知道“接了链接该干啥”——在目标应用的UIAbility的onCreate()或者onNewWant()里解析链接参数,比如拿到好友信息后,就可以触发加好友逻辑了:

// 以DeepAbility.ets为例
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { url } from '@kit.ArkTS';

const DOMAIN = 0x0000;

export default class DeepAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 从want中获取传入的链接信息。
    // 如传入的url为:link://www.example.com/programs?action=showall
    let uri = want?.uri;
    if (uri) {
      // 从链接中解析query参数,拿到参数后,开发者可根据自己的业务需求进行后续的处理。
      let urlObject = url.URL.parseURL(want?.uri);
      let action = urlObject.params.get('action');
      // 例如,当action为showall时,展示所有的节目。
      if (action === 'showall') {
        // ···
      }
    }
  }
// ···
}

说白了,想让碰一碰拉起指定App干指定事,只要分享的时候把Deeplink链接发出去就行,系统会自动帮你找对App。不过上面都是靠系统分享界面实现的,有没有更“私密”的玩法,不用走系统界面?必须有!直接注册’knockShare’监听,就能自己掌控碰一碰的全过程,看官方代码👇

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

@Component
export default struct Index {
  aboutToAppear(): void {
    let capabilityRegistry: harmonyShare.SendCapabilityRegistry = {
      windowId: 999, // 此值仅为示例 实际使用时请替换正确的windowId
    }
    harmonyShare.on('knockShare', capabilityRegistry, (sharableTarget: harmonyShare.SharableTarget) => {
      let shareData: systemShare.SharedData = new systemShare.SharedData({
        utd: utd.UniformDataType.HYPERLINK,
        content: 'https://sharekitdemo.drcn.agconnect.link/ZB3p',
        // 根据title,description,thumbnailUri会生成不同的卡片模板,具体可参考设置配套的卡片样式。
        title: '碰一碰分享卡片标题',
        description: '碰一碰分享卡片描述'
      });
      // 若云端预览图无法及时下载 可先发送数据
      sharableTarget.share(shareData);

      setTimeout(() => {
        // 待预览图下载完成后 补充更新预览图
        let uiContext: UIContext = this.getUIContext();
        let contextFaker: Context = uiContext.getHostContext() as Context;
        let filePath = contextFaker.filesDir + '/exampleKnock1.jpg'; // 仅为示例 请替换正确的文件路径
        sharableTarget.updateShareData({
          thumbnailUri: fileUri.getUriFromPath(filePath)
        });
      }, 5000);
    });
  }

  aboutToDisappear(): void {
    let capabilityRegistry: harmonyShare.SendCapabilityRegistry = {
      windowId: 999, // 此值仅为示例 实际使用时请替换正确的windowId
    }
    // 解除碰一碰分享'knockShare'监听事件
    harmonyShare.off('knockShare', capabilityRegistry);
  }

  build() {
  }
}

● 碰一碰加好友功能实战

光说不练假把式,接下来咱就拿“喵屿App”的碰一碰加宠物好友为例,手把手带你把这个功能落地,让养宠党贴贴手机就能互加宠物好友,再也不用手动输宠物信息啦!

碰一碰接收端处理

想让碰一碰跳自家App加好友,第一步得给App“办个身份证”——在module.json5里配置skills标签,告诉系统:“这个链接是我的,别给别人!”

   "abilities": [
    {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon" : "$media:layered_image",
        "startWindowBackground" : "$color:start_window_background"
          ,
        "exported": true
          ,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "ohos.want.action.home"
            ]
          },
          {
            "actions": ["action.system.viewData"],
            "entities": ["entity.system.browser"],
            "uris": [
              {
                // scheme必选,可以自定义,以catIsland为例,需要替换为实际的scheme
                "scheme": "catIsland",
                "host": "catIsland.sx.com"
              }
            ]
          } // 新增一个skill对象,用于跳转场景。如果存在多个跳转场景,需配置多个skill对象。
        ]
      }]

碰一碰然后选择接收数据会走到onCreate()或者onNewWant()生命周期
划重点: 碰一碰接收数据后,App会走onCreate()或onNewWant()生命周期,俩都得处理,不然会漏数据!

然后就得让App“读懂”链接里的好友信息了——在EntryAbility的onCreate()和onNewWant()里解析链接参数,把宠物姓名、性别、生日这些信息抠出来,存到AppStorage里,等着后续处理:

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 从want中获取传入的链接信息。
    // 如传入的url为:catIsland://catIsland.sx.com/share/?name=${info.name}&sex=${info.sex}&birth=${info.birth}&breed=${info.breed}
    let uri = want?.uri;
    if (uri) {
      // 从链接中解析query参数,拿到参数后,开发者可根据自己的业务需求进行后续的处理。
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate' + JSON.stringify(uri));
      const urlObject = url.URL.parseURL(want?.uri);
      const name = urlObject.params.get('name');
      const sex = urlObject.params.get('sex') ;
      const birth = urlObject.params.get('birth');
      const breed = urlObject.params.get('breed');
      if(name){
        let baseInfo = new BaseInfo(name)
        baseInfo.sex = Number(sex)
        baseInfo.birth = birth
        baseInfo.breed = breed
        AppStorage.setOrCreate('newFriend', JSON.stringify(baseInfo)); //数据保存起来
      }
    }

			...
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 从want中获取传入的链接信息。
    // 如传入的url为:catIsland://catIsland.sx.com/share/?name=${info.name}&sex=${info.sex}&birth=${info.birth}&breed=${info.breed}
    let uri = want?.uri;
    if (uri) {
      // 从链接中解析query参数,拿到参数后,开发者可根据自己的业务需求进行后续的处理。
      hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate' + JSON.stringify(uri));
       const urlObject = url.URL.parseURL(want?.uri);
      const name = urlObject.params.get('name');
      const sex = urlObject.params.get('sex') ;
      const birth = urlObject.params.get('birth');
      const breed = urlObject.params.get('breed');
      if(name){
        let baseInfo = new BaseInfo(name)
        baseInfo.sex = Number(sex)
        baseInfo.birth = birth
        baseInfo.breed = breed
        AppStorage.setOrCreate('newFriend', JSON.stringify(baseInfo));//数据保存起来
        emitter.emit(BaseConstants.NEW_FRIEND) //通知有新朋友数据
      }
    }
  }
  ...

为啥俩生命周期都要解析?简单说:App冷启动(第一次打开)走onCreate(),如果App已经开着,Deeplink拉它就只走onNewWant()。俩都处理,不管App是开是关,都能稳稳接住好友数据,主打一个“不漏网之鱼”!

在这里插入图片描述
MainPage弹窗选择是否保存好友数据

数据存好了,总得让用户知道吧?在App首页MainPage.ets里监听好友数据,只要有新数据,就弹个窗让用户选“加好友”还是“拒绝”,体验直接拉满:

 onNewFriend = ()=>{
    if(AppStorage.get('newFriend') != undefined){
      let newFriend = JSON.parse(AppStorage.get('newFriend')!!) as BaseInfo
      console.log('testTag' + JSON.stringify(newFriend))
      AppStorage.set('newFriend',  undefined)
      this.friendDialog = DialogHub.getCustomDialog()//好友数据弹窗可以选择接受或者拒绝
        .setOperableContent(wrapBuilder(NewFriendDialogBuilder), (action: DialogAction) => {
          let para = new PetDialogParams(newFriend, action, this.promptAction,  0);
          return para;
        })
        .setConfig({ dialogBehavior: { isModal: true, autoDismiss: false, passThroughGesture: false } })
        .setAnimation({ dialogAnimation: AnimationType.BOTTOM_UP })
        .setStyle({
          radius: 32,
          backgroundColor: Color.White,
          maskBackgroundEffect: { blurOptions: { grayscale: [50, 50] }, radius: 10 }
        })
        .build();
      this.friendDialog.show();
    }
  }

 aboutToAppear(): Promise<void> {
    emitter.on(BaseConstants.NEW_FRIEND, this.onNewFriend) //注册好友申请的监听
    this.onNewFriend()
    ...
 }
 ...
 aboutToDisappear(): void {
    emitter.off(BaseConstants.NEW_FRIEND.eventId, this.onNewFriend)
  }

App启动后打开MainPage,先注册好友申请监听,确保主页在的时候能实时收到通知;然后立刻检查有没有待处理的好友数据,有就弹窗,不让用户等,主打一个“贴心”!

碰一碰发送端处理

宠物信息界面设置碰一碰监听
宠物信息界面设置碰一碰监听
碰一碰后触发的分享界面
碰一碰后触发的分享界面

最后一步,让碰一碰“活”起来!在宠物信息界面注册碰一碰监听,只要检测到两台手机贴贴,就把当前宠物的信息打包进Deeplink,注意要和前面moudule.json5中定义的scheme和host要匹配, 对方接住后就能按上面的流程处理好友申请了:

showID(info: BaseInfo, index: number = 0) {
    harmonyShare.on('knockShare', (sharableTarget: harmonyShare.SharableTarget) => {
      const filePath = info.icon; // 仅为示例 请替换正确的文件路径
      let shareData: systemShare.SharedData = new systemShare.SharedData({
        utd: utd.UniformDataType.HYPERLINK,
        content: `catIsland://catIsland.sx.com/share/?name=${info.name}&sex=${info.sex}&birth=${info.birth}&breed=${info.breed}`,
        // 根据title,description,thumbnailUri会生成不同的卡片模板。
        thumbnailUri: fileUri.getUriFromPath(filePath),
        title: `分享${info.name}给好友`,
        description: '碰一碰即可将宠物信息分享给好友'
      });
      sharableTarget.share(shareData);
    });

    this.IDDialog = DialogHub.getCustomDialog()
      .setOperableContent(wrapBuilder(PetIDDialogBuilder), (action: DialogAction) => {
        let para = new PetDialogParams(info, action, this.promptAction,  index);
        return para;
      })
      .setConfig({ dialogBehavior: { isModal: true, autoDismiss: false, passThroughGesture: false } })
      .setAnimation({ dialogAnimation: AnimationType.BOTTOM_UP })
      .setStyle({
        radius: 32,
        backgroundColor: Color.White,
        maskBackgroundEffect: { blurOptions: { grayscale: [50, 50] }, radius: 10 }
      })
      .build();
    this.IDDialog.show();
  }
}

这里踩个坑提醒各位老铁:博主最开始傻乎乎跟着官方新API写,用了6.0.0(20)新增的带capability参数的on方法,结果发现5.0版本的设备直接不兼容,主打一个“新是新,用不了”!最后只能乖乖换回5.0.0(12)的低版本API(不用传capability)。大家用的时候可别踩这个坑——新API虽香,但得看你的用户设备支不支持啊!

	//注册设备轻贴的事件监听。
	//起始版本:6.0.0(20)
	on(event: 'knockShare', capability: SendCapabilityRegistry, callback: Callback<SharableTarget>): void
	//起始版本:5.0.0(12)
	on(event: 'knockShare', callback: Callback<SharableTarget>): void

实际效果演示可自行下载APP碰一碰试一试: 喵屿下载链接

总结

怎么样,是不是发现鸿蒙碰一碰加好友功能一点都不复杂?从配置Deeplink让链接精准跳自家App,到解析冷/热启动的参数不漏数据,再到监听碰一碰事件分享好友信息,一套流程走下来,你的App也能拥有“贴贴加好友”的神仙能力!

相比传统的扫码、输码加好友,碰一碰直接把操作简化到“贴一下”,用户体验直接拉满。而且整个开发过程几乎不用造轮子,依托鸿蒙Share Kit就能搞定,唯一要注意的就是API版本兼容问题,别像博主一样踩坑就行。

现在就把这些代码搬去你的项目里试试吧,不管是做宠物社交、熟人社交还是其他场景,碰一碰加好友都能让你的App多一个“让人眼前一亮”的小功能。要是开发过程中踩了啥新坑,或者有更酷的玩法,评论区唠唠~

Logo

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

更多推荐