鸿蒙判断应用是否支持某能力

背景

在鸿蒙系统跨应用能力调用场景中,需解决以下核心问题:被调用方应用的版本是否满足当前所需功能。例如SDK判断抖音是否支持投稿、授权才真正进行拉起抖音,不然会出现拉起抖音后没有任何反应或者能力缺失。因此目标为实现在SDK侧能判断应用是否支持某能力。

方案对比(太长不看版)

方案 实现 是否可用 是否启动对方进程 优点 缺点
metadata 字段读取 应用 A 在 module.json5 声明能力及版本号,SDK 侧用 bundleManager 读取 不会启动 读取轻量,性能极高,不依赖进程存活;可以随意自定义字段 三方应用只能查自身包信息,SDK 无法读取其他应用信息
DataShareExtensionAbility 应用 A 侧声明并注册扩展能力,SDK 侧创建 DataShareHelper 查询 会启动对方的 DataShareExtensionAbility 进程(但不拉 UI) 跨应用通用,数据可动态更新。如果能力有问题在App侧可以通过settings修改值来避免拉起却不可用的情况。 第一次调用耗时(冷启动几十~几百 ms);IPC 大数据效率低;目前仅对系统应用开放,暂不提供相关内容与指导
使用 canOpenLink 判断应用是否可访问 调用方在 module.json5 配置 querySchemes 声明 URL scheme,调用 canOpenLink 接口;目标方配置 uris 属性 不会启动 读取轻量,性能极高,不依赖进程存活 1. 调用方的 querySchemes 最多配置 50 个 URL scheme 2. 每次新增版本号需要在被调用侧新增 schema 的 host。

方案调研

metadata字段读取

实现

在应用A侧的module.json5 里声明能力及版本号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"module": {
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/MainAbility.ets",
"label": "AppA",
"metadata": [
{
"name": "bytedance_open_sdk_support",
"value": "1"
}
]
}
]
}
}

在SDK侧使用bundleManager 读取字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import bundleManager from '@ohos.bundle.bundleManager';

async function getAppACapabilityVersion(): Promise<string | null> {
try {
let bundleInfo = await bundleManager.getBundleInfo(
"com.example.appA", // App A 的包名
bundleManager.BundleFlag.GET_ABILITY_INFO_WITH_METADATA
);

for (let ability of bundleInfo.abilityInfos) {
if (ability.metadata) {
for (let meta of ability.metadata) {
if (meta.name === "bytedance_open_sdk_support") {
console.log(`App A 能力版本号: ${meta.value}`);
return meta.value;
}
}
}
}
} catch (err) {
console.error("读取能力失败: " + JSON.stringify(err));
}
return null;
}

问题

三方应用目前只能查询到自身应用包的信息,由于安全隐私,暂时不支持查询其他应用。因此在SDK侧无法通过bundleManager读取其他应用的信息。
https://developer.huawei.com/consumer/cn/doc/architecture-guides/insurance-v1_2-ts_34-0000002312854248

DataShareExtensionAbility

实现

在应用A侧声明 DataShareExtensionAbility在 ets/CustomDataShare.ets 里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import dataShare from '@ohos.data.dataShare';
import hilog from '@ohos.hilog';

const TAG: string = "CustomDataShare";

export default class CustomDataShare extends dataShare.DataShareExtensionAbility {
onCreate(want, callback) {
hilog.info(0x0000, TAG, "DataShareExtensionAbility onCreate");
}

// 处理查询请求
async query(uri, columns, predicates, callback) {
hilog.info(0x0000, TAG, `query called. uri=${uri}`);

// 模拟返回一个自定义字段
let resultSet = {
columnNames: ["customField"],
rowCount: 1,
rowIndex: 0,
getString: (index) => {
if (index === 0) return "HelloFromProviderApp";
return "";
},
goToFirstRow: () => true,
close: () => {}
};

callback.onResult(resultSet);
}
}

在 module.json5 注册扩展能力

1
2
3
4
5
6
7
8
9
10
{
"extensionAbilities": [
{
"name": "CustomDataShare",
"srcEntry": "./ets/CustomDataShare.ets",
"type": "dataShare",
"uri": "datashareproxy://com.example.provider"
}
]
}

SDK侧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
async queryCustomField() {
try {
let context = featureAbility.getContext();
let helper = await dataShare.createDataShareHelper(
context,
"datashareproxy://com.example.provider"
);

let resultSet = await helper.query(
{ uri: "datashareproxy://com.example.provider/customField" },
["customField"],
null
);

if (resultSet.goToFirstRow()) {
this.message = resultSet.getString(0);
}
resultSet.close();
} catch (err) {
hilog.error(0x0000, TAG, "query error: " + JSON.stringify(err));
}
}

问题

使用DataShareExtensionAbility实现数据共享:目前仅对系统应用开放,暂不具体展开提供相关内容和指导。
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-share-overview

实现

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/canopenlink

调用方(SDK):

  1. 在 entry 模块的 module.json5 文件中配置 querySchemes 属性,声明想要查询的 URL scheme。
1
2
3
4
5
6
7
8
{
"module": {
//...
"querySchemes": [
"app1Scheme"
]
}
}
  1. 调用 canOpenLink 接口:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);
    }

目标方:
在 module.json5 文件中配置 uris 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"module": {
//...
"abilities": [
{
//...
"skills": [
{
// actions不能为空,actions为空会造成目标方匹配失败
"actions": ["ohos.want.action.home"],
"uris": [
{
"scheme": "app1Scheme",
"host": "test.example.com",
"pathStartWith": "home"
}
]
}
]
}
]
}
}

问题

querySchemes 中最多允许配置 50 个 URL scheme。因此限制不能无限使用新的 schema 头,但整体方案是可行的。