AbilityMeta 能力元信息:不只是能调用,还要能看懂
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.etsexportinterfaceAbilityMeta{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):Recordstring,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 permissionnone mocktrue 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返回 sourcemock。BridgeLog 看到mocktrue就知道这条数据不能用于业务决策。enabled: true—— 注册时启用如果要临时关掉改成false再调就会回 403。一条 meta让一个能力同时具备「执行」「展示」「分类」「权限识别」「开关控制」五种属性。六、hasAbility/isEnabled/listAbilities怎么用AbilityMeta不是只能写不能读。Registry 提供三个读取入口hasAbility(action:string):boolean// action 是否已注册isEnabled(action:string):boolean// 已注册且 enabledtruelistAbilities():AbilityMeta[]// 列出所有 meta不暴露 handlerhasAbility—— 「这个 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.enabled和mock是两个独立维度。新手容易混。enabled false表示「这能力不能调」是一个开关mock true表示「这能力的实现是模拟的」是一种性质。一个 mock 能力可以是enabled truedem 里能调一个真实能力也可以是enabled false线上没准备好。3.permission暂时只是字符串。当前项目没接真实权限校验框架permission字段就是个标签谁都没拿它做拦截判断。但字段已经在那儿了将来加权限校验时只需要在Registry.dispatch里加一句if (!permissionGranted(reg.meta.permission)) return fail(...)整张表的权限要求都生效 —— 这就是「数据先备齐、再做功能」的好处。4. meta 必须和 handler 一起注册。不存在「先注册 handler后补 meta」的 API。这是设计上故意的能力的「身份」和「实现」要么一起出现要么都没有避免出现「能调但说不出是干嘛」的孤魂能力。我应该能讲出来的 5 个问题只有 action → handler 的注册方式会让哪些场景没法做至少举出三个。AbilityMeta里六个字段action / namespace / description / permission / mock / enabled分别给什么场景用请逐个对应。mock和enabled这两个字段差别在哪能不能用同一个字段表示listAbilities()为什么返回AbilityMeta[]而不是RegisteredAbility[]假设明天要加一个requestPayment能力且支付权限尚未到位应该怎么填它的 meta为什么