智能客服
你问我答,随时在线为你解决问题
Share Kit(分享服务)提供隔空传送分享,支持用户通过“一抓一放”手势实现跨设备文件分享(图片、视频、文档等)以及跨设备链接分享。
隔空传送分享文件 | 通过隔空传送手势触发文件分享,接收端为phone、tablet设备会将媒体文件存储至图库,非媒体文件存储至文件管理器,接收端为PC/2in1设备则会将媒体以及非媒体文件存储至文件管理器。
|
|---|---|
隔空传送分享链接 | 通过隔空传送手势触发分享App Linking链接,接收端已安装应用可直接打开应用查看内容。
|

隔空传送基于分享服务,允许用户通过简单的“一抓一放”手势实现跨设备分享,当前支持手机、平板、PC,使用体验无差异,应用接入只需监听harmonyShare.on('gesturesShare')方法。
当用户做出手势进行隔空传送分享时,系统触发回调,应用可以在回调中实现数据分享。
接收端的处理由系统统一管理:

harmonyShare(华为分享)模块提供了隔空传送分享事件的监听方法on('gesturesShare')及取消监听的方法off('gesturesShare')。
隔空传送监听方法:
on(event: 'gesturesShare', capability: SendCapabilityRegistry, callback: Callback<SharableTarget>): void
该方法需传入capability参数,类型为SendCapabilityRegistry,继承自BaseCapabilityRegistry。其包含windowId属性,需要传入当前应用的窗口ID。在PC/2in1或平板自由窗口模式中,系统会根据窗口ID判断当前应用是否获取了窗口焦点。只有在获取焦点后,当用户触发隔空传送事件时,才会触发应用注册的隔空传送方法。
该方法支持phone、tablet和PC/2in1设备类型,在多设备开发时建议使用该接口。
定义ShareModel类,用于封装分享相关方法,也便于分享页面调用相关方法,首先需要导入相关模块,其中harmonyShare、systemShare为分享服务相关模块。
import { harmonyShare, systemShare } from '@kit.ShareKit';
import { uniformTypeDescriptor } from '@kit.ArkData';
import { fileUri } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { FileData, ShareType, VIDEO_SOURCES } from './FileData';将隔空传送监听方法on('gesturesShare')封装为immersiveListening(),取消监听的方法off('gesturesShare')封装为immersiveDisableListening()。在分享页面可通过初始化ShareModel类并调用immersiveListening()方法来启动监听,调用immersiveDisableListening()方法取消监听。
/**
* Add gesturesShare listening.
*/
public immersiveListening(shareType: ShareType) {
if (canIUse('SystemCapability.Collaboration.HarmonyShare')) {
window.getLastWindow(this.context).then((data) => {
try {
let mainWindowID: number = data.getWindowProperties().id;
harmonyShare.on('gesturesShare', { windowId: mainWindowID }, (target: harmonyShare.SharableTarget) => {
this.immersiveCallback(target, shareType);
});
} catch (err) {
let error = err as BusinessError;
hilog.error(0x0000, 'GesturesShare', `getWindowProperties error ${error.code} ${error.message}`);
}
}).catch((error: BusinessError) => {
hilog.error(0x0000, 'GesturesShare', `immersiveListening error ${error.code} ${error.message}`);
})
}
}
/**
* Remove gesturesShare listening.
*/
public immersiveDisableListening() {
if (canIUse('SystemCapability.Collaboration.HarmonyShare')) {
window.getLastWindow(this.context).then((data) => {
try {
let mainWindowID: number = data.getWindowProperties().id;
harmonyShare.off('gesturesShare', { windowId: mainWindowID });
} catch (error) {
let err = error as BusinessError;
hilog.error(0x0000, 'GesturesShare', `getWindowProperties error ${err.code} ${err.message}`);
}
}).catch((error: BusinessError) => {
hilog.error(0x0000, 'GesturesShare', `immersiveDisableListening error ${error.code} ${error.message}`);
})
}
}开发者应在用户进入隔空传送分享页面时调用隔空传送分享接口。当应用不再需要隔空传送分享文件或离开页面(包括退至后台等情况)时,应及时调用取消监听的方法。ShareModel模块自定义封装了分享相关方法,可以通过调用this.shareModel.immersiveListening()方法并传入ShareType.FILE_SHARE来注册文件分享事件监听,调用this.shareModel.immersiveDisableListening()来取消监听。
当用户在文件分享页面勾选或取消勾选文件时,会触发onChange()方法,在该方法中,调用setFileShare()方法将要分享的文件索引数据存储到shareModel实例中,以便在生成分享数据时使用。
import { common } from '@kit.AbilityKit';
import { filePreview } from '@kit.PreviewKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { FileData, FILE_SOURCES, ShareType } from '../model/FileData';
import { ShareModel } from '../model/ShareModel';
import { FileUtil, BreakpointConstants, BreakpointType } from '@ohos/common';
@Component
export struct FileSharePageComponent {
// ...
// Pending shared data index.
@State fileShare: number[] = [];
// All file data collection.
@State dataList: FileData[] = FILE_SOURCES;
build() {
NavDestination() {
List({ space: 16 }) {
ForEach(this.dataList, (item: FileData, index: number) => {
ListItem() {
Stack() {
// ...
Column() {
Checkbox({ name: index + '', group: 'checkboxGroup'})
// ...
.onChange((value: boolean) => {
if (value) {
this.fileShare.push(index);
} else {
this.fileShare.splice(this.fileShare.indexOf(index), 1);
}
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel.setFileShare(this.fileShare);
this.shareModel.setVideoDataList(this.dataList);
})
// ...
}
// ...
.onShown(() => {
// ...
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel.immersiveListening(ShareType.FILE_SHARE);
})
.onHidden(() => {
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel.immersiveDisableListening();
})
}
}在分享数据时,分享发起方需要构建SharedRecord对象。在文件分享场景中,发起方在构造此参数时,必须传入uri和utd这两个属性。
在ShareModel模块中定义getShareRecord()方法,用于根据当前的文件类型构建分享数据。定义getFileShareData()方法,当需要分享多个文件时,该方法会通过循环处理来获取分享数据。
/**
* Get file Share data.
* @returns systemShare.SharedData.
*/
private getFileShareData(): systemShare.SharedData {
let shareData: systemShare.SharedData =
new systemShare.SharedData(this.getShareRecord(this.videoDataList[this.fileShare[0]]));
try {
for (let i = 1; i < this.fileShare.length; i++) {
shareData.addRecord(this.getShareRecord(this.videoDataList[this.fileShare[i]]));
}
} catch (err) {
let error = err as BusinessError;
hilog.error(0x0000, 'GesturesShare', `shareData.addRecord error ${error.code} ${error.message}`);
}
return shareData;
}
/**
* Get shared data.
*
* @param data File data to be shared.
* @returns systemShare.SharedRecord.
*/
private getShareRecord(data: FileData): systemShare.SharedRecord {
let suffix = '.' + data.url.split('.').pop();
// Obtain the UTD through the file extension.
let utd = uniformTypeDescriptor.getUniformDataTypeByFilenameExtension(suffix);
hilog.info(0x0000, 'GesturesShare', `getShareRecord utd ${utd}`);
return {
utd: utd,
uri: data.url,
thumbnailUri: data.thumbnail,
title: data.name,
description: data.description
};
}在隔空传送事件回调中,调用this.immersiveCallback()方法,通过传入的ShareType类型来判断当前是文件分享还是链接分享,文件分享调用getFileShareData()方法构造分享数据,并通过sharableTarget.share()方法分享数据,完成隔空传送文件分享流程。
public immersiveCallback(target: harmonyShare.SharableTarget, shareType: ShareType) {
if (shareType === ShareType.FILE_SHARE) {
if (!this.fileShare || this.fileShare.length === 0) {
return;
}
let shareData: systemShare.SharedData = this.getFileShareData();
target.share(shareData);
} else {
let shareData: systemShare.SharedData = this.getLinkShareData();
target.share(shareData);
}
}分享接收端接收数据时遵循统一规则,详情请参考目标设备接收分享数据一步直达体验。当传入的分享参数utd为uniformTypeDescriptor.UniformDataType.IMAGE且文件确认为图片文件时,接收端为phone、tablet设备时默认将其存储在图库中。当传入的utd为uniformTypeDescriptor.UniformDataType.FILE时,文件会默认存储到文件管理中。接收端为PC/2in1设备类型时则会将媒体以及非媒体文件存储至文件管理器。系统预置了常用类型,详情可参考UTD预置列表。
开发者可以通过分享 AppLinking 链接实现应用间的无缝跳转和内容精准直达。当用户通过隔空传送分享链接时,接收端接收链接后:
详情请参考碰一碰视频分享典型场景章节。
分享链接再注册监听以及取消监听与分享文件章节下的分享注册监听与取消监听处理过程一致,开发者可参考分享文件这一章节的内容,在调用immersiveListening()方法需传入ShareType.LINK_SHARE来注册链接分享事件监听,调用this.shareModel.immersiveDisableListening()来取消监听。
在分享链接页面,当用户点击不同的视频集数时,会触发onClick()方法。在该方法中,调用setVideoIndex()方法将当前集数存储到shareModel实例中,以便在生成分享链接时使用。
import { common } from '@kit.AbilityKit';
import { JSON } from '@kit.ArkTS';
import { FileData, ShareType, VIDEO_SOURCES } from '../model/FileData';
import { ShareModel } from '../model/ShareModel';
import { BreakpointConstants } from '@ohos/common';
@Component
export struct LinkSharePageComponent {
// ...
// Current playing video index.
@State currentVideoIndex: number = 0;
@State url: ResourceStr = $rawfile(this.dataList[this.currentVideoIndex].url);
build() {
NavDestination() {
Scroll() {
Column() {
// ...
Column() {
// ...
Scroll() {
Row({ space: 10 }) {
ForEach(this.dataList, (item: FileData, index) => {
RelativeContainer() {
// ...
}
// ...
.onClick(() => {
this.currentVideoIndex = index;
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel.setVideoIndex(this.currentVideoIndex);
this.videoController.reset();
this.url = $rawfile(item.url);
})
// ...
}
// ...
.onShown(() => {
// ...
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel?.immersiveListening(ShareType.LINK_SHARE);
})
.onHidden(() => {
this.shareModel = ShareModel.getInstance(this.context);
this.shareModel?.immersiveDisableListening();
})
}
// ...
}在隔空传送事件回调中调用this.immersiveLinkCallback()方法,实现分享数据构建,在链接分享场景中,发起方在构造SharedRecord参数时,必须传入content和utd这两个属性,并通过sharableTarget.share()方法传输数据,完成隔空传送链接分享流程。
getLinkShareData(): systemShare.SharedData {
// share app linking.
let videoData: FileData = VIDEO_SOURCES[this.videoIndex];
// Video thumbnail image sandbox path.
let filePath: string = this.context?.filesDir + `/${videoData.thumbnail}`;
// Get video thumbnail URI path.
let coverUri: string = fileUri.getUriFromPath(filePath);
let shareData: systemShare.SharedData = new systemShare.SharedData({
// Set the shared data type to Link.
utd: uniformTypeDescriptor.UniformDataType.HYPERLINK,
// The shared App Linking link is replaced with the real address here.
content: `https://www.example.com?videoIndex=${this.videoIndex}`,
thumbnailUri: coverUri,
title: videoData.name,
description: videoData.description
});
return shareData;
}开发者可参考使用App Linking实现应用间跳转进行配置和使用。例如,此处配置的App Linking的链接为:www.example.com,开发者需在entry模块的module.json5中进行如下配置:
{
"module": {
// ...
"abilities": [
{
"name": "EntryAbility",
// ...
"exported": true,
"skills": [
{
"entities": [
"entity.system.home",
// entities must contain "entity.system.browsable"
"entity.system.browsable"
],
"actions": [
"ohos.want.action.home",
// Actions must contain "ohos.want.action.viewData"
"ohos.want.action.viewData"
],
"uris": [
{
// The scheme must be configured as https
"scheme": "https",
// The host must be configured as the associated domain name
"host": "www.example.com",
"path": ""
}
],
// domainVerify must be set to true
"domainVerify": true
}
]
}
],
// ...
}
}当对端设备已安装应用并收到分享的 App Linking 链接后,系统会拉起应用。根据应用是否已在运行,有以下两种情况需处理:
应用未运行:此时跳转应用,应在onCreate()方法中获取链接中的视频唯一标识符videoIndex并存储到AppStorage中,同时存储GesturesShare_isShareLink为true,表示当前应用通过分享系统启动,以便于跳转到隔空传送链接分享页面播放视频。
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ...
let videoIndex = this.getVideoIndex(want);
if (videoIndex !== '') {
AppStorage.setOrCreate('GesturesShare_shareVideoIndex', Number.parseInt(videoIndex));
AppStorage.setOrCreate('GesturesShare_isShareLink', true);
}
}应用已运行:此时跳转应用,需在onNewWant()方法中获取链接中的视频唯一标识符videoIndex并存储到AppStorage中,并存储GesturesShare_isShareLink为true,表示当前应用通过分享系统启动,以便于跳转到隔空传送链接分享页面播放视频。
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
let videoIndex = this.getVideoIndex(want);
if (videoIndex !== '') {
AppStorage.setOrCreate('GesturesShare_shareVideoIndex', Number.parseInt(videoIndex));
AppStorage.setOrCreate('GesturesShare_isShareLink', true);
}
}getVideoIndex()通过want参数获取分享链接中的参数。
private getVideoIndex(want: Want): string {
try {
let uri = want?.uri;
let videoIndex: string = '';
// Parse the parameters to obtain the app linking
if (uri) {
let urlObject = url.URL.parseURL(want?.uri);
videoIndex = urlObject.params.get('videoIndex') as string;
hilog.info(0x0000, 'GesturesShare', `getAid aid:${videoIndex}`);
}
return videoIndex;
} catch (err) {
let error = err as BusinessError;
hilog.error(0x0000, 'GesturesShare', `Failed to getVideoIndex, error: ${error.code} ${error.message}`);
}
return '';
}在应用首页判断GesturesShare_isShareLink是否为true,若为true则跳转至隔空传送链接分享页面。
onPageShow(): void {
if (this.isShareLink) {
this.pageInfos.pushPathByName('LinkSharePage', null, true);
}
}分享服务支持链接分享和文件分享两种场景,但不支持两者混合分享。在混合分享时,数据可能会在分享过程中丢失或提示无效的数据类型。详情可参考分享数据类型不支持。