查看: 111|回复: 3

鸿蒙ASCF半屏拉起元服务跨服务跳转的实现与踩坑

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙元服务开发中,经常会遇到这样的需求:用户在A元服务里点击一个按钮,需要临时调用B元服务的能力,操作完回到A服务继续原流程。比如支付、实名认证、选优惠券等。早期我尝试用 has.navigateTo 去跳转,很快发现不行——那是同一个元服务内的页面跳转,跨服务的拉起需要走ASCF,也就是元服务跨服务调用框架。ASCF提供了专门的半屏拉起组件 open-embedded-atomicservice,目标服务从底部滑上来,盖住屏幕下半部分,用户操作完自动退出,回到原服务页面,体验上比跳新页面再返回流畅不少。项目代码里已经能用到这个组件,我在实际对接过程中验证了几个关键点,整理成文。

组件核心用法

open-embedded-atomicservice 是ASCF提供的半屏拉起组件的标签名,使用方式就是把它包在触发操作的元素外层。它不需要自己写跨进程通信逻辑,也不需要管理路由栈,拉起、传参、返回值、异常回调都由组件帮你包好。

组件属性只有5个:appid(必填)、path、want-param、bindterminated、binderror。其中 appid 是唯一必填项,相当于目标元服务的唯一标识,可以从目标服务开发者那里获取,或者在AppGallery Connect上查到。我第一次测试时随便填了一个id,结果binderror报错10001,提示找不到对应的元服务。这里要注意,appid必须对应华为市场正式上架、或者调试签名匹配的元服务,二者不一致时本地调试会一直失败。
  1. <open-embedded-atomicservice appid="{{appid}}">
  2. <button>打开半屏元服务</button>
  3. </open-embedded-atomicservice>
复制代码

path指定打开目标服务的入口页面。如果目标服务有多个页面,用path指定具体路径,路径格式跟app.json里定义的一致,后面可以带 ?key=value 参数,目标服务Page.onLoad里能直接拿到。path为空或无效时,默认打开目标服务的首页。
  1. <open-embedded-atomicservice
  2. appid="{{appid}}"
  3. path="page/index/index?type=payment&amount=99">
  4. <button>去支付</button>
  5. </open-embedded-atomicservice>
复制代码

需要传复杂数据时用want-param。格式取决于目标服务的开发框架,这是最容易踩坑的地方。如果目标是ASCF框架开发的元服务,数据必须用 ascfPara.extraData 字段包一层;如果是ArkTS框架开发的,直接平铺键值对即可。
  1. // 目标为ASCF元服务时
  2. Page({
  3. data: {
  4. params: {
  5. ascfPara: {
  6. extraData: {
  7. orderId: '123456',
  8. amount: 99.00,
  9. productName: '高级会员'
  10. }
  11. }
  12. }
  13. },
  14. });
复制代码
  1. // 目标为ArkTS元服务时
  2. Page({
  3. data: {
  4. params: {
  5. data: 'test',
  6. orderId: '123456'
  7. }
  8. },
  9. });
复制代码

我第一次对接时给ASCF服务传了平铺格式,对方一直拿不到extraData,排查了半天才意识到要包一层ascfPara,浪费了不少时间。建议对接前先确认目标服务的开发框架,或者直接问对方要传参样例。

回调与错误处理

bindterminated 在目标服务正常退出时触发,回调参数里能拿到目标服务返回的数据。返回数据的获取方式也分框架:ArkTS目标服务调用 terminateSelfWithResult 退出,数据会带回;ASCF目标服务(1.0.18+)调用 has.terminateSelf 即可。
  1. <open-embedded-atomicservice
  2. appid="{{appid}}"
  3. bindterminated="onTerminated">
  4. <button>打开</button>
  5. </open-embedded-atomicservice>
复制代码
  1. Page({
  2. onTerminated(info) {
  3. // info.detail.params 里是目标服务返回的数据
  4. console.info('目标服务返回:', JSON.stringify(info.detail.params));
  5. has.showModal({
  6. title: '返回数据',
  7. content: JSON.stringify(info.detail.params),
  8. });
  9. },
  10. });
复制代码

binderror 在打开失败或运行异常时触发。打开失败的原因基本就是三类:appid不对、目标服务没安装、目标服务启动崩溃。建议两个回调都绑上。我一开始只绑了bindterminated没绑binderror,测试时故意让目标服务崩溃了一下,发现bindterminated不触发,还以为数据丢了——后来才知道闪退或被杀时触发的是binderror。
  1. Page({
  2. onError(err) {
  3. console.error('打开失败:', err.detail.errCode, err.detail.errMsg);
  4. has.showModal({
  5. title: '打开失败',
  6. content: err.detail.errMsg,
  7. });
  8. },
  9. });
复制代码

典型场景:支付服务拉起

以电商订单支付为例,主服务渲染订单信息,按钮外层套open-embedded-atomicservice,appid指向支付元服务,path指定支付页面并携带订单参数,want-param用ascfPara.extraData包裹订单数据,bindterminated处理支付结果,binderror做降级。支付完成自动回到主服务,不需要手动返回。
  1. <view class="scene-card">
  2. <text class="scene-title">订单支付</text>
  3. <view class="order-info">
  4. <text class="order-label">商品:鸿蒙开发实战课程</text>
  5. <text class="order-label">金额:¥99.00</text>
  6. </view>
  7. <open-embedded-atomicservice
  8. appid="{{paymentAppId}}"
  9. path="page/payment/index?amount=99"
  10. want-param="{{paymentParams}}"
  11. bindterminated="onPaymentDone"
  12. binderror="onPaymentError">
  13. <button type="primary" class="pay-btn">去支付</button>
  14. </open-embedded-atomicservice>
  15. </view>
复制代码
  1. Page({
  2. data: {
  3. paymentAppId: '57xxxxxxxxx', // 支付服务的 appid
  4. paymentParams: {
  5. ascfPara: {
  6. extraData: {
  7. orderId: 'ORD20240628001',
  8. amount: 99,
  9. subject: '鸿蒙开发实战课程',
  10. },
  11. },
  12. },
  13. },
  14. onPaymentDone(info) {
  15. const result = info.detail.params;
  16. if (result.payResult === 'success') {
  17. has.showToast({ title: '支付成功' });
  18. } else {
  19. has.showToast({ title: '支付取消', icon: 'none' });
  20. }
  21. },
  22. onPaymentError(err) {
  23. has.showToast({ title: '支付服务异常', icon: 'none' });
  24. },
  25. });
复制代码

类似的场景还有打开认证服务:账户操作前半屏拉起人脸识别或身份确认服务,认证通过后bindterminated回调返回verified字段,再执行后续操作。注意如果目标服务未安装,打开会失败,建议在binderror里做降级处理,比如提示用户先安装认证服务,或者走备选方案。

与 has.navigateTo 的边界

has.navigateTo 只适用于同一个元服务内部的页面跳转。跨元服务、需要半屏呈现、需要把结果带回来,用open-embedded-atomicservice。业务决策很简单:看你的目标页面在不在当前元服务的包内,不在就上这个组件。

踩坑记录

把我在实际开发中遇到的几个坑整理如下,每个都是真实排查过的。

appid与调试签名不匹配。测试环境填正式环境的appid,本地调试一直报10001,因为调式签名跟正式appid对不上,换成测试环境的appid就正常了。

want-param格式不匹配。ASCF服务要包ascfPara.extraData,ArkTS直接平铺。搞混的话目标服务解析不到数据,看起来像是组件坏了,其实是格式问题。

bindterminated只在正常退出时触发。目标服务闪退或被系统杀掉时,触发的是binderror不是bindterminated。所以降级逻辑不要只写在bindterminated里,binderror也要处理。

path路径必须写完整。app.json里定义的是 "page/payment/index",path就写 "page/payment/index?key=val",不能简写成 "/payment/index" 或 "payment/index"。

半屏布局适配。半屏状态下目标服务显示区域变小,如果页面按全屏设计,底部按钮可能被截掉一半。我自己就遇到过:被拉起的服务底部确认按钮被半屏切掉,后来在目标服务里加媒体查询,检测到窗口高度低于阈值时缩小padding和字号才解决。更重要的是,半屏状态下getSystemInfo、getWindowInfo这些API的返回值可能跟全屏不一致,不要依赖它们做布局计算,用flex等比自适应更安全。

SDK版本要求。open-embedded-atomicservice组件的起始版本是1.0.17,SDK版本要求不低于5.1.0(18)。版本不够组件不生效,开发前先确认环境。

另外这个组件目前只有华为生态支持,在其他设备上可能不生效。做跨平台兼容时,建议先判断环境再决定要不要展示这个组件。

总结

open-embedded-atomicservice 的价值在于把跨元服务调用的复杂度封装掉了,不再需要自己写跨进程通信。核心要点四个:appid必填且要对得上、want-param格式看目标框架、bindterminated拿返回值、binderror做降级。搭配合适的path和半屏布局适配,就能实现流畅的跨元服务半屏交互。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF半屏拉起元服务跨服务跳转的实现与踩坑

感谢分享!最近正好在搞元服务间调用,ASCF半屏拉起这个方案确实比跳页面体验好。之前一直被want-param传参格式绕晕,看了你这个总结清楚多了,特别是ASCF框架要包一层ascfPara.extraData,这个坑我估计也会踩。想再请教一下,如果目标服务启动后没有正常退出,比如用户点击了系统返回键,是不是也会触发binderror?还是说会有类似取消的回调?另外path里带参数时,中文值需要编码吗?
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF半屏拉起元服务跨服务跳转的实现与踩坑

感谢楼主分享,这个半屏拉起的体验确实比跳转再返回顺滑不少。我之前也踩过want-param格式的坑,对接ASCF服务时死活拿不到参数,后来也是看了文档才发现要包ascfPara,这块如果能在文档里标注清楚就好了。另外binderror和bindterminated确实两个都得绑,不然出错时容易误判成没返回值。楼主总结得很清楚,收藏了。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: 鸿蒙ASCF半屏拉起元服务跨服务跳转的实现与踩坑

感谢楼主的分享,写得非常清楚!特别是ASCF和ArkTS框架传参格式的差异,这个坑我前几天也踩了,当时对方一直说收不到extraData,后来才发现要包一层ascfPara,确实浪费了不少时间。楼主总结的这几个属性用法很实用,尤其是binderror和bindterminated的触发区别,我之前也以为闪退会走terminated,看了这个才明白。想再请教一下:实际支付场景里,如果目标服务返回的数据需要区分支付成功还是失败,是在bindterminated的回调里自己判断返回字段吗?还是ASCF有标准化的返回码定义?另外半屏拉起对目标服务的页面高度有没有限制,能不能自定义高度?期待后续文章!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-8-14 11:24 , Processed in 0.024370 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部