.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rgba(37, 41, 51, 1) }
.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代码里的图片引用路径)。

鸿蒙论坛相关梯子传送门

developer.huawei.com/consumer/cn…

这是大佬的回复,应该是官方马甲。

如有知道为何资源无法正常加载的同学不吝赐教:)

微信扫一扫

关注该公众号悬空八只脚

React-Native开发鸿蒙NEXT-多bundle的加载的更多相关文章

  1. React Native 开发笔记

    ReactNativeDemo 学习ReactNative开发,搭建ReactNative第一个项目 React Native 开发笔记 1.安装Homebrew $ /usr/bin/ruby -e ...

  2. React Native开发入门

    目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料   一.前言 虽然只是简单的了解了一下Reac ...

  3. React Native开发技术周报2

    (1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...

  4. React Native开发技术周报1

    (一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...

  5. DECO 一个REACT NAtive 开发IDE工具

    DECO 一个REACT NAtive 开发IDE工具. 目前只支持 OS,NO WINDOWS https://www.decosoftware.com/ 一个方便的快速 ERXPRESS 教程:h ...

  6. React Native 开发之 (02) 用Sublime 3作为React Native的开发IDE

    Sublime Text是一个代码编辑器.也是HTML和散文先进的文本编辑器.漂亮的用户界面和非凡的功能,例如:迷你地图,多选择Python插件,代码段等等.完全可自定义键绑定,菜单和工具栏等等.漂亮 ...

  7. 【转】【React Native开发】

    [React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19)  [React Native开发]React Native控件之Touchable*系列组 ...

  8. React Native开发的通讯录应用

    React Native开发的通讯录应用(使用JavaScript开发原生iOS应用,vczero) 0.前言: 项目地址:https://github.com/vczero/React-Native ...

  9. iOS程序员的React Native开发工具集

    本文整理了React Native iOS开发过程中有用的工具.服务.测试.库以及网站等. 工具 你可以选择不同的开发环境:DECO.EXPO或者你可以使用Nuclide+Atom,目前我使用EXPO ...

  10. React-Native(二):React Native开发工具vs code配置

    从网上翻阅了一些开发react-native的开发工具时,发现其实可选的工具还是比较多的Sublime Text,WebStrom,Atom+Nuclide,vs code 等.因为我用.net生态环 ...

随机推荐

  1. 【Azure Storage Account】利用App Service作为反向代理, 并使用.NET Storage Account SDK实现上传/下载操作

    问题描述 在使用Azure上的存储服务 Storage Account 的时候,有时需要代替 它原本提供的域名进行访问,比如默认的域名为:mystorageaccount.blob.core.chin ...

  2. TypeError: 'NoneType' object is not iterable 一次错误场景

    TypeError: 'NoneType' object is not iterable 源码 def get_url(lines): urls=[] for line in lines: if 'i ...

  3. Open-Sora 2.0 重磅开源!

    潞晨科技正式推出 Open-Sora 2.0 -- 一款全新开源的 SOTA 视频生成模型,仅 20 万美元(224 张 GPU)成功训练商业级 11B 参数视频生成大模型.开发高性能的视频生成模型通 ...

  4. 第十八届全国大学生信息安全竞赛暨第二届“长城杯”铁人三项赛web方向部分wp

    第十八届全国大学生信息安全竞赛暨第二届"长城杯"铁人三项赛web方向部分wp hello_web 查看源代码发现有两个文件,访问一下 Tips是phpinfo 里面可以看到disa ...

  5. 怎么给EXE文件加启动参数

    第一步 首先右键单击 exe 文件文件,创建 exe 文件的快捷方式. 第二步 右键单击此快捷方式--属性. 在快捷方式属性界面,点击目标后面的链接. 先打一个空格然后输入参数,然后点击应用按钮.确定 ...

  6. mysql grant 用户权限

    用户添加授权 mysql> grant all privileges on *.* to 'niuben'@'%' identified by '123456' with grant optio ...

  7. Creo 4.0二次开发工具框架搭建

    一.新建MFC DLL工程  二.配置项目属性 附加依赖项中输入:netapi32.lib;psapi.lib;mpr.lib;wsock32.lib;protk_dll_NU.lib;protk_d ...

  8. 同一局域网下,远程连接另一台电脑的Mysql数据库

    博客地址:https://www.cnblogs.com/zylyehuo/ 参考链接 同一局域网,远程连接别人的Mysql数据库 用电脑A去远程电脑B的数据库,那我们要先在电脑B上设置一下: ste ...

  9. docker 版本号说明

    17.03 版本以前 Docker CE 在 17.03 版本之前叫 Docker Engine, 版本说明参考这里 => Docker Engine release notes, 可以看到 D ...

  10. ANSYS 启动窗口过大问题解决

    方法总结(省流版):选择兼容性下更改高 DPI 设置 => 勾选高DPI 缩放代替 ,且其下对应应用程序选项 1.环境 系统环境:Windows 11 设备情况:分辨率 1920×1080:缩放 ...