AbilityMeta 能力元信息:不只是能调用,还要能看懂
2026/6/25 16:03:20 网站建设 项目流程

AbilityMeta 能力元信息:不只是能调用,还要能看懂

这是「ASCF 架构升级」系列的第 4 篇,对应提交5825abb的另一半。
上一篇讲了为什么用NativeAbilityRegistry取代 if-else;这一篇专门讲那张「报到表」的另一列:AbilityMeta—— 能力的说明书。

一、先想一个问题:「action → handler」够不够

上一篇里的注册表是这样的:

register(action:string,handler:NativeAbilityHandler):void

一个名字、一个函数。这样能跑吗?能。H5 报getDeviceInfo,注册表查到对应函数、调用、返回结果。作为「能让 H5 调通」的最小集合,这一对足够了。

但 demo 跑起来之后,下面这些问题立刻冒出来:

  • 调试面板想列「目前底座支持哪些能力」 —— 没地方读
  • README 想给读者展示一张能力清单 —— 得人工抄
  • 想做权限校验:「这个 H5 想调相册,它配不配?」 —— 没有元信息可查
  • BridgeLog 想标注「这条调用属于 device 类、是 mock 实现」 —— 也无从查起
  • 想做能力开关:「上线先把支付这个能力关掉」 —— 不知道往哪儿挂状态

这些问题的共性是:光知道「能调」还不够,得知道「这是个什么东西」。

对应到产品话术,就是「能力得有说明书」。

二、AbilityMeta是什么

文件在entry/src/main/ets/bridge/AbilityMeta.ets

exportinterfaceAbilityMeta{action:string;namespace:string;description:string;permission:string;mock:boolean;enabled:boolean;}

可以理解为「能力的身份证」:以前注册表只记一个名字,现在每个能力都带一张证件,正反面写满字段。

逐字段拆解:

action—— H5 调用时报的名字,跟BridgeAction常量一一对应,比如'getDeviceInfo'

namespace—— 能力分类,例如device / runtime / ui / clipboard / location。等调试面板做出来,能力会按 namespace 分组显示;BridgeLog 里也会以device/getDeviceInfo这种格式展现,一眼能看出是哪一类。

description—— 人话能力说明,比如'获取设备信息'。给两类人看:调试面板上的开发者、自动生成 README 时的读者。

permission—— 权限标识,目前是字符串:

  • 'none'—— 不需要权限
  • 'clipboard.read' / 'clipboard.write'—— 剪贴板读 / 写
  • 'location.mock'—— 模拟定位

将来真要做权限校验,会有一张「能力 → 需要权限」的表,dispatch 时先查权限再决定调不调。现在先用字符串占位,把这条数据通道打通。

mock—— 这个能力是否是「模拟实现」。
demo 里像剪贴板(内存模拟)、定位(写死坐标)就是mock: true。打到真机时一眼能看出哪些是「为了能跑而 mock 的」,哪些是「读真实系统的」。这个字段以后还能驱动 UI:模拟能力前面加个「演」字标识,避免演示时被误以为是真功能。

enabled—— 是否启用。
注册表dispatch时会查这个字段:能力虽然登记在册,但enabled === false就回ABILITY_DISABLED(403),不进 handler。给「能力开关」、「灰度上线」、「应急下线」预留了入口。

三、AbilityMeta和 handler 的关系

二者放在同一份记录里:

exportinterfaceRegisteredAbility{meta:AbilityMeta;// 这是个什么东西handler:NativeAbilityHandler;// 怎么做这件事}

可以这么类比:

metahandler
类比营业执照 + 业务说明牌真正办事的窗口职员
内容「我是谁、归哪管、要什么证件、是不是模拟营业、今天开不开门」「来一个我办一个」
谁来读调试面板、BridgeLog、权限校验、README 生成器注册表的dispatch
静态 / 动态静态(一旦注册一般不变)动态(每次请求都跑一次)

两者必须同时出现:注册一个能力的时候,handler 是必填,meta 也是必填。这避免了「有人偷偷塞个 handler 进去不写元信息」这种情况。来看实际注册代码:

constdeviceInfoMeta:AbilityMeta={action:BridgeAction.GET_DEVICE_INFO,namespace:'device',description:'获取设备信息',permission:'none',mock:true,enabled:true};registry.register(deviceInfoMeta,(req:BridgeRequest):Record<string,string>=>{returnthis.getDeviceInfo();});

每个能力都是「先把说明书填好,再把办事函数交出来」。这意味着代码里再也没法出现「这能力我也不知道是干嘛的,就是有」的灰色地带。

四、为什么AbilityMeta适合上 BridgeLog / 能力面板 / README

回想一下没有 meta 的 BridgeLog 长什么样:

[完成] getDeviceInfo id req_001 → {...} ← {...} [失败] foobar id t1 → {...} ← {"code":404, ...}

只有 action 名。看着不亏,但回答不了下面这些问题:

  • 这是个什么类别的能力?
  • 这次调用是 mock 还是真实底座?
  • 这能力默认要权限吗?

把 meta 一接上,立马不一样:

[完成] device/getDeviceInfo id req_001 permission=none mock=true 8ms [失败] unknown/foobar id t1 3ms ← code 404

同样这条日志,现在能告诉你:

  • 这是 device 类能力
  • 不需要权限
  • 是模拟实现(演示用,不要打包到生产去)
  • 总耗时 8 ms

调试面板做出来后,能直接画成一张表:每行 action、namespace、description、permission、mock、enabled、最近调用次数、平均耗时 ——所有需要的字段,meta 这边已经备齐了。这就是「把数据备好 ≠ 立即做面板」,但等想做时不用回头补数据。

README 自动生成同理:写一段 markdown 模板遍历registry.listAbilities(),每个 ability 渲染成一行说明 —— 这张能力清单就再也不会因为「忘了同步」而过时。

五、用getLocation举一个完整的例子

来挑一个 demo 里的getLocation能力,逐字段说说 meta 怎么被用上:

{action:'getLocation',namespace:'location',description:'获取模拟定位信息',permission:'location.mock',mock:true,enabled:true}
  • action: 'getLocation'—— H5 调用时send({ action: 'getLocation', ... })就走到这里。
  • namespace: 'location'—— BridgeLog 里这条记录会显示location/getLocation。未来调试面板按 namespace 分组时,它跟其他定位类能力(geocode、reverseGeocode 之类,未来加的话)会被分到同一组。
  • description: '获取模拟定位信息'—— README 自动生成的能力清单里,这条会输出「location.getLocation—— 获取模拟定位信息」。
  • permission: 'location.mock'—— 这是个特殊权限标记:「模拟定位」。将来真接定位时,会换成'location.read',需要的权限就升级了。现在打这个标记,将来做权限校验时一眼能看出来「这能力在 demo 阶段允许,到真机要换」。
  • mock: true——NativeAbilityImp.readLocation()写死了一组坐标(22.3193, 114.1694),返回 source=mock。BridgeLog 看到mock=true就知道这条数据不能用于业务决策。
  • enabled: true—— 注册时启用;如果要临时关掉,改成false,再调就会回 403。

一条 meta,让一个能力同时具备「执行」「展示」「分类」「权限识别」「开关控制」五种属性。

六、hasAbility/isEnabled/listAbilities怎么用

AbilityMeta不是只能写不能读。Registry 提供三个读取入口:

hasAbility(action:string):boolean// action 是否已注册isEnabled(action:string):boolean// 已注册且 enabled=truelistAbilities():AbilityMeta[]// 列出所有 meta(不暴露 handler)
  • hasAbility—— 「这个 action 我认不认识」的预检;调试面板用它筛已注册名单。
  • isEnabled—— 「这个能力现在能不能调」的预检;UI 上灰显某个按钮的逻辑可以挂在这里。
  • listAbilities—— 直接把所有 meta 倒出来给上层用。

为什么listAbilities不返回 handler,只返回 meta?因为对外暴露的应该是「能力的描述」,不是「能力的实现」。Handler 是动态的、有副作用的、可能改的;meta 是静态的、能转 JSON 的、可以截屏放进文档的。

七、如果要加扫码 / 支付 / 分享,meta 怎么扩

设想一下,明天要加三个能力:扫码、支付、分享。每个能力的 meta 提前先想清楚:

constscanQrCodeMeta:AbilityMeta={action:'scanQrCode',namespace:'camera',description:'调起扫码界面,识别二维码 / 条形码',permission:'camera.use',mock:false,enabled:true};
constrequestPaymentMeta:AbilityMeta={action:'requestPayment',namespace:'payment',description:'发起一次支付(需要商户接入)',permission:'payment.request',mock:true,// demo 阶段先 mock 一个支付成功enabled:false// 默认关,需要灰度时再打开};
constshareTextMeta:AbilityMeta={action:'shareText',namespace:'social',description:'调起系统分享面板分享一段文字',permission:'share.system',mock:false,enabled:true};

看出门道了吗?AbilityMeta让「这能力到底什么样」的所有维度都集中在一个对象里

  • 扫码需要相机权限 ——permission: 'camera.use'
  • 支付目前是 mock,并且默认不开 ——mock: true, enabled: false
  • 分享是 UI 层副作用 ——namespace: 'social'

如果只有action → handler的注册方式,这些「能力到底什么样」的信息就会散在四处:权限分布在某个权限校验函数里、是不是 mock 注释在 Imp 文件里、是不是默认开看运维配置文件……每加一个能力都要找一遍。有了 meta,一处定义、处处使用

八、新人最容易踩到的几个点

1. meta 不是注释。
看上去 meta 字段都是「人话描述」,但它们是 ArkTS 强类型对象,会跟着代码一起被打包、可以被读、可以被序列化。注释不能被代码访问,meta 可以 —— 这是它们的根本区别。

2.enabledmock是两个独立维度。
新手容易混。enabled = false表示「这能力不能调」,是一个开关;mock = true表示「这能力的实现是模拟的」,是一种性质。一个 mock 能力可以是enabled = true(dem 里能调);一个真实能力也可以是enabled = false(线上没准备好)。

3.permission暂时只是字符串。
当前项目没接真实权限校验框架,permission字段就是个标签,谁都没拿它做拦截判断。但字段已经在那儿了,将来加权限校验时,只需要在Registry.dispatch里加一句if (!permissionGranted(reg.meta.permission)) return fail(...),整张表的权限要求都生效 —— 这就是「数据先备齐、再做功能」的好处。

4. meta 必须和 handler 一起注册。
不存在「先注册 handler,后补 meta」的 API。这是设计上故意的:能力的「身份」和「实现」要么一起出现要么都没有,避免出现「能调但说不出是干嘛」的孤魂能力。

我应该能讲出来的 5 个问题

  1. 只有 action → handler 的注册方式,会让哪些场景没法做?至少举出三个。
  2. AbilityMeta里六个字段(action / namespace / description / permission / mock / enabled)分别给什么场景用?请逐个对应。
  3. mockenabled这两个字段差别在哪?能不能用同一个字段表示?
  4. listAbilities()为什么返回AbilityMeta[]而不是RegisteredAbility[]
  5. 假设明天要加一个requestPayment能力,且支付权限尚未到位,应该怎么填它的 meta?为什么?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询