鸿蒙专家 发表于 2026-8-12 14:00:06

鸿蒙ASCF NativeBridge实战:JS调ArkTS桥接配置与踩

ASCF 的能力边界在哪儿?这是用 ASCF 做复杂功能时绕不开的问题。ASCF 的 API 比 ArkTS 少很多——没有直接的电话拨打接口、没有完整的设备信息获取、没有文件系统操作。之前做一个需要拨号的功能,翻遍 ASCF 文档没找到 has.makePhoneCall,当时一脸懵。

后来发现 ASCF 提供了一个叫 NativeBridge 的桥接机制:JS 代码里调 has.callNative,ArkTS 侧的 EntryAbility 里处理,打完收工。但老实说,配置这个桥折腾了我一整天——要发邮件申请权限、要改 EntryAbility 代码、要处理同步异步的坑。这篇文章把整个流程和踩过的坑整理出来,给后面要用的人省点时间。

NativeBridge 是什么

NativeBridge 是 ASCF 和 ArkTS 之间的通信桥梁。ASCF 框架本身是用 JS/TS 写的,能调的系统能力有限。而 ArkTS 作为鸿蒙原生框架,能用的 API 多得多。NativeBridge 做的事情就是——JS 侧发请求,ArkTS 侧处理,结果返回给 JS。


JS (has.callNative) -> ASCF 框架 -> EntryAbility (onNativeCalled) -> ArkTS API -> 返回结果


一句话总结:ASCF 调不了的 ArkTS 接口,通过 NativeBridge 来调。

开发前准备:先发邮件申请权限

这个跟其他 API 不一样,不是开箱即用的,要先申请权限。发邮件到 atomicservice@huawei.com,标题按固定格式写:


[元服务申请使用框架间通信]-[元服务名称]-


邮件内容里提供元服务名称和 APP ID。APP ID 在华为开发者联盟的项目设置里可以查到。官方说 3 个工作日内反馈,我当时等了 2 天收到了开通确认邮件——期间一直以为是自己写错了邮箱。

这个权限申请流程对独立开发者来说有点麻烦,尤其是还在调试阶段的服务。建议在项目初期就申请下来,别等到功能开发到一半了才发邮件,干等两天。

API 一览:同步和异步两个入口

NativeBridge 提供了两个 API,一个同步一个异步。

has.callNative(同步)


// 传字符串
const res = has.callNative('getSystemInfo');
console.info('系统信息:', res);

// 传对象
const sum = has.callNative({
action: 'calculate',
args: ,
});
console.info('计算结果:', sum);


同步调用会阻塞当前线程直到结果返回。如果 ArkTS 侧处理比较耗时(比如读取大文件),JS 侧会卡住。耗时操作建议用异步版本。

同步调用返回的是字符串,如果 ArkTS 侧返回的是对象,记得 JSON.parse。

has.callNativeAsync(异步)


has.callNativeAsync({
params: 'getSystemInfo',
success: (res) => {
    console.info('获取系统信息成功:', res);
},
fail: (err) => {
    console.error('获取失败:', err);
},
complete: () => {
    console.info('调用结束');
},
});

has.callNativeAsync({
params: { action: 'makeCall', args: '13112341234' },
success: (res) => {
    console.info('拨号成功');
},
fail: (err) => {
    console.error('拨号失败:', err);
},
});


异步调用不会阻塞 JS 线程,结果通过回调返回。适合网络请求、拨号、文件读写等耗时操作。

ArkTS 侧:EntryAbility 里配处理

这是整个流程里最容易被忽略的部分——JS 侧调用了,ArkTS 侧没人接,结果就是没反应。需要在 EntryAbility.ts 里继承 AscfUIAbility,复写 onNativeCalled(同步)或 onNativeCalledAsync(异步)。

同步处理


import { AscfUIAbility } from '@atomicservice/ascfapi';
import { atomicService } from '@kit.ScenarioFusionKit';

const SYSTEM_INFO_STATE_ARRAY: atomicService.SystemInfoType[] = [
'brand', 'deviceModel', 'screenWidth', 'screenHeight',
'statusBarHeight', 'screenSafeArea', 'language', 'osFullName',
'sdkApiVersion', 'bluetoothEnabled', 'wifiEnabled', 'locationEnabled',
'deviceOrientation', 'theme', 'windowWidth', 'windowHeight'
];

export default class EntryAbility extends AscfUIAbility {
onNativeCalled(params: string | object): string {
    // 获取系统信息
    if (typeof params === 'string' && params === 'getSystemInfo') {
      const systemInfo = atomicService.getSystemInfoSync(SYSTEM_INFO_STATE_ARRAY);
      return JSON.stringify(systemInfo);
    }

    // 计算
    if (typeof params === 'object') {
      const paramObj = params as Record<string, Object>;
      if (paramObj.action === 'calculate' && paramObj.args) {
      const numbers = paramObj.args as number[];
      const sum = numbers.reduce((a, b) => a + b, 0);
      return sum.toString();
      }
    }
    return '';
}
}


注意返回值必须是 string。如果想返回对象,自己 JSON.stringify 一下,JS 侧再 JSON.parse。我一开始返回了一个对象没转字符串,ArkTS 报类型错误编译不过……排查了好一会儿。

异步处理


import { AscfUIAbility } from '@atomicservice/ascfapi';
import { atomicService } from '@kit.ScenarioFusionKit';
import { call } from '@kit.TelephonyKit';

export default class EntryAbility extends AscfUIAbility {
onNativeCalledAsync(params: string | object): Promise<object> {
    // 异步获取系统信息
    if (typeof params === 'string' && params === 'getSystemInfo') {
      return atomicService.getSystemInfo(SYSTEM_INFO_STATE_ARRAY);
    }

    // 拨打电话
    if (typeof params === 'object') {
      const paramObj = params as Record<string, Object>;
      if (paramObj.action === 'makeCall' && paramObj.args) {
      const phoneNumber = paramObj.args as string;
      return call.makeCall(phoneNumber).then(() => {
          return { success: true };
      });
      }
    }
    return Promise.reject(new Error('未知的异步操作'));
}
}


异步方法返回 Promise<object>,JS 侧通过 success/fail 回调拿到结果。

callNative 和 callNativeAsync 怎么选

简单判断:ArkTS 侧是同步 API 就用 callNative,是异步 API 就用 callNativeAsync。如果不确定 ArkTS 接口是同步还是异步,看文档——大部分系统 API 带 Sync 后缀的是同步(如 getSystemInfoSync),不带的是异步。

实用场景

获取完整设备信息

ASCF 的 has.getSystemInfo 返回的信息有限,通过 NativeBridge 可以拿到更多字段:


// JS 侧
const infoStr = has.callNative('getSystemInfo');
const info = JSON.parse(infoStr);
console.info('品牌:', info.brand);
console.info('安全区域:', JSON.stringify(info.screenSafeArea));


ArkTS 侧用 atomicService.getSystemInfoSync 能拿到 brand、screenSafeArea、bluetoothEnabled、wifiEnabled 等 ASCF 没有的信息。

拨打电话

ASCF 没有拨号 API。通过 NativeBridge 调 ArkTS 的 call.makeCall:


// JS 侧
has.callNativeAsync({
params: { action: 'makeCall', args: '13112341234' },
success: () => {
    console.info('拨号成功');
},
fail: (err) => {
    console.error('拨号失败:', err);
    has.showToast({ title: '拨号失败', icon: 'none' });
},
});


ArkTS 侧用 call.makeCall(phoneNumber) 实现。这个场景是 NativeBridge 最典型的应用——ASCF 做不了的事,交给 ArkTS。

读取设备电池信息

ASCF 没有电池信息相关的 API,通过桥接可以拿到:


// ArkTS 侧
import { batteryInfo } from '@kit.BatteryInfoKit';
onNativeCalled(params: string | object): string {
if (params === 'getBatteryInfo') {
    const info = {
      batteryLevel: batteryInfo.batterySOC,
      chargingStatus: batteryInfo.batteryHealthState,
      isCharging: batteryInfo.isCharging,
    };
    return JSON.stringify(info);
}
// ...
}



// JS 侧
const batteryStr = has.callNative('getBatteryInfo');
const battery = JSON.parse(batteryStr);
console.info('电量:', battery.batteryLevel + '%');
console.info('是否充电:', battery.isCharging);


拿到电量信息后可以做低电量提醒、省电模式等功能。

读写文件

ASCF 的文件 API 有限,通过 ArkTS 的 fileIo 可以操作任意文件路径:


// ArkTS 侧
import { fileIo } from '@kit.CoreFileKit';
onNativeCalledAsync(params: string | object): Promise<object> {
if (typeof params === 'object') {
    const p = params as Record<string, Object>;
    if (p.action === 'readFile' && p.path) {
      const filePath = p.path as string;
      try {
      const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);
      const buf = new ArrayBuffer(1024);
      fileIo.readSync(file.fd, buf);
      fileIo.closeSync(file);
      return Promise.resolve({ content: buf.toString() });
      } catch (e) {
      return Promise.reject(e);
      }
    }
}
return Promise.reject(new Error('未知操作'));
}


注意文件读写涉及沙箱路径和权限,不是所有目录都能访问。建议在沙箱内操作。

获取网络类型详情

ASCF 的 has.getNetworkType 只返回 wifi/4g/5g 等基本类型,ArkTS 的 network 接口能拿到更多详细信息:


// ArkTS 侧
import { network } from '@kit.NetworkKit';
onNativeCalledAsync(params: string | object): Promise<object> {
if (params === 'getNetworkDetail') {
    return network.getDefaultNet().then((netHandle) => {
      return network.getNetCapabilities(netHandle);
    });
}
return Promise.reject(new Error('未知操作'));
}


这在做网络诊断或者需要精确判断网络质量的功能时很有用。

自定义计算/数据处理

如果 JS 侧的性能不够,或者需要调用特定的 ArkTS 库做计算,可以传参过去算完再返回:


// JS 侧
const res = has.callNative({
action: 'calculate',
args: ,
});
console.info('求和结果:', res);


当然,简单的计算在 JS 侧做就行了,犯不着走一遍桥。这个更多是演示传参和返回的完整链路。

验证桥接是否生效

配置完了怎么知道桥有没有通?我一般通过三步验证。

第一步:ArkTS 侧写一个最简单的回显。在 EntryAbility 的 onNativeCalled 里加一个 ping 操作:


onNativeCalled(params: string | object): string {
if (params === 'ping') {
    return 'pong';
}
// 其他逻辑
}


第二步:JS 侧调一下:


const res = has.callNative('ping');
console.info('桥接测试结果:', res);
// 期望输出: pong


如果输出 pong 说明桥通了。如果一直没有输出或者报错,先确认权限申请是否通过、EntryAbility 是否正确继承 AscfUIAbility。

第三步:调一个真实的 ArkTS API。ping 通了之后,再试真正的 ArkTS API,比如获取设备信息。逐步确认问题出在桥本身还是业务逻辑上。

我当初测试的时候,ping 通了就以为全好了,结果调 getSystemInfo 还是没反应——因为忘了 JSON.stringify,ArkTS 侧编译报错了但日志被吞了,JS 侧什么也看不到。后来在 ArkTS 侧加了 try-catch 和 hilog 输出才定位到问题。

EntryAbility 文件在哪

第一次用 NativeBridge 的人可能找不到 EntryAbility 在哪——项目目录下一般是 entry/src/main/ets/entryability/EntryAbility.ts。ASCF 项目生成时默认的 EntryAbility 继承的是 UIAbility,需要手动改成 AscfUIAbility:


// 改之前
import { UIAbility } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility { ... }

// 改之后
import { AscfUIAbility } from '@atomicservice/ascfapi';
export default class EntryAbility extends AscfUIAbility { ... }


改了之后如果编译报错找不到 @atomicservice/ascfapi,检查一下 oh-package.json5 里有没有加这个依赖。没有的话手动加:


{
"dependencies": {
    "@atomicservice/ascfapi": "file:./path/to/ascfapi"
}
}


具体路径取决于项目结构,如果不确定可以问 IDE——在代码里写 import { AscfUIAbility } 时 IDE 会提示可导入的包路径。

JS 侧的错误处理

NativeBridge 调用出错时,JS 侧的表现因同步异步而异:

callNative(同步):ArkTS 侧抛异常会直接传到 JS 侧,可以用 try-catch 捕获。

callNativeAsync(异步):ArkTS 侧 Promise.reject 触发 JS 侧的 fail 回调。


// 同步调用异常捕获
try {
const res = has.callNative('someMethod');
console.info('结果:', res);
} catch (e) {
console.error('调用异常:', e);
has.showToast({ title: '调用失败', icon: 'none' });
}

// 异步调用异常捕获
has.callNativeAsync({
params: 'someAsyncMethod',
success: (res) => {
    console.info('成功:', res);
},
fail: (err) => {
    console.error('失败:', JSON.stringify(err));
    has.showToast({ title: '异步调用失败', icon: 'none' });
},
});


ArkTS 侧建议统一用 try-catch 包裹所有业务逻辑,确保异常能被正确抛到 JS 侧。如果 ArkTS 侧吃了异常,JS 侧什么都不知道,用户点了也没反应,排查起来很费劲。

踩过的坑合集

没申请权限直接调。没发邮件申请权限就调 callNative/callNativeAsync,调用会静默失败——没有错误提示,没有日志,就是没反应。检查的第一步就是确认权限有没有开通。我调了大半个小时以为是代码写错了,结果发现压根没申请权限。

EntryAbility 没继承 AscfUIAbility。如果 EntryAbility 继承的是默认的 UIAbility 而不是 AscfUIAbility,onNativeCalled/onNativeCalledAsync 不会被调用。JS 侧调了也没反应。

同步方法返回类型必须是 string。onNativeCalled 的返回值类型是 string,不能直接返回对象或数字。如果返回了非 string 类型,ArkTS 编译阶段会报错。正确做法是 JSON.stringify 后返回,JS 侧再 JSON.parse 解析。

异步方法必须返回 Promise。onNativeCalledAsync 的返回值类型是 Promise<object>,如果返回了 null 或者 undefined,JS 侧的 fail 回调会触发。未匹配到任何操作时应 return Promise.reject(new Error('未知的操作')),这样 JS 侧可以统一在 fail 里处理错误。

参数传递的格式统一。JS 侧和 ArkTS 侧约定好参数格式是关键。我一开始 JS 侧传 { action: 'makeCall', phone: '131xxxxxxxx' },ArkTS 侧读的是 args 字段,两边字段名对不上,怎么调都不通。后来统一了字段名就好了。建议把参数格式文档化,双方各留一份。

权限申请的 appid 搞错。发邮件时填的 APP ID 是项目的 APP ID,不是网传的包名或 bundleName。APP ID 在华为开发者联盟 -> 项目设置 -> 应用 -> APP ID 里查。填错了运营人员会驳回申请,白等两三天。我第一封邮件就填错了,等了 3 天收到驳回通知,改对了重新提交又等了 2 天,一来一回一周就过去了。申请之前一定要确认清楚。

写在最后

NativeBridge 是 ASCF 的一个能力扩展通道。它的使用流程是固定的:发邮件申请权限 -> ArkTS 侧 EntryAbility 配置处理 -> JS 侧调用。虽然配置流程比普通 API 麻烦,但好处是一旦配好了,ASCF 缺失的系统能力都可以通过这个桥接调用了。

对了,不要所有功能都走 NativeBridge。只有 ASCF 确实不支持的 ArkTS 特有能力才需要走桥。ASCF 本身就有的 API(如网络请求、本地缓存)直接在 JS 侧调就行,绕一圈反而多余。判断标准就一条——ASCF 文档里搜不到的,才上桥。

热心网友1 发表于 2026-8-12 14:10:00

Re: 鸿蒙ASCF NativeBridge实战:JS调ArkTS桥接配置与踩

看了楼主的实战分享,非常实用!NativeBridge这个机制确实是ASCF的补强,不然很多系统能力都调不到。尤其是那个权限申请流程,提醒得很到位,不然真等到开发到一半再等两天确实难受。同步和异步的坑也总结得清楚,遇到耗时操作异步是必须的。 想追问一下:ArkTS侧onNativeCalledAsync的写法跟同步的差别大吗?还有如果JS侧传的参数是对象,在ArkTS侧解析时有什么类型转换的坑?如果楼主有遇到的话希望能补充一下,感谢!

热心网友1 发表于 2026-8-12 14:10:00

Re: 鸿蒙ASCF NativeBridge实战:JS调ArkTS桥接配置与踩

收藏了,正好最近在做元服务,卡在ASCF能力边界这块。老哥这篇写得很实在,尤其是权限申请那段,要不是看到这个帖子,我估计也得等开发到一半才去发邮件,白白浪费时间。 有个小问题想请教下:同步调用的返回值如果是对象,必须得 JSON.parse 的话,那 ArkTS 侧返回的字符串是不是得按约定的格式来?比如某些接口返回的字段名是驼峰还是下划线,有没有业内约定俗成的规范?

热心网友1 发表于 2026-8-12 14:10:00

Re: 鸿蒙ASCF NativeBridge实战:JS调ArkTS桥接配置与踩

楼主这篇实战分享太及时了,正好最近在琢磨ASCF的边界问题。之前试过一些API确实比ArkTS少很多,拨号这种需求还真不知道怎么绕,看到NativeBridge的桥接思路一下就通了。 不过有几个细节想再确认一下:那个权限申请是不是必须等邮件回复才能用,还是说在调试阶段有开关可以先试?另外同步返回字符串那里,ArkTS侧如果返回的是复杂对象,是不是都得自己手动JSON.stringify?还有就是EntryAbility里如果既有同步又有异步需求,两个回调都要复写吗?希望楼主有空能再展开说说,感谢!
页: [1]
查看完整版本: 鸿蒙ASCF NativeBridge实战:JS调ArkTS桥接配置与踩