鸿蒙|能力特性-应用间跳转

15 阅读15分钟

概览:本文介绍了应用间跳转的基本概念和应用场景,涵盖拉起指定应用和指定类型应用两种方式。通过App Linking和Deep Linking实现前者,通过startAbilityByType或mailto实现后者,支持导航、邮件、金融、航班等系统级应用的调用,灵活实现跨应用交互与系统功能集成。

什么是应用间跳转?

应用跳转是指从一个应用跳转至另外一个应用,传递相应的数据、执行特定的功能。通过应用跳转可以满足用户更为真实丰富的场景诉求、提升交互体验的便捷性和流畅性。

应用跳转类型

1、拉起指定应用(目标明确)
拉起方应用明确指定跳转的目标应用,来实现应用跳转。指向性跳转可以分为指定应用链接、指定Ability两种方式。
指定应用链接(推荐):使用 openLink 或 startAbility 接口,通过链接跳转至目标页面。 
指定Ability(不推荐):显式Want,从 API 12 开始不再推荐使用。

2、拉起指定类型的应用(用户选择)
指定应用类型(如“地图类”)系统展示匹配应用列表,用户自主选择打开。

3、拉起系统应用

1、拉起指定应用(目标明确)

应用链接两种类型对比:

类型App Linking(推荐)Deep Linking
实现方案目标应用需要在 module.json5 中声明应用链接;同时需要向系统注册域名并通过域名认证。目标应用需要在 module.json5 中声明应用链接。
链接格式scheme 必须为 https。例如:www.example.com/product/123scheme 可以自定义。通常不为 https、http、file,否则会拉起默认的系统浏览器。
例如:myapp://product/123
是否可用于分享或直接在网页中访问可以不可以,需在代码中调用。
是否可以直接拉起目标应用可以可以,但不推荐使用,存在被仿冒风险。

两种应用链接跳转结果对比:

应用链接类型App Linking(推荐)Deep Linking
appLinkingOnly 为 false目标应用已安装 → 直接跳转打开目标应用。
未安装 → 跳转默认浏览器打开网页。
目标应用已安装 → 跳转目标应用(如果有多个符合条件的应用时,展示应用选择弹框)
目标应用未安装 → 返回失败,系统不跳转,由应用自行处理;当前会展示 “链接无法打开” 弹框。
appLinkingOnly 为 true目标应用已安装 → 直接跳转打开目标应用。
未安装 → 返回失败,系统不跳转由应用自行处理。
目标应用已安装 → 返回失败,系统不跳转,由应用自行处理。
目标应用未安装 → 返回失败,系统不跳转,由应用自行处理。

appLinkingOnly:表示是否必须以AppLinking的方式启动UIAbility。

使用App Linking实现应用间跳转

使用App Linking进行跳转时,系统会根据接口传入的uri信息(HTTPS链接)将用户引导至目标应用中的特定内容,无论应用是否已安装,用户都可以访问到链接对应的内容,跳转体验相比Deep Linking方式更加顺畅。

实现原理:
1、App Linking在Deep Linking基础上增加了域名校验环节,通过域名校验,可帮助用户消除歧义,识别合法归属于域名的应用,使链接更加安全可靠。
2、App Linking要求对于同一HTTPS网址,有应用和网页两种内容的呈现方式。当应用安装时则优先打开应用去呈现内容;当应用未安装时,则打开浏览器呈现Web版的内容。

拉起方应用跳转实现:

1、通过openLink接口拉起 拉起方应用通过UIAbilityContext.openLink()接口,拉起目标应用,配置appLinkingOnly。目标应用安装后,需要在AGC开通App Linking服务,具体开通步骤参考官方文档。 developer.huawei.com/consumer/cn…

2、通过系统浏览器或ArkWeb拉起: 当用户在系统浏览器或者集成ArkWeb的应用的网页上点击某个链接时,若有链接匹配的应用,系统则会 通过App Linking能力优先拉起目标应用,并在应用内展示相应的内容。

使用Deep Linking实现应用间跳转

Deep Linking基于隐式Want匹配机制中的uri匹配来查询、拉起目标应用。主要有以下两种拉起方式:

1、通过openLink接口拉起: 在openLink接口的link字段中传入目标应用的URL信息,options字段中的appLinkingOnly配置为false。

2、通过startAbility接口拉起: startAbility接口是将应用链接放入want中,通过调用隐式want匹配的方法触发应用跳转。

2、拉起指定类型的应用(用户选择)

拉起指定类型应用是指通过指定应用类型、而非某个具体的应用,来实现应用跳转。通常有以下几种方式:

1、通过startAbilityByType接口拉起垂类面板:调用startAbilityByType接口拉起对应的垂域面板(目前支持拉起导航、金融、邮件、航班、快递类应用面板),该面板将展示目标方接入的垂域应用,由用户选择打开指定应用以实现相应的垂类意图。

2、通过mailto方式跳转电子邮件应用:通过mailto电子邮件协议,可以创建指向电子邮件地址的超链接,方便用户通过网页或应用中的超链接直接跳转电子邮件应用。

3、通过startAbility接口打开文件:开发者可以通过调用startAbility接口,由系统从已安装的应用中寻找符合要求的应用,打开特定类型的文件。

调用方接口传入的type与wantParams.sceneType取值,与目标方在module.json5配置文件中声明的对应linkFeature应用映射关系如下:

支持的功能调用方(startAbilityByType 接口入参)目标方(配置文件 linkFeature 取值)
路线规划功能- type: navigation- wantParams.sceneType: 1RoutePlan
导航功能- type: navigation- wantParams.sceneType: 2Navigation
位置搜索功能- type: navigation- wantParams.sceneType: 3PlaceSearch
转账汇款功能- type: finance- wantParams.sceneType: 1Transfer
信用卡还款功能- type: finance- wantParams.sceneType: 2CreditCardRepayment
撰写邮件功能- type: mail- wantParams.sceneType: 1ComposeMail
按航班号查询航班功能- type: flight- wantParams.sceneType: 1QueryByFlightNo
按起降地查询航班功能- type: flight- wantParams.sceneType: 2QueryByLocation
快递查询功能- type: express- wantParams.sceneType: 1QueryExpress
拉起导航类应用(startAbilityByType)

developer.huawei.com/consumer/cn…

接口type字段为navigation,支持路线规划、导航、位置搜索三种意图场景,以导航场景为例介绍 wantParam参数如下:

参数名类型必填说明
sceneTypenumber意图场景,表明本次请求对应的操作意图。导航场景填2。
destinationNamestring终点名称。
destinationLatitudenumber终点纬度。
destinationLongitudenumber终点经度。
destinationPoiIdsRecord<number, string>终点POI ID列表,当前仅支持传入花瓣地图、高德地图、百度地图的POI ID。

拉起方开发步骤:导入相关模块。构造接口参数并调用startAbilityByType接口。终点POI ID列表(destinationPoiIds)和起点POI ID列表(originPoiIds)需开发者自行从各地图系统中获取,并按照对应关系传参。

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let wantParam: Record<string, Object> = {
  'sceneType': 1,
  'destinationLatitude': 32.060844,
  'destinationLongitude': 118.78315,
  'destinationName': 'xx市xx路xx号',
  'destinationPoiIds': {
    1: '1001', // key为1代表花瓣地图,value需为花瓣地图POI
    2: '2002', // key为2代表高德地图,value需为高德地图POI
    3: '3003'  // key为3代表百度地图,value需为百度地图POI
  } as Record<number, string>,
  'originName': 'xx市xx公园',
  'originLatitude': 31.060844,
  'originLongitude': 120.78315,
  'originPoiIds': {
    1: '1101', // key为1代表花瓣地图,value需为花瓣地图POI
    2: '2202', // key为2代表高德地图,value需为高德地图POI
    3: '3303'  // key为3代表百度地图,value需为百度地图POI
  } as Record<number, string>,
  'vehicleType': 0
};
let abilityStartCallback: common.AbilityStartCallback = {
  onError: (code: number, name: string, message: string) => {
    console.error(`onError code ${code} name: ${name} message: ${message}`);
  },
  onResult: (result) => {
    console.info(`onResult result: ${JSON.stringify(result)}`);
  }
}

context.startAbilityByType("navigation", wantParam, abilityStartCallback,
  (err) => {
    if (err) {
      console.error(`startAbilityByType fail, err: ${JSON.stringify(err)}`);
    } else {
      console.info(`success`);
    }
  });
拉起邮件类应用(startAbilityByType)

developer.huawei.com/consumer/cn…

接口中type字段为mail,对应的wantParam参数:

参数名类型必填说明
emailstring[ ]收件人邮箱地址(支持多个且以逗号分隔)。
ccstring[ ]抄送人邮箱地址(支持多个且以逗号分隔)。
bccstring[ ]密送人邮箱地址(支持多个且以逗号分隔)。
subjectstring邮件主题。
bodystring邮件内容。
ability.params.streamstring[ ]邮件附件(附件的uri地址列表)。
ability.want.params.uriPermissionFlagwantConstant.Flags给邮件附件赋予至少读权限。邮件附件参数存在时,该参数也必须要传。
sceneTypenumber意图场景,表明本次请求对应的操作意图。1:发邮件。默认为1。

拉起方开发步骤:导入相关模块。构造接口参数并调用startAbilityByType接口。

import { common, wantConstant } from '@kit.AbilityKit';

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;

let wantParam: Record<string, Object> = {
  'sceneType': 1,
  'email': [encodeURI('xxx@example.com'), encodeURI('xxx@example.com')], // 收件人邮箱地址,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
  'cc': [encodeURI('xxx@example.com'), encodeURI('xxx@example.com')], // 抄送人邮箱地址,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
  'bcc': [encodeURI('xxx@example.com'), encodeURI('xxx@example.com')], // 密送人邮箱地址,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
  'subject': encodeURI('邮件主题'), // 邮件主题,对内容使用encodeURI()方法进行url编码
  'body': encodeURI('邮件正文'), // 邮件正文,对内容使用encodeURI()方法进行url编码
  'ability.params.stream': [encodeURI('附件uri1'), encodeURI('附件uri2')], // 附件uri,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
  'ability.want.params.uriPermissionFlag': wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION
};

let abilityStartCallback: common.AbilityStartCallback = {
  onError: (code: number, name: string, message: string) => {
    console.error(`onError code ${code} name: ${name} message: ${message}`);
  },
  onResult: (result) => {
    console.info(`onResult result: ${JSON.stringify(result)}`);
  }
}

context.startAbilityByType("mail", wantParam, abilityStartCallback,
  (err) => {
    if (err) {
      console.error(`startAbilityByType fail, err: ${JSON.stringify(err)}`);
    } else {
      console.info(`success`);
    }
  });
拉起邮件类应用(mailto方式)

developer.huawei.com/consumer/cn…

mailto协议标准格式如下:

mailto:地址?参数=值
mailto:someone@example.com?key1=value1&key2=value2

mailto::mailto scheme,必填。
someone@example.com:收件人地址,选填。如果存在多个地址,用英文逗号分隔。
?:邮件头声明开始符号。如果带邮件头参数,则必填。
key-value:邮件头参数,详细参数见下表。

邮件头参数表

邮件头含义数据类型是否必填
subject邮件主题string
body邮件正文string
cc抄送人,多个用逗号分隔string
bcc密送人,多个用逗号分隔string

拉起方开发步骤:把mailto字符串传入uri参数,开发时需要将邮箱地址替换为真实的邮箱,邮件内容根据需要进行配置。

let ctx = this.getUIContext().getHostContext() as common.UIAbilityContext;
ctx.startAbility({
  action: 'ohos.want.action.sendToData',
  uri: 'mailto:feedback@example.com?subject=App Feedback&body=Please describe your feedback here...'
})
拉起金融类应用实现支付场景(startAbilityByType)

developer.huawei.com/consumer/cn…

接口中type字段为finance,对应的wantParam参数:

参数名类型必填说明
sceneTypenumber意图场景,表明本次请求对应的操作意图。1:转账汇款 2:信用卡还款。默认为1
bankCardNostring银行卡卡号

拉起方开发步骤:导入相关模块。构造接口参数并调用startAbilityByType接口。

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let wantParam: Record<string, Object> = {
  'sceneType': 1,
  "bankCardNo": '123456789'
};
拉起航班类应用(startAbilityByType)

developer.huawei.com/consumer/cn…

接口中type字段为flight,支持按航班号查询、按起降地查询两种意图场景,wantParam参数如下:

按航班号查询场景

参数名类型必填说明
sceneTypenumber意图场景,表明本次请求对应的操作意图。默认为1,按航班号查询场景填1或不填。
flightNostring航班号,航司二位代码+数字。
departureDatestring航班出发时间:YYYY-MM-DD。

按起降地查询场景

参数名类型必填说明
sceneTypenumber意图场景,表明本次请求对应的操作意图。按起降地查询场景填2。
originLocationstring出发地。
destinationLocationstring目的地。
departureDatestring航班出发时间:YYYY-MM-DD。

拉起方开发步骤:导入相关模块。构造接口参数并调用startAbilityByType接口。

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let wantParam: Record<string, Object> = {
  'sceneType': 1,
  'flightNo': 'ZH1509',
  'departureDate': '2024-10-01'
};
let abilityStartCallback: common.AbilityStartCallback = {
  onError: (code: number, name: string, message: string) => {
    console.error(`onError code ${code} name: ${name} message: ${message}`);
  },
  onResult: (result) => {
    console.info(`onResult result: ${JSON.stringify(result)}`);
  }
}

context.startAbilityByType("flight", wantParam, abilityStartCallback,
  (err) => {
    if (err) {
      console.error(`startAbilityByType fail, err: ${JSON.stringify(err)}`);
    } else {
      console.info(`success`);
    }
  });
拉起快递类应用(startAbilityByType)

developer.huawei.com/consumer/cn…

接口中type字段为express,支持查询快递意图,对应的wantParam参数如下:

参数名类型必填说明
sceneTypenumber意图场景,表明本次请求对应的操作意图。默认为1,查询快递场景填1或不填。
expressNostring快递单号。

拉起方开发步骤:导入相关模块。构造接口参数并调用startAbilityByType接口。

let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let wantParam: Record<string, Object> = {
  'sceneType': 1,
  'expressNo': 'SF123456'
};
let abilityStartCallback: common.AbilityStartCallback = {
  onError: (code: number, name: string, message: string) => {
    console.error(`onError code ${code} name: ${name} message: ${message}`);
  },
  onResult: (result) => {
    console.info(`onResult result: ${JSON.stringify(result)}`);
  }
}

context.startAbilityByType("express", wantParam, abilityStartCallback,
  (err) => {
    if (err) {
      console.error(`startAbilityByType fail, err: ${JSON.stringify(err)}`);
    } else {
      console.info(`success`);
    }
  });
拉起图片编辑类应用(startAbilityByType)

developer.huawei.com/consumer/cn…

接口中type字段为imageEdit ,接口说明如下:

接口名描述
onStartContentEditing(uri: string, want:Want, session:UIExtensionContentSession):void可以执行读取原始图片、加载页面等操作。
saveEditedContentWithImage(pixeMap: image.PixelMap, option:image.PackingOption): Promise < AbilityResult >传入编辑过的图片的PixelMap对象并保存。
拉起文件处理类应用(startAbility)

developer.huawei.com/consumer/cn…

startAbility请求中want相关参数说明:

参数名称类型是否必填说明
uristring表示待打开文件的URI路径,一般配合type使用。uri格式为:file://bundleName/path- file:文件URI的标志。- bundleName:该文件资源的属主。- path:文件资源在应用沙箱中的路径。
typestring表示打开文件的类型,推荐使用UTD类型,比如:'general.plain-text'、'general.image'。目前也可以兼容使用MIME type类型,如:'text/xml'、'image/'等。说明:1. type为可选字段,如果不传type,系统会尝试根据uri后缀名判断文件类型进行匹配;如果传入type,必须确保与uri的文件类型一致,否则会导致无法匹配到合适的应用。文件后缀与文件类型的映射关系参见Uniform Type Descriptor(UTD)预置列表。2. 不支持传/*。
parametersRecord<string, Object>表示由系统定义,由开发者按需赋值的自定义参数,文件打开场景请参考表2。
flagsnumber表示处理方式,文件打开场景请参考表3。
actionstring表示要执行的通用操作。文件打开场景固定值:'ohos.want.action.viewData' ,表示查看数据的操作。

3、拉起系统应用

developer.huawei.com/consumer/cn…

➢ 权限设置:通过requestPermissionsFromUser()接口拉起权限申请弹框时,如果用户拒绝授权,将 无法使用该接口再次拉起弹框,需要调用requestPermissionOnSetting接口拉起权限设置弹窗。

➢ 通知管理: 通过requestEnableNotification()接口拉起通知授权弹框时,如果用户拒绝授权,将无 法使用该接口再次拉起弹框,需要调用openNotificationSettings()接口,支持拉起通知管理弹窗。

➢ 网络管理:通过openNetworkManagerSettings()接口拉起WLAN设置弹窗。

➢ 应用市场:通过loadProduct()接口、App Linking等多种方式拉起应用详情页。

➢ 钱包:通过requestPayment()接口实现单次支付、支付并签约。

➢ 电话:通过makeCall()接口跳转到拨号界面,并显示待拨出的号码。

➢ 日历:通过addEvent接口创建日程。

➢ 联系人:通过联系人Picker(Contacts Picker),拉起联系人应用,读取联系人数据。

➢ 地图:通过地图Picke拉起地点详情展示、地点选取、区划选择。

➢ 拍照录像:通过相机Picker实现拍照、录像。

➢ 扫码 :通过扫码Picker调用相机,实现默认界面扫码。

➢ 卡证识别:通过卡证识别Picker调用相机,识别各类证件并提取卡证信息。

➢ 文档扫描 :通过文档扫描Picker调用相机,拍摄文档并转化为高清扫描件。

➢ 文件管理:文件Picker(DocumentViewPicker)用于访问、保存公共目录中文档类文件;音频 Picker(AudioViewPicker):用于访问、保存公共目录的音频文件。

➢ 图库(媒体库):通过照片Picker访问、保存公共目录的图片或视频文件。

案例:使用openLink接口和startAbility两种方式实现应用跳转

新建项目MyApplication1、MyApplication2

Index.ets (MyApplication1)

import { common, OpenLinkOptions, Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { cameraPicker } from '@kit.CameraKit';
import { camera } from '@kit.CameraKit';

const TAG: string = '[UIAbilityComponentsOpenLink]';
const DOMAIN_NUMBER: number = 0xFF00;

@Entry
@Component
struct Index {

  build() {
    Column({ space: 20 }) {

      Button('openLink接口实现应用跳转', { type: ButtonType.Capsule, stateEffect: true })
        .width('87%')
        .height('5%')
        .margin({ top: '20vp' })
        .onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          let link: string = "link://www.example.com";
          let openLinkOptions: OpenLinkOptions = {
            appLinkingOnly: false
          };
          try {
            context.openLink(link, openLinkOptions)
              .then(() => {
                hilog.info(DOMAIN_NUMBER, TAG, 'openLink success.');
              }).catch((err: BusinessError) => {
              hilog.error(DOMAIN_NUMBER, TAG, `openLink failed. Code is ${err.code}, message is ${err.message}`);
            });
          } catch (paramError) {
            hilog.error(DOMAIN_NUMBER, TAG,
              `Failed to start link. Code is ${paramError.code}, message is ${paramError.message}`);
          }
        })

      Button('startAbility接口实现应用跳转', { type: ButtonType.Capsule, stateEffect: true })
        .width('87%')
        .height('5%')
        .margin({ top: '20vp' })
        .onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          let want: Want = {
            uri: "link://www.example.com"
          };
          try {
            context.startAbility(want).then(() => {
              hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
            }).catch((err: BusinessError) => {
              hilog.error(DOMAIN_NUMBER, TAG, `startAbility failed. Code is ${err.code}, message is ${err.message}`);
            });
          } catch (paramError) {
            hilog.error(DOMAIN_NUMBER, TAG,
              `Failed to start ability. Code is ${paramError.code}, message is ${paramError.message}`);
          }
        })

    }.width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}

module.json5 (MyApplication2)

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

案例:拉起指定类型的应用(导航类)

Index.ets (MyApplication1)

// 路线规划功能 'sceneType': 1, 导航功能 'sceneType': 2, 位置搜索功能 'sceneType': 3,

Button('拉起导航类应用(路线规划页面)', { type: ButtonType.Capsule, stateEffect: true })
  .width('87%')
  .height('5%')
  .margin({ top: '20vp' })
  .onClick(() => {
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    let wantParam: Record<string, Object> = {
      'sceneType': 1,
      'destinationLatitude': 32.060844,
      'destinationLongitude': 118.78315,
      'destinationName': 'xx市xx路xx号',
      'destinationPoiIds': {
        1: '1001', // key为1代表花瓣地图,value需为花瓣地图POI
        2: '2002', // key为2代表高德地图,value需为高德地图POI
        3: '3003'  // key为3代表百度地图,value需为百度地图POI
      } as Record<number, string>,
      'originName': 'xx市xx公园',
      'originLatitude': 31.060844,
      'originLongitude': 120.78315,
      'originPoiIds': {
        1: '1101', // key为1代表花瓣地图,value需为花瓣地图POI
        2: '2202', // key为2代表高德地图,value需为高德地图POI
        3: '3303'  // key为3代表百度地图,value需为百度地图POI
      } as Record<number, string>,
      'vehicleType': 0
    };
    let abilityStartCallback: common.AbilityStartCallback = {
      onError: (code: number, name: string, message: string) => {
        console.error(`onError code ${code} name: ${name} message: ${message}`);
      },
      onResult: (result) => {
        console.info(`onResult result: ${JSON.stringify(result)}`);
      }
    }
    context.startAbilityByType("navigation", wantParam, abilityStartCallback,
      (err) => {
        if (err) {
          console.error(`startAbilityByType fail, err: ${JSON.stringify(err)}`);
        } else {
          console.info(`success`);
        }
      });
  });

新建项目MyApplication3,并创建三个页面RoutePlanPage、NavigationPage、PlaceSearchPage

@Entry
@Component
struct RoutePlanPage {
  build() {
    Row() {
      Column() {
        Text('这是路径规划页面')
          .fontSize($r('app.float.page_text_font_size'))
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}
@Entry
@Component
struct NavigationPage {
  build() {
    Row() {
      Column() {
        Text('这是导航页面')
          .fontSize($r('app.float.page_text_font_size'))
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}
@Entry
@Component
struct PlaceSearchPage {
  build() {
    Row() {
      Column() {
        Text('这是置搜索页面')
          .fontSize($r('app.float.page_text_font_size'))
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

resources/base/profile/main_pages.json (MyApplication3)

{
  "src": [
    "pages/Index",
    "pages/RoutePlanPage",
    "pages/PlaceSearchPage",
    "pages/NavigationPage"
  ]
}

module.json5 (MyApplication3)

"skills": [
  {
    "entities": [
      "entity.system.home"
    ],
    "actions": [
      "ohos.want.action.home"
    ]
  },
  {
    "actions": [
      // actions不能为空,actions为空会造成目标方匹配失败。
      "ohos.want.action.viewData"
    ]
  }, // 新增一个skill对象,用于跳转场景。如果存在多个跳转场景,需配置多个skill对象。
  // 导航场景
  {
    "uris": [
      {
        "scheme": "maps", // 这里仅示意,应用需确保这里声明的uri能被外部正常拉起
        "host": "navigation",
        "path": "",
        "linkFeature": "Navigation" // 声明应用支持导航功能
      },
      {
        "scheme": "maps", // 这里仅示意,应用需确保这里声明的uri能被外部正常拉起
        "host": "routePlan",
        "path": "",
        "linkFeature": "RoutePlan" // 声明应用支持路线规划功能
      },
      {
        "scheme": "maps", // 这里仅示意,应用需确保这里声明的uri能被外部正常拉起
        "host": "search",
        "path": "",
        "linkFeature": "PlaceSearch" // 声明应用支持位置搜索功能
      }
    ]
  }
]

EntryAbility.ets (MyApplication3)

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  windowStage: window.WindowStage | null = null;
  uri?: string;
  destinationLatitude?: number;
  destinationLongitude?: number;
  destinationName?: string;
  originName?: string;
  originLatitude?: number;
  originLongitude?: number;
  vehicleType?: number;
  destinationPoiId?: string;
  originPoiId?: string;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onCreate(want, launchParam);
    this.parseWant(want);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onNewWant(want, launchParam);
    this.parseWant(want);
    if (!this.windowStage) {
      this.context.terminateSelf();
      return;
    }
    this.loadPage(this.windowStage);
  }

  private parseWant(want: Want): void {
    this.uri = want.uri as string | undefined;
    this.destinationLatitude = want.parameters?.destinationLatitude as number | undefined;
    this.destinationLongitude = want.parameters?.destinationLongitude as number | undefined;
    this.destinationName = want.parameters?.destinationName as string | undefined;
    this.originName = want.parameters?.originName as string | undefined;
    this.originLatitude = want.parameters?.originLatitude as number | undefined;
    this.originLongitude = want.parameters?.originLongitude as number | undefined;
    this.vehicleType = want.parameters?.vehicleType as number | undefined;
    this.destinationPoiId = want.parameters?.destinationPoiId as string | undefined;
    this.originPoiId = want.parameters?.originPoiId as string | undefined;
  }
  private loadPage(windowStage: window.WindowStage): void {
    if (this.uri === 'maps://navigation') {
      // 构建导航场景参数
      const storage: LocalStorage = new LocalStorage({
        "destinationLatitude": this.destinationLatitude,
        "destinationLongitude": this.destinationLongitude,
        "destinationPoiId": this.destinationPoiId
      } as Record<string, Object>);
      // 拉起导航页面
      windowStage.loadContent('pages/NavigationPage', storage)
    } else if (this.uri === 'maps://routePlan') {
      // 构建路径规划场景参数
      const storage: LocalStorage = new LocalStorage({
        "destinationLatitude": this.destinationLatitude,
        "destinationLongitude": this.destinationLongitude,
        "destinationName": this.destinationName,
        "originName": this.originName,
        "originLatitude": this.originLatitude,
        "originLongitude": this.originLongitude,
        "vehicleType": this.vehicleType,
        "destinationPoiId": this.destinationPoiId,
        "originPoiId": this.originPoiId
      } as Record<string, Object>);
      // 拉起路径规划页面
      windowStage.loadContent('pages/RoutePlanPage', storage)
    }  else if (this.uri === 'maps://search') {
      // 构建位置搜索场景参数
      const storage: LocalStorage = new LocalStorage({
        "destinationName": this.destinationName
      } as Record<string, Object>);
      // 拉起位置搜索页面
      windowStage.loadContent('pages/PlaceSearchPage', storage)
    } else {
      // 默认拉起首页
      windowStage.loadContent('pages/Index', (err) => {
        if (err.code) {
          return;
        }
      });
    }
  }
  onDestroy(): void {
  }
  onWindowStageCreate(windowStage: window.WindowStage): void {
    this.windowStage = windowStage;
    this.loadPage(this.windowStage);
  }
  onWindowStageDestroy(): void {
  }
  onForeground(): void {
  }
  onBackground(): void {
  }
}

案例:演示拉起系统中的相机应用

Index.ets (MyApplication1)

@State uri: Resource | string | undefined = undefined;
private cameraPosition: Array<camera.CameraPosition> = [
  camera.CameraPosition.CAMERA_POSITION_UNSPECIFIED, camera.CameraPosition.CAMERA_POSITION_BACK,
  camera.CameraPosition.CAMERA_POSITION_FRONT
];
private mediaType: Array<cameraPicker.PickerMediaType> = [
  cameraPicker.PickerMediaType.PHOTO, cameraPicker.PickerMediaType.VIDEO
];


Button('拉起系统应用中的相机')
  .width('87%')
  .height('5%')
  .margin({ top: '20vp' })
  .onClick(async () => {
    try {
      // 配置以启动后置摄像头
      let pickerProfile: cameraPicker.PickerProfile = { cameraPosition: this.cameraPosition[1] };
      // 配置为拍照模式
      let pickerResult: cameraPicker.PickerResult = await cameraPicker.pick(this.getUIContext().getHostContext(),
        [this.mediaType[0]], pickerProfile);
      // 获取视频URI
      this.uri = pickerResult.resultUri;
      hilog.info(0x0000, ' ', "the pick pickerResult is:" + JSON.stringify(pickerResult));
    } catch (error) {
      let err = error as BusinessError;
      hilog.error(0x0000, '', `the pick call failed. error code: ${err.code}`);
    }
  })