React-Native开发鸿蒙NEXT-多bundle的加载
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { line-height: 1.5; margin-top: 35px; margin-bottom: 10px; padding-bottom: 5px }
.markdown-body h1 { font-size: 24px; line-height: 38px; margin-bottom: 5px }
.markdown-body h2 { font-size: 22px; line-height: 34px; padding-bottom: 12px; border-bottom: 1px solid rgba(236, 236, 236, 1) }
.markdown-body h3 { font-size: 20px; line-height: 28px }
.markdown-body h4 { font-size: 18px; line-height: 26px }
.markdown-body h5 { font-size: 17px; line-height: 24px }
.markdown-body h6 { font-size: 16px; line-height: 24px }
.markdown-body p { line-height: inherit; margin-top: 22px; margin-bottom: 22px }
.markdown-body img { max-width: 100% }
.markdown-body hr { border-top: 1px solid rgba(221, 221, 221, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body code, .markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre { overflow: auto; position: relative; line-height: 1.75 }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body a { text-decoration: none; color: rgba(2, 105, 200, 1); border-bottom: 1px solid rgba(209, 233, 255, 1) }
.markdown-body a:active, .markdown-body a:hover { color: rgba(39, 91, 140, 1) }
.markdown-body table { display: inline-block !important; font-size: 12px; width: auto; max-width: 100%; overflow: auto; border: 1px solid rgba(246, 246, 246, 1) }
.markdown-body thead { background: rgba(246, 246, 246, 1); color: rgba(0, 0, 0, 1); text-align: left }
.markdown-body tr:nth-child(2n) { background-color: rgba(252, 252, 252, 1) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { color: rgba(102, 102, 102, 1); padding: 1px 23px; margin: 22px 0; border-left: 4px solid rgba(203, 203, 203, 1); background-color: rgba(248, 248, 248, 1) }
.markdown-body blockquote:after { display: block; content: "" }
.markdown-body blockquote>p { margin: 10px 0 }
.markdown-body ol, .markdown-body ul { padding-left: 28px }
.markdown-body ol li, .markdown-body ul li { margin-bottom: 0; list-style: inherit }
.markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item { list-style: none }
.markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul { margin-top: 0 }
.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul { margin-top: 3px }
.markdown-body ol li { padding-left: 6px }
.markdown-body .contains-task-list { padding-left: 0 }
.markdown-body .task-list-item { list-style: none }
@media (max-width: 720px) { .markdown-body h1 { font-size: 24px } .markdown-body h2 { font-size: 20px } .markdown-body h3 { font-size: 18px } }.markdown-body pre, .markdown-body pre>code.hljs { color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.hljs-comment, .hljs-quote { color: rgba(153, 153, 136, 1); font-style: italic }
.hljs-keyword, .hljs-selector-tag, .hljs-subst { color: rgba(51, 51, 51, 1); font-weight: 700 }
.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { color: rgba(0, 128, 128, 1) }
.hljs-doctag, .hljs-string { color: rgba(221, 17, 68, 1) }
.hljs-section, .hljs-selector-id, .hljs-title { color: rgba(153, 0, 0, 1); font-weight: 700 }
.hljs-subst { font-weight: 400 }
.hljs-class .hljs-title, .hljs-type { color: rgba(68, 85, 136, 1); font-weight: 700 }
.hljs-attribute, .hljs-name, .hljs-tag { color: rgba(0, 0, 128, 1); font-weight: 400 }
.hljs-link, .hljs-regexp { color: rgba(0, 153, 38, 1) }
.hljs-bullet, .hljs-symbol { color: rgba(153, 0, 115, 1) }
.hljs-built_in, .hljs-builtin-name { color: rgba(0, 134, 179, 1) }
.hljs-meta { color: rgba(153, 153, 153, 1); font-weight: 700 }
.hljs-deletion { background: rgba(255, 221, 221, 1) }
.hljs-addition { background: rgba(221, 255, 221, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }
ArkTS下加载RNbundle,大致分为以下三个步骤
- 创建并持有RN实例对象(RNInstance)
- 创建RN三方组件上下文(RNComponentContext)
- 创建UI(RNAPP/RNSurface)
基于RN构建的app,可以分为单bundle应用和多bundle应用两类。
单bundle应用很好理解:整个应用基于一个bundle。多bundle应用有通过"A-RN跳转到B-RN"方式的实现,或是界面干脆由多个bundle共同绘制。
对于多bundle应用来说往往需要考虑bundle的预加载。如果跳转对象bundle体积较大在看到界面前会有一定时间的白屏等待(多个bundle已经事先预下载到本地)。虽然可以通过加载动画等方式来缓解,但不能满足部分对延迟较为敏感的客户。鸿蒙上对于多bundle的预加载处理较为简单,下面来看看ArkTS下是如何加载多个bundle的。
还是以index.ets为入口页面进行说明。
在aboutToAppear生命周期中进行单/多个bundle的创建。其中rnohCoreContext是RN自身的上下文,我们加载的bundle都在它下面。
index.ets
@StorageLink('RNOHCoreContext') rnohCoreContext: RNOHCoreContext | undefined = undefined
aboutToAppear() {
......
// 多bundle
if (!this.rnohCoreContext) return;
this.loadMetroBundle()
}
loadMetroBundle方法是bundle加载的实现,通过LoadManager进行了封装,其中_rnInstance始终指向当前显示的RN对象用于原生和RN的交互(项目中不存在多个bundle同时活跃的情况)。注意assetsDest的设置,在例子中两个bundle的资源文件指向了不同的路径,这里面存在一个未解决问题,下面会提到。
index.ets
loadMetroBundle() {
let XXXBundle = new BundleBean();
XXXBundle.debugMode = false;
XXXBundle.bundlePath = 'XXX/bundle.harmony.js';
XXXBundle.assetsDest = 'rawfile/XXX/assets';
XXXBundle.appName = 'XXX';
LoadManager.loadMetroBundle(XXXBundle).then((flag: boolean) => {
AppStorage.setOrCreate('isMetroAvailable', flag)
this.isMetroAvailable = flag
this.isBundleXXXReady = flag;
if(flag){
this._rnInstance = LoadManager.instanceMap.get(XXXBundle.appName);
this.currentModuleName = XXXBundle.appName;
}
})
let YYYBundle = new BundleBean();
YYYBundle.debugMode = false;
YYYBundle.bundlePath = 'YYY/bundle.harmony.js';
YYYBundle.assetsDest = 'rawfile/YYY/assets';
YYYBundle.appName = 'YYY';
LoadManager.loadMetroBundle(YYYBundle).then((flag: boolean) => {
AppStorage.setOrCreate('isMetroAvailable', flag)
this.isMetroAvailable = flag
this.isBundleYYYReady = flag;
})
}
LoadManager.ets封装的loadMetroBundle用于创建RN实例与上下文。注意enableCAPIArchitecture的设置,现在只有ture这个选项,HarmonyOS NEXT由于不再支持java,turboModule在原生端只有cpp实现。
LoadManager.ets
public static async loadMetroBundle(bundle:BundleBean): Promise<boolean> {
const rnohCoreContext: RNOHCoreContext | undefined = AppStorage.get('RNOHCoreContext')
if (LoadManager.shouldResetMetroInstance && rnohCoreContext && !LoadManager.instanceMap.has(bundle.appName)) {
let rnInstance = await rnohCoreContext.createAndRegisterRNInstance({
createRNPackages: createRNPackages,
enableNDKTextMeasuring: true,
enableBackgroundExecutor: true,
enableCAPIArchitecture: ENABLE_CAPI_ARCHITECTURE,
arkTsComponentNames: arkTsComponentNames,
assetsDest:bundle.assetsDest
});
LoadManager.ctx = new RNComponentContext(
RNOHContext.fromCoreContext(rnohCoreContext!, rnInstance),
wrapBuilder(buildCustomComponent),
wrapBuilder(buildRNComponentForTag),
new Map()
);
let provider:JSBundleProvider |null = null;
if(bundle.debugMode){
provider = new MetroJSBundleProvider();
}else {
provider = new ResourceJSBundleProvider(rnohCoreContext.uiAbilityContext.resourceManager, bundle.bundlePath);
}
await rnInstance.runJSBundle(provider)
const jsBundleExecutionStatus: string = rnInstance.getBundleExecutionStatus(provider.getURL()) as string
console.log(" getBundleExecutionStatus = ", jsBundleExecutionStatus);
// LoadManager.shouldResetMetroInstance = false
if (jsBundleExecutionStatus === "DONE") {
// 缓存bundle
LoadManager.loadedBundle.add(bundle);
// 缓存rnInstance
LoadManager.instanceMap.set(bundle.appName,rnInstance);
return true
} else {
return false
}
}
return true
}
回到index.ets。对于单bundle应用,可以选择RNApp组件作为RN加载容器。
build() {
Column() {
if (this.rnohCoreContext && this.shouldShow) {
if (this.rnohCoreContext?.isDebugModeEnabled) {
RNOHErrorDialog({ ctx: this.rnohCoreContext })
}
RNApp({
rnInstanceConfig: {
createRNPackages,
enableNDKTextMeasuring: true,
enableBackgroundExecutor: false,
enableCAPIArchitecture: true,
arkTsComponentNames: arkTsComponentNames,
},
initialProps: { "keyXXX": "valueXXX" } as Record<string, string>,
appKey: "XXX",
wrappedCustomRNComponentBuilder: wrappedCustomComponentBuilder,
onSetUp: (rnInstance) => {
this._rnInstance = rnInstance;
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new MetroJSBundleProvider(),
// NOTE: to load the bundle from file, place it in
// `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js`
// on your device. The path mismatch is due to app sandboxing on HarmonyOS
new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]),
this.rnohCoreContext.logger),
})
}
}
.height('100%')
.width('100%')
}
RNApp组件只能存在一个,对于需要加载多个bundle,尤其是多bundle拼接出页面这种组件化开发场合,可以使用RNSurface作为加载容器。根据参数选择实现bundle的切换或者同时拼接UI。
build() {
Column() {
if (this.isBundleXXXReady && this.currentModuleName === "XXX") {
RNSurface({
surfaceConfig: {
appKey: "XXX",
initialProps: this.initPropsMap.get('XXX'),
},
ctx: new RNComponentContext(
RNOHContext.fromCoreContext(this.rnohCoreContext!, this._rnInstance),
wrappedCustomComponentBuilder,
wrapBuilder(buildRNComponentForTag),
new Map()
),
})
}
if(this.isBundleYYYReady && this.currentModuleName === "YYY") {
RNSurface({
surfaceConfig: {
appKey: "YYY",
initialProps: this.initPropsMap.get('YYY'),
},
ctx: new RNComponentContext(
RNOHContext.fromCoreContext(this.rnohCoreContext!, this._rnInstance),
wrappedCustomComponentBuilder,
wrapBuilder(buildRNComponentForTag),
new Map()
),
})
}
}
}
.height('100%')
.width('100%')
}
bundle间的切换通过RN发消息给原生实现。index.ets中,isBundleXXXReady,isBundleYYYReady,currentModuleName均是@state变量。
index.ets
emitter.on(ConstUtil.event_id_open_YYY, (data) => {
// 带入参数
this.initProps = {};
this.initPropsMap.delete('YYY');
if(data && data.data && data.data.param){
try {
this.initPropsMap.set('YYY',JSON.parse(data.data.param) as Record<string,object>);
} catch (error) {
console.error("Parsing error:", error);
}
}
// 切换显示YYYRN
this.currentModuleName = 'YYY';
this._rnInstance = LoadManager.instanceMap.get('YYY');
console.info('this.initProps = ' + this.initPropsMap.get('YYY'));
});
项目是基于单RN开发的,多个bundle的逻辑在单RN版本上架后开始修改,边边角角考虑尚有欠缺。最后说下assetDest的问题。目前发现设置资源文件路径后,app还是只会去读取rawfile/assets/文件下的内容,在鸿蒙论坛提问了还是没有找到原因,暂时是将两个RN的资源文件夹名称改成不一样的,让两个资源文件可以都放在rawfile/assets/下(形如rawfile/assets/src/imgXXX与rawfile/assets/src/imgYYY,这样需要批量替换下RN代码里的图片引用路径)。
鸿蒙论坛相关梯子传送门
这是大佬的回复,应该是官方马甲。
如有知道为何资源无法正常加载的同学不吝赐教:)
微信扫一扫
关注该公众号悬空八只脚
React-Native开发鸿蒙NEXT-多bundle的加载的更多相关文章
- React Native 开发笔记
ReactNativeDemo 学习ReactNative开发,搭建ReactNative第一个项目 React Native 开发笔记 1.安装Homebrew $ /usr/bin/ruby -e ...
- React Native开发入门
目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料 一.前言 虽然只是简单的了解了一下Reac ...
- React Native开发技术周报2
(1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...
- React Native开发技术周报1
(一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...
- DECO 一个REACT NAtive 开发IDE工具
DECO 一个REACT NAtive 开发IDE工具. 目前只支持 OS,NO WINDOWS https://www.decosoftware.com/ 一个方便的快速 ERXPRESS 教程:h ...
- React Native 开发之 (02) 用Sublime 3作为React Native的开发IDE
Sublime Text是一个代码编辑器.也是HTML和散文先进的文本编辑器.漂亮的用户界面和非凡的功能,例如:迷你地图,多选择Python插件,代码段等等.完全可自定义键绑定,菜单和工具栏等等.漂亮 ...
- 【转】【React Native开发】
[React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19) [React Native开发]React Native控件之Touchable*系列组 ...
- React Native开发的通讯录应用
React Native开发的通讯录应用(使用JavaScript开发原生iOS应用,vczero) 0.前言: 项目地址:https://github.com/vczero/React-Native ...
- iOS程序员的React Native开发工具集
本文整理了React Native iOS开发过程中有用的工具.服务.测试.库以及网站等. 工具 你可以选择不同的开发环境:DECO.EXPO或者你可以使用Nuclide+Atom,目前我使用EXPO ...
- React-Native(二):React Native开发工具vs code配置
从网上翻阅了一些开发react-native的开发工具时,发现其实可选的工具还是比较多的Sublime Text,WebStrom,Atom+Nuclide,vs code 等.因为我用.net生态环 ...
随机推荐
- Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
#div_digg { float: right; font-size: 12px; margin: 10px; text-align: center; width: 120px; position: ...
- C#使用Interlocked实现线程同步
通过System.Threading命名空间的Interlocked类控制计数器,从而实现进程 的同步.Iterlocked类的部分方法如下表: 示例,同时开启两个线程,一个写入数据,一个读出数据 代 ...
- 【ABAQUS 二次开发笔记】Python 报错收集和解决方法
1. 运行报错,找不到本地文件及模块.自定义模块 1.1 Example: $/home/tops/bin/python xxx.py Traceback (most recent call last ...
- Surpac 安装
修改config文件 share 文件进行整体替换 数字地质 环境配置问题总结 *http 报错! 块体生成数据暂停,点击生成验收量提交,报错生成失败!!! 解决方案:重新打开块体
- Tauri新手向 - 基于LSB隐写的shellcode加载器
此篇是记录自己初次学习tauri开发工具,包含遇到的一些问题以及基本的知识,也给想上手rust tauri的师傅们一些小小的参考.此项目为保持免杀性暂不开源,希望各位师傅多多支持,反响可以的话后续会放 ...
- DEV插件--Spreadsheet1电子表格
常用操作Spreadsheet常用属性标题栏是否可见 Spreadsheet1.TitleBar.Visible=true标题栏背景颜色 Spreadsheet1.TitleBar.Interior. ...
- go 语言中的占位符详解
在 Go 语言的 fmt 包中,占位符用于格式化输出,允许在输出时插入变量的值.以下是一些常用的占位符及其用法: 通用占位符: %v:按照值的默认格式输出. %+v:输出结构体时,会添加字段名. %# ...
- GStreamer开发笔记(一):GStreamer介绍,在windows平台部署安装,打开usb摄像头对比测试
前言 当前GStreamer是开源的多媒体框架,其适配后可以支持板卡的硬编码.硬解码,还提供RTSP服务器等功能,降低了音视频开发的门槛(转移到gstreamer配置和开发上了,但是跨平台),瑞芯 ...
- tomcat非root用户启动
部署远程服务器时候, 基本都是用root账户登录, 习惯上会直接使用root启动tomcat. 这样其实是有风险的, 黑客获取的权限即容器的权限, 如果容器运行权限就很高,被攻破黑客即可获取很高的权限 ...
- seata-server 1.3.0整合nacos,使用nacos做注册和配置中心
前言 关于seata版本的选择和更详细的安装,可以参考 SpringCloud Alibaba之Seata入门及踩坑 本篇博客是整合nacos,nacos直接下载安装解压运行就可以了. seata的下 ...