一、前言
最近在开发中,我们的元服务需要被其他应用通过FullScreenLaunchComponent拉起,我只能说当时上了5.0的当,FullScreenLaunchComponent也是Beta版本的!在实际开发中作为碰了几次灰,踩了不少坑,觉得有必要分享下,故有了此篇文章。
该系列依旧会带着大家,了解,开阔一些不怎么热门的API,也可能是偷偷被更新的API,也可以是好玩的,藏在官方文档的边边角角~当然也会有一些API,之前是我们辛辛苦苦的手撸代码,现在有一个API能帮我们快速实现的,希望大家能找宝藏。
如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏
二、FullScreenLaunchComponent的诞生背景
OK,步入正题把,在HarmonyOS开发中,我们可能会需要在一个应用中嵌入另一个应用的能力。比如,你的应用想要使用某个元服务提供的功能,但又不想让用户跳转到另一个应用,而是希望在自己的应用内无缝使用这个能力。
以前我们可能需要通过跳转的方式拉起另一个应用,这样用户体验就不够流畅。FullScreenLaunchComponent就是为了解决这个问题而生的,它允许我们以全屏方式拉起元服务,使得应用能够提供更友好的用户体验。虽然我不是很喜欢这个能力,因为真的坑到我了。
1. 什么是FullScreenLaunchComponent
FullScreenLaunchComponent是由ArkUI提供的组件,简单来说,它就是一个"容器",可以嵌入运行另一个元服务的UI。
当被拉起方授权使用方应用嵌入式运行元服务时,使用方应用可全屏嵌入式运行该服务;若未授权,则使用方应用将以跳出式方式拉起元服务。
三、基本使用
1. 版本说明
- 该组件从API version 12开始支持
- 该组件不支持在Wearable设备上使用(真的需要在穿戴设备使用这个功能吗????)
- 5.0和6.0的表现非常不一样!
2. 使用方接入
使用方接入非常简单,只需要几行代码就能搞定:
typescript
import { FullScreenLaunchComponent } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State appId: string = 'XXXXX'; // 元服务appId
build() {
Row() {
Column() {
FullScreenLaunchComponent({
content: this.ColumnChild, // 占位图标组件
appId: this.appId, // 元服务appId
options: {}, // 拉起参数
onTerminated: (info) => {
// 元服务退出时的回调
console.info(`onTerminated code: ${info.code.toString()}`);
},
onError: (err) => {
// 发生异常时的回调
console.error(`onError code: ${err.code}, message: ${err.message}`);
},
onReceive: (data) => {
// 接收元服务传递的数据
console.info(`onReceive, data: ${JSON.stringify(data)}`);
}
}).width("80vp").height("80vp")
}
.width('100%')
}
.height('100%')
}
}
// 占位图标,点击后拉起元服务
@Builder
function ColumnChild() {
Column() {
Image($r('app.media.startIcon'))
Text('test')
}
}
可以看到,接入确实非常方便!只需要:
- 导入
FullScreenLaunchComponent - 传入
appId(元服务的唯一标识) - 传入
content(占位图标组件) - 可选:设置回调函数
3. 提供方实现
提供方需要继承EmbeddableUIAbility,这是必须的!否则系统无法保证元服务功能正常(刚开始,你继承或者不继承,都不影响不正常...)。
提供方入口文件:/src/main/ets/entryability/EntryAbility.ets
typescript
import { AbilityConstant, Want, EmbeddableUIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
const DOMAIN = 0x0000;
export default class EntryAbility extends EmbeddableUIAbility {
storage = new LocalStorage();
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// ⚠️ 重要:正常开发中getMainWindowSync可以正常使用,但如果需要提供给FullScreenLaunchComponent,
// 在FullScreenLaunchComponent场景下getMainWindowSync会崩溃,必须使用try-catch,虽然你也拿不到,笑死了。
try {
let mainWindow = windowStage.getMainWindowSync();
//TODO 做点什么
} catch (err) {
hilog.error(DOMAIN, 'testTag', 'getMainWindowSync failed: %{public}s', JSON.stringify(err));
}
windowStage.loadContent('pages/Index', this.storage);
}
onWindowStageDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}
四、实际开发中的坑点
以下问题都是我们在作为提供方开发时遇到的,如果你是使用方,可能不会遇到这些问题,但了解这些限制也有助于更好地使用FullScreenLaunchComponent。
1. 状态栏高度获取问题
在HarmonyOS 6.0之前,我们在EmbeddableUIAbility中顶部的状态栏高度没有办法正确的获取。这个问题很隐蔽,因为不会报错,只是获取到的值不对,导致UI布局出现问题。
解决方案:
在6.0之前,我直接使用了固定的值。虽然不够优雅,但这是最稳妥的方案。如果你的应用需要适配多个版本,建议封装一个工具方法来处理状态栏高度的获取,避免在不同版本上出现显示问题。
2. Window相关API的异常处理
在正常开发中,我们一般会使用windowStage.getMainWindowSync()来获取主窗口,这个方法在普通场景下是可以正常工作的。但是,如果你的元服务需要提供给FullScreenLaunchComponent使用,getMainWindowSync()会崩!!!!!
很多地方习惯性地使用getMainWindow()或者windowStage.getMainWindowSync(),结果在FullScreenLaunchComponent场景下直接崩溃,当时真的是一脸懵逼。(这也促使我们在后面的代码规范中,禁止所有对window方法的直接使用)
这是因为FullScreenLaunchComponent内部默认方式下,提供方无真正的窗口承载,所以无法正常获取Window对象。如果你没有进行try-catch的话,哦吼,崩掉。
3. 调试困难问题
调试非常困难,特别是你需要调试的东西在启动流程(如onCreate、onWindowStageCreate)的时候,没法进行调试。因为每次被使用方拉起时,都会创建一个新的Embeddable进程,没办法提前断点,或者断点启动,所以根本没有办法进行启动流程的调试。
我们只能通过日志来一点点排查问题,非常痛苦。
解决方案:
只能一点点的加日志输出。虽然调试体验不好,但这是跨进程架构带来的限制,我们只能通过日志来排查问题。
4. UIContext获取异常
因为提供方无真正的窗口承载,所以第2点说的window异常的,实际上获取的UIContext的获取也是异常的。我们在代码中大量使用了getUIContext()来获取UIContext,结果在FullScreenLaunchComponent场景下可能获取不到正确的值,导致很多功能异常。
最后我们修改了大量的使用Context的地方,部分改用applicationContext,才解决了这个问题。
在FullScreenLaunchComponent场景下,尽量避免使用UIContext,优先使用applicationContext或UIAbilityContext。如果必须使用UIContext,一定要做好异常处理。
5. 不支持富文本组件
部分功能使用了RichText组件来显示富文本内容,然后发现RichText组件在FullScreenLaunchComponent中不支持。淦!
解决方案:
阿弥陀佛,我们直接重写了一套,富文本组件,搞心态。
6. EmbeddableUIAbilityContext的特殊性
作为提供方,我们必须继承EmbeddableUIAbility,但是EmbeddableUIAbility的上下文,是和普通的UIAbility的上下文UIAbilityContext不一样的,使用的是EmbeddableUIAbilityContext,虽然是有继承关系,但在某些API的使用上可能存在差异。
我们在开发时,有些原本在普通UIAbility中能正常工作的代码,在EmbeddableUIAbility中就不行了,就是因为上下文类型的差异导致的。
在使用上下文相关API时,要注意EmbeddableUIAbilityContext和UIAbilityContext的差异,做好兼容处理。
五、能力范围与限制
官方文档其实也列出很多限制场景,如下:
1. 不支持的能力
组件方面:
FullScreenLaunchComponent:不支持嵌套拉起(总不能套娃吧...)EmbeddedComponent:不支持嵌套拉起RichText:不支持富文本组件FolderStack:不支持(需要和宿主方窗口形成联动)XComponent:不支持FormLink:不支持HyperLink:不支持ContextMenu:不支持
Node-API接口方面:
- 页面间转场:不支持
- 组件内隐式共享元素转场:不支持
componentUtils:不支持(获取的位置信息是EmbeddableUIAbility的WindowProxy的信息)UIContext:不支持(提供方无真正的窗口承载)DragController:不支持(依赖UIContext)- 注册自定义字体:不支持
2. 部分支持的能力
弹窗组件:
- 警告弹窗、列表选择弹窗、自定义弹窗:部分支持
- 若在FullScreenLaunchComponent中设置
showInSubWindow为true,弹窗将基于FullScreenLaunchComponent的宿主窗口对齐 - 仅限于窗口对齐,其他能力可能受限
Navigation组件:
- 部分支持
- 如果FullScreenLaunchComponent未设置模态或沉浸式,Navigation无法扩展到安全区
- 无法路由到宿主方的页面中
3. 其他约束
安全能力约束:
- FullScreenLaunchComponent能力无法独立提供安全保障机制
- 提供方应用需要结合使用其他ArkUI的能力进行安全保护
- 如果存在安全方面的诉求,建议优先使用其他方案
嵌套约束:
- FullScreenLaunchComponent暂不支持嵌套
- 不支持A应用(UIAbility)->B应用(EmbeddableUIAbility)->C应用(EmbeddableUIAbility)这种嵌套能力依赖
事件处理机制约束:
- FullScreenLaunchComponent不支持通用事件,会将事件经过坐标转换后传递给提供方EmbeddableUIAbility处理
- 宿主进程与提供方进程的交互默认均是异步处理
- 某些事件(如按键事件、焦点事件)支持同步处理,但支持超时等待机制
页面渲染效果体验约束:
- 闪白现象:创建并拉起另一个进程是耗时的,使用方在等待过程中会感知到FullScreenLaunchComponent的背景色(默认是白色)
- 渲染显示不同步现象:当使用方应用页面快速变化时(如横竖屏切换),会出现使用方页面渲染过程和FullScreenLaunchComponent组件中展示的内容不同步的情况
消减闪白问题的方法:
typescript
FullScreenLaunchComponent({
content: ColumnChild,
appId: this.appId,
options: {},
})
.backgroundColor('#F5F5F5') // 根据提供方页面背景色设置,实现无跳变感知
.width('100%')
.height('100%')
六、总结
总的来说,FullScreenLaunchComponent是一个很强大的组件,使用方接入确实非常方便(苦了我们这些提供方),虽然存在一些限制和坑点,但FullScreenLaunchComponent的效果其实还不错的,有时候都感知不到是到了我们的元服务里面,以为就是自己~
好了,关于FullScreenLaunchComponent的内容我们就介绍到这里。虽然使用方接入很方便,但作为提供方在实际开发中确实有不少坑,希望这篇文章能帮助到正在开发或准备开发可被FullScreenLaunchComponent接入的元服务的开发者。
七、最后
因为篇幅原因,我们先到这,哈、下一篇还没想好写什么,如果你有想看的也可以在评论区,或者私信给我,马上写~咕咕咕。
如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏