HarmonyOS:如何实现NFC碰一碰快捷唤起你的应用

285 阅读4分钟

引言

鸿蒙平台提供 AirTouch服务,支持元服务、原生鸿蒙应用、快应用等实现 NFC标签触达功能。

典型场景,例如:

  • 餐饮:用户手机碰Tag标签 -> Portal页 -> 快速点餐。

  • 出行:用户手机碰Tag标签 -> Portal页 -> 解锁共享单车。

本文,小编将业务中实现的能力NFC碰一碰唤起我方应用中的‘技术要点’及‘实现步骤’进行整理归纳。

一、创建 AirTouch 服务

  1. 登录AppGallery Connect网站,点击“我的项目”。

image.png

  1. 选择需要创建 AirTouch 服务的项目,进入项目设置页面

image.png

  1. 在“项目设置”页签,左侧导航选择“构建 > AirTouch”。

image.png

  1. 点击“申请AirTouch服务”。

image.png

  1. 设置AirTouch服务基本信息。

image.png

  1. 点击“保存”,创建完成后,返回AirTouch服务创建首页,可对所创建的服务进行详情查看、修改与直达服务的创建。

image.png

  1. 在AirTouch服务首页列表项中,点击“申请直达服务”按钮,进入直达服务创建首页。

image.png

  1. 点击“新建商家直达服务”,请填写服务信息。点击“下一步”。

image.png

image.png

  • HarmonyOS NEXT版本请选择HMACSHA256签名算法。
  • 选择关联MimeType的PayLoad作为查询参数,下文我会举例如何在NFC唤起应用时获取携带数据
  1. 点击“下一步”,设置AirTouch卡片样式。

image.png

右侧为NFC碰一碰后提示用户将拉起的应用弹窗效果图

需要注意的是跳转地址如何填写?例如:在元服务跳转地址栏目填写跳转链接:

image.png

以我的demo应用为例,填写的跳转链接内容为:

{
    "bundleName": "com.atomicservice.5765880207854372291",
    "abilityName": "EntryAbility",
    "moduleName": "entry"
}
  1. 点击“下一步”,完成商家直达服务的配置,此时该服务状态为待审核,审核通过后,可下载对应的直达标签内容(写入标签时需要用到)。

image.png

二、NFC 标签需要写入哪些数据

标签需要写入一条URI记录,一条AirTouch格式记录,两条AAR(Android Application Record)记录,和一条MIME格式记录,需要按顺序写入。

标签格式(包含五部分):

标签写入参考以下代码样例:

String airTouchContent = "下载的标签内容,审核通过后即可下载";
// 默认的跳转链接,也就是新建商家服务直达里填写的内容
String airTouchDefaultLink = "https:/default.airtouch.huawei.com/blankcodelabdev"; 
// 若选择"标签中关联MimeType的PayLoad作为查询参数",需要以下两个变量
// 跳转链接参数,例如创建时填写的 paramId
String airTouchJumpParamKey = "paramId";
String airTouchJumpParamValue = "跳转链接参数值";
byte[] TYPE_HW = {0x68, 0x77};
NdefMessage ndefMessage = new NdefMessage(
    new NdefRecord[] {
        NdefRecord.createUri(airTouchDefaultLink),
        new NdefRecord(NdefRecord.TNF_MIME_MEDIA, TYPE_HW, new byte[0], Base64.decode(airTouchContent, Base64.NO_WRAP)),
        NdefRecord.createApplicationRecord("com.huawei.hwid"),
        NdefRecord.createApplicationRecord("com.huawei.hms"),
        // 若选择"标签中关联MimeType的PayLoad作为查询参数",需要加入以下NdefRecord
        new NdefRecord(NdefRecord.TNF_MIME_MEDIA, airTouchJumpParamKey, new byte[0], airTouchJumpParamValue),
    }
);
.......
Tag tag = intent.getParcelbleExtra(NfcAdapter.EXTRA_TAG);
Ndef ndef = Ndef.get(tag);
......
ndef.writeNdefMessage(ndefMessage);

使用Flutter开发的NFC写入代码示例

import 'dart:typed_data';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:ndef/ndef.dart' as ndef;
import 'dart:convert';
import 'dart:developer' as dev;

abstract class NfcWriteTool {
  static const url = '默认的跳转链接';
  static const tagContent = '下载的标签内容';
  static final content = ndef.NDEFRecord(
    tnf: ndef.TypeNameFormat.media,
    type: Uint8List.fromList(utf8.encode('hw')),
    payload: base64Url.decode(tagContent),
  );
  
  // 搜索是否有可写入设备
  static Future<bool> findNfcTag({
    int timeout = 20,
    bool checkWritable = true,
  }) async {
    try {
      final nfcTag = await FlutterNfcKit.poll(
        timeout: Duration(seconds: timeout),
      );
      if (checkWritable) {
        return nfcTag.ndefWritable ?? false;
      } else {
        return true;
      }
    } catch (e) {
      return false;
    }
  }
  
  // 将数据写入NFC设备
  static Future<bool> writeNDEFRecords({required String paramId}) async {
      final param = ndef.NDEFRecord(
        tnf: ndef.TypeNameFormat.media,
        type: Uint8List.fromList(utf8.encode('paramId')),
        payload: Uint8List.fromList(utf8.encode(paramId)),
      );
      try {
         await FlutterNfcKit.writeNDEFRecords([
           ndef.UriRecord.fromString(url),
           content,
           ndef.AARRecord(packageName: 'com.huawei.hwid'),
           ndef.AARRecord(packageName: 'com.huawei.hms'),
           param
          ]);
        await FlutterNfcKit.finish();
        return true;
      } catch (e) {
        return false;
      }
    }
}

三、如何获取 NFC 携带的参数

在入口文件EntryAbility中做以下处理,接收入参

export default class EntryAbility extends UIAbility {

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.receiveWantParams(want, false)
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT)
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.receiveWantParams(want, true)
  }
} 
receiveWantParams(want: Want, isRelaunch: boolean): void {
  const wantAction = want.action ?? ''
  if (want.parameters) {
    if (wantAction === LaunchChannel.NFC) {
      const paramId = (want.parameters['tagParam'] as string) ?? ''
      // 这个 paramId 就是NFC交互时携带的数据,可自行处理,例如存入`appStorage 或 static`,然后在主页面根据数据响应逻辑(跳转,弹窗)等
    }
  }
}

运行效果图

由于示例项目申请的服务还没过审,直接展示项目效果如下:

1739171972766.gif

文档参考:developer.huawei.com/consumer/cn…