以下内容均基于HarmonyOS NEXT版本
为什么需要应用间跳转
1.用户场景驱动
- 社交分享:图库App跳转微信发送图片
- 服务闭环:电商App跳转地图导航至收货地址
- 生态协同:浏览器中点击链接唤起银行App支付
2.系统级优势
- 沙箱隔离突破:安全可控的数据传递(基于URI权限管控)
- 垂类能力复用:避免重复开发(如直接调用高德地图导航)
- 多设备协同:手机与车机间的跨设备跳转(扩展FA模型能力)
跳转类型
指定应用跳转(精准拉起)
- 指定应用链接(推荐):通过openLink或startAbility接口来指定应用链接,拉起目标应用页面。
- 指定Ability(不推荐):通过startAbility接口指定具体的Ability(即显式Want方式),拉起目标应用页面。
- 方式:Deep Linking(自定义Scheme)或 App Linking(HTTPS+域名校验)
- Deep Linking:是一种通过链接跳转至应用特定页面的技术,其特点是支持开发者定义任意形式的scheme。由于缺乏域名校验机制,容易被其他应用所仿冒。
- App Linking:其限定了scheme必须为https,同时通过增加域名校验机制,可以从已匹配到的应用中筛选过滤出目标应用,消除应用查询和定位中产生的歧义,直达受信的目标应用。
- 场景:社交分享(跳转指定购物App)、广告引流(跳转推广页面)
使用canOpenLink判断应用是否可访问
extendtypescript
import { bundleManager } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
try {
let link = 'app1Scheme://test.example.com/home';
let canOpen = bundleManager.canOpenLink(link);
hilog.info(0x0000, 'testTag', 'canOpenLink successfully: %{public}s', JSON.stringify(canOpen));
} catch (err) {
let message = (err as BusinessError).message;
hilog.error(0x0000, 'testTag', 'canOpenLink failed: %{public}s', message);
}
指定类型跳转(垂类面板)
- 方式:startAbilityByType() + 垂类标识(如navigation/mail)
- 场景:导航路线规划、邮件编辑、图片处理(用户选择同类应用)
实现方案
案例1:App Linking(推荐)
- 优势:防劫持、支持网页跳转、系统自动处理未安装场景(跳转浏览器)
- 域名归属验证:需在AppGallery Connect后台配置数字资产链接
- 数据防篡改:URL参数自动签名(SDK支持)
- 创建流程
extendtypescript
// 目标方配置(module.json5)
"uris": [{
"scheme": "https",
"host": "www.example.com",
"pathStartWith": "transfer" // 路径前缀匹配
"domainVerify": true // 开启域名认证
}]
// 拉起方代码
import { common } from '@kit.AbilityKit';
try {
await context.openLink("https://pay.demo.com/transfer?amt=100", {
appLinkingOnly: true, // 强制校验域名,表示是否必须以App Linking的方式启动UIAbility,默认为false。
parameters: { token: "xxx" } // 传递参数,敏感数据建议加密
});
} catch (error) {
// 处理未安装场景:引导下载或网页版
}
案例2:Deep Linking(兼容旧版)
- 限制:需处理多应用冲突弹窗;非HTTPS链接无法直接网页访问
extendtypescript
// 目标方配置(module.json5)
"uris": [{
"scheme": "myapp", // 自定义协议
"host": "page"
}]
// 拉起方代码
// 处理多应用冲突
const want: Want = {
uri: "demo://product/detail?id=123",
// 增加entity过滤避免误匹配
entities: ["entity.system.browsable"]
};
context.startAbility(want).catch(() => {
// 弹窗提示用户"未找到处理程序"
});
案例3:垂类面板跳转(通用意图)
- 支持垂类:导航(RoutePlan/Navigation)、邮件(ComposeMail)、金融(Transfer)、图片编辑(PhotoEditor)
extendtypescript
// 场景1:导航路线规划
context.startAbilityByType("navigation", {
sceneType: 1, // 路线规划
destination: {
name: "北京首都机场",
lat: 39.989,
lon: 116.481
}
});
// 场景2:图片编辑
context.startAbilityByType("photoEditor", {
sourceUri: "internal://cache/photo.jpg",
editOptions: { cropRatio: "16:9" } // 扩展参数
});
//场景3 拉起邮件类应用(mailto方式)
let ctx = getContext(this) as common.UIAbilityContext;
ctx.startAbility({
action: 'ohos.want.action.sendToData',
uri: 'mailto:[email protected]?subject=App Feedback&body=Please describe your feedback here...'
})
//场景4:拉起文件处理类应用(startAbility)
// 获取文件沙箱路径
let filePath = this.context.filesDir + '/test.txt';
// 将沙箱路径转换为uri
let uri = fileUri.getUriFromPath(filePath);
// 构造请求数据
let want: Want = {
action: 'ohos.want.action.viewData', // 表示查看数据的操作,文件打开场景固定为此值
uri: uri,
type: 'general.plain-text', // 表示待打开文件的类型
// 配置被分享文件的读写权限,例如对文件打开应用进行读写授权
flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION
};
// 调用接口启动
this.context.startAbility(want)
.then(() => {
console.info('Succeed to invoke startAbility.');
})
.catch((err: BusinessError) => {
console.error(`Failed to invoke startAbility, code: ${err.code}, message: ${err.message}`);
});
//开发者可注册新的垂类类型(需向华为申请审核),例如:
// 新建ExtAbility
"type": "custom_editor",
"metadata": [{
"name": "custom_editor",
"value": "VideoEditor" // 新垂类标识
}]
案例4. 隐式Want跳转(通用兜底)
extendtypescript
const want: Want = {
// 不指定具体组件
action: "ohos.want.action.EDIT_DATA",
uri: "file://docs/report.docx", // 根据文件类型匹配
type: "application/vnd.ms-word" // MIME类型辅助匹配
};
案例5. 使用Web组件实现应用跳转
- Web组件需要跳转DeepLink链接应用时,可通过拦截回调onLoadIntercept中对定义的事件进行处理,实现应用跳转。
extendtypescript
// index.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({ src: $rawfile('index.html'), controller: this.controller })
.onLoadIntercept((event) => {
const url: string = event.data.getRequestUrl();
if (url === 'link://www.example.com') {
(getContext() as common.UIAbilityContext).openLink(url)
.then(() => {
console.log('openLink success');
}).catch((err: BusinessError) => {
console.error('openLink failed, err:' + JSON.stringify(err));
});
return true;
}
// 返回true表示阻止此次加载,否则允许此次加载
return false;
})
}
}
}