1. 介绍
在HarmonyOS分布式能力加持下,通过“碰一碰”实现古诗秒传的创新应用。用户只需将两台鸿蒙设备轻触,即可瞬间完成古诗内容传输与页面跳转,打造零门槛的跨设备文化共享体验。
用户体验:
- 家长与孩子碰触设备,即可将《静夜思》的诗词解析同步过去,打造沉浸式国学场景。
- 课堂场景中,教师设备轻碰学生平板,精选诗词集瞬间分发,配合鸿蒙分布式文件系统实现多端批注同步。
充分展现HarmonyOS“一次开发,多端部署”的优势,通过硬件级近场交互与软总线技术融合,让传统文化传承拥有更优雅的数字化表达,为教育、亲子等场景提供高效连接方案。
2. 效果

3. 使用App Linking实现应用间跳转
在AGC控制台开通App Linking服务
请先参考“应用开发准备”完成基本准备工作,再继续进行以下开发活动。
登录AppGallery Connect,点击“我的项目”。
在项目列表中点击您的项目。
在左侧导航栏中选择“增长 > App Linking”,进入App Linking页面,点击“立即开通”。

如果您的项目此时未设置数据处理位置,请在提示框内启用数据处理位置和设置默认数据处理位置,点击“确定”。

进入“项目设置 > 常规”页面,选择创建的HarmonyOS应用,查看应用的APP ID,后续开发需要使用该ID。

在开发者网站上关联应用
在开发者的网站域名服务器上做如下配置。后续当您配置该网站域名时,系统会通过此文件确认哪些应用才是合法归属于此域名的,使链接更加安全可靠。
创建域名配置文件applinking.json,内容如下:
{
"applinking": {
"apps": [
{
"appIdentifier": "1234567"
}
]
}
}
说明
appIdentifier填写在AGC控制台开通App Linking服务的步骤5展示的APP ID。
同一个网站域名可以关联多个应用,只需要在"apps"列表里放置多个"appIdentifier"元素即可,其中每个"appIdentifier"元素对应每个应用。
将配置文件放在域名服务器的固定目录下:
https://domain.name/.well-known/applinking.json
例如:开发者的服务器域名为www.example.com,则必须将applinking.json文件放在如下位置:
https://www.example.com/.well-known/applinking.json
在AGC控制台关联网址域名
基于HarmonyOS应用链接能力,需要为HarmonyOS应用创建关联的网址域名。如果用户已安装HarmonyOS应用,则用户点击域名下网址链接后,系统会默认打开该HarmonyOS应用内的相关页面。
登录AppGallery Connect,点击“我的项目”。
在项目列表中点击您的项目。
在左侧导航栏中选择“增长 > App Linking”,选择“应用链接(API>=12适用)”页签,点击“创建”。

填写在开发者网站上关联应用的网址域名,例如:https://www.example.com。必须输入精确的域名,不可输入包含特殊字符的模糊网址。

设置完成后点击“发布”,AGC会对该网站域名的配置文件所包含的应用与本项目内的应用列表进行交集校验。

4. 封装ShareKitUtils分享公共类
export class ShareKutUtils {
private constructor() {}
private static instance: ShareKutUtils;
public static getInstance(): ShareKutUtils {
if (!ShareKutUtils.instance) {
ShareKutUtils.instance = new ShareKutUtils();
}
return ShareKutUtils.instance;
}
/**
* 分享回调
* 先截图当前屏幕,再分享
* @param sharableTarget
* @param shareUrl
*/
async shareKitCallback(sharableTarget: harmonyShare.SharableTarget, shareUrl: string) {
let windowClass:window.Window = await window.getLastWindow(getContext(this))
windowClass.snapshot((error, image) => {
if (image) {
ShareKutUtils.getInstance().pixelToFile(image).then((filePath: string) => {
let shareData: systemShare.SharedData = new systemShare.SharedData({
// HYPERLINK显示为链接;HTML显示为网页;TEXT显示为文本;VIDEO显示为视频;
// AUDIO显示为音乐;IMAGE显示为图片;FILE显示为文件;
utd: utd.UniformDataType.HYPERLINK,
content: shareUrl,
thumbnailUri: filePath
});
sharableTarget.share(shareData);
})
console.log("xx snapshot success");
} else {
console.log("xx snapshot failure: " + JSON.stringify(error));
}
});
}
/**
* PixelMap保存为缓存文件
* @param pixelImg
* @returns
*/
pixelToFile(pixelImg:PixelMap):Promise<string>{
return new Promise((resolve,reject)=>{
const context: Context = getContext().getApplicationContext();
const path: string = context.cacheDir + "/cacheImage.jpg";
let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 }
let file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
const imagePackerApi: image.ImagePacker = image.createImagePacker();
imagePackerApi.packToFile(pixelImg, file.fd, packOpts, async(err: BusinessError) => {
if (err) {
console.error('packToFile failed.');
reject(null)
} else {
let imgUri = fileUri.getUriFromPath(file.path)
resolve(imgUri)
}
})
})
}
}
5. 应用module.json5配置
- "entities"列表中必须包含"entity.system.browsable"。
- "actions"列表中必须包含"ohos.want.action.viewData"。
- "uris"列表中必须包含"scheme"为"https"且"host"为域名地址的元素,可选属性包含"path"、"pathStartWith"和"pathRegex",具体请参见“uris标签说明”。
- "domainVerify"设置为true,表示开启域名校验开关。
例如,声明应用关联的域名是www.example.com,则需进行如下配置。
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
// 请将exported配置为true;如果exported为false,仅具有权限的系统应用能够拉起该应用,否则无法拉起应用
"exported": true,
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
// API19及以上版本须配置为"ohos.want.action.home",API18及以下版本请配置为"action.system.home"
"ohos.want.action.home"
]
},
{
"entities": [
// entities必须包含"entity.system.browsable"
"entity.system.browsable"
],
"actions": [
// actions必须包含"ohos.want.action.viewData"
"ohos.want.action.viewData"
],
"uris": [
{
// scheme须配置为https
"scheme": "https",
// host须配置为关联的域名
"host": "www.example.com",
// path可选,表示域名服务器上的目录或文件路径,例如www.example.com/path1中的path1
// 如果应用只能处理部分特定的path,则此处应该配置应用所支持的path,避免出现应用不能处理的path链接也被引流到应用中的问题
"path": "path1"
}
],
// domainVerify须设置为true
"domainVerify": true
}
// 若有其他跳转能力,如推送消息跳转、NFC跳转,可新增一个skill对象,防止与App Linking业务冲突
]
}
]
}
}
6. 古诗页碰一碰分享监听
aboutToAppear(): void {
// 开始启用监听
this.whiteListening()
AppStorage.setOrCreate("obj", this.detailData)
}
aboutToDisappear(): void {
// 禁用监听
this.whiteDisablingListening()
}
/**
* 启用监听
*/
private whiteListening() {
harmonyShare.on('knockShare', this.whiteCallback);
}
/**
* 禁用监听
*/
private whiteDisablingListening() {
harmonyShare.off('knockShare', this.whiteCallback);
}
/**
* 回调函数
* @param sharableTarget
*/
private whiteCallback(sharableTarget: harmonyShare.SharableTarget) {
let obj = AppStorage.get("obj") as Record<string, string>;
ShareKutUtils.getInstance().shareKitCallback(sharableTarget, `https://www.example.com/path1?action=detail&id=${obj.id}`)
}
7. EntryAbility参数接收
7.1 定义全局变量
let currentWindowStage: window.WindowStage;
let selectPage: string | undefined = "";
let id: string | undefined = "";
7.2 冷启动时,接收参数存储在全局变量
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 从want中获取传入的链接信息。
let uri = want?.uri
if (uri) {
let urlObject = url.URL.parseURL(want?.uri);
let action = urlObject.params.get('action')
let dataId = urlObject.params.get('id')
if (action === "detail"){
selectPage = "Detail"
id = dataId+""
}
}
if (currentWindowStage !== undefined && currentWindowStage !== null) {
this.onWindowStageCreate(currentWindowStage);
}
}
7.3 热启动时,接收参数存储在全局变量
async onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
// 从want中获取传入的链接信息。
let uri = want?.uri
if (uri) {
let urlObject = url.URL.parseURL(want?.uri);
let action = urlObject.params.get('action')
let dataId = urlObject.params.get('id')
if (action === "detail"){
selectPage = "Detail"
id = dataId+""
}
}
if (currentWindowStage !== undefined && currentWindowStage !== null) {
this.onWindowStageCreate(currentWindowStage);
}
}
7.4 存储参数id到AppStorage,并根据参数跳转到相应页面
onWindowStageCreate(windowStage: window.WindowStage): void {
AppStorage.setOrCreate<string>("id", id);
let targetPage: string;
// 根据传递的targetPage不同,选择拉起不同的页面
switch (selectPage) {
case 'Detail':
targetPage = 'pages/Detail';
break;
case 'Index':
targetPage = 'pages/Index';
break;
default:
targetPage = 'pages/Index';
}
if (currentWindowStage === undefined || currentWindowStage === null) {
currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
8. 古诗页获取参数
aboutToAppear() {
const uid: string | undefined = AppStorage.get<string>('id')??"0";
}
9. 总结
对应用要进行手动签名,不能使用DevEco Studio的自动签名功能,必须使用手动签名,否则无法拉起应用。分享公共类通过碰一碰后,把当前界面截图下来,当作分享预览页。在教育、亲子场景中展现显著价值:教师可高效分发诗词素材至学生平板,家长能便捷同步赏析内容至其它设备,实现跨形态设备的文化内容流转。未来,可拓展至多模态诗词互动、AI诵读跟读等场景,结合鸿蒙分布式数据库实现多端批注同步,持续挖掘近场交互与传统文化数字化的结合点,为教育信息化与文化传承提供创新范式。