鸿蒙NEXT(五):鸿蒙版React Native架构浅析
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; overflow-x: hidden; color: rgba(43, 43, 43, 1); font-family: -apple-system, system-ui, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; background-image: linear-gradient(90deg, rgba(159, 219, 252, 0.15) 3%, rgba(0, 0, 0, 0) 0), linear-gradient(1turn, rgba(159, 219, 252, 0.15) 3%, rgba(0, 0, 0, 0) 0); background-size: 20px 20px; background-position: center }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { padding: 30px 0; margin-top: 35px; margin-bottom: 10px; color: rgba(77, 208, 225, 1) }
.markdown-body h1 { font-size: 30px; text-align: center; position: relative; width: max-content; margin: 0 auto }
.markdown-body h1:before { position: absolute; content: ""; z-index: -1; top: -20px; height: 100%; width: 100px; left: 0; right: 0; margin: 0 auto; background: url("data:image/png;base64,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") center / 64px 64px no-repeat; opacity: 0.84 }
.markdown-body h1:after { position: absolute; content: ""; width: 150%; left: -25%; height: 50%; bottom: 12px; border-radius: 50%; background: linear-gradient(rgba(0, 0, 0, 0) 80%, rgba(77, 208, 225, 0.8)); opacity: 0.6; animation: 6s linear infinite h1animate }
@keyframes h1Animate { 0% { background-position: right bottom } 50% { background-position: right } 100% { background-position: right bottom } }
.markdown-body h2 { display: block; border-bottom: 4px solid rgba(77, 208, 225, 1); position: relative; font-size: 24px; padding: 12px 32px; margin: 30px 0 }
.markdown-body h2:before { width: 24px; height: 24px; left: 0; top: 0; margin: auto; background-size: 24px 24px; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADGklEQVRYR81X32vTYBQ999s6mFjQgQ+DrbHiVFZYU4cDcQ/6pGhTFVYFEXGi82H+Bz448UnEF1Fx9ccEEcXpZE3d5tP2ooKiTacTHaLNpigMHDgnU9tcSbrWrkwWR0sbyEOSe885ObnfvV8IRT6oyPwoLQHBx+OVM5WJvSyEVAhnBOjt7yU/+/rr6r6l8TMO+F/EN0JQhICqQpD/xaRpcpAc9tS+M+9lBCia/oqBamK+zeDuQogQZaKJk3wcQjxSva7tGQGB2Ke1zIk3DNyMyNL+QpCnMQOaPsDAVuGAp9cjvbYc8Ec/bCYSg0zoiHilk1tHxqsqEsYlML4kjIpT/eurJxRNPweQU5VdrWaOEo1fgKAVbBgXIz73kF3R/ph+ghgdzMYWM29eAWlBJqgZaFlFYtC6nhWpaDqnSGlIlV1WjJ3DloDNgyNLncudqgX//Ucg3LxuStHGuhi8pqKCW3rqV342rwFjRznKm+/LNaN2yC237ThgF2wxcfMLeP6+ncrKzoPoKTGeLQbYbg4TNoC5iZPJY5HGVRdSNZAWYBclD3FzBQzrR8hACAKdzBzKA/4/IYioDQaOskBbpEG6PO8qKKSAEi3CnEb0Pw4oMf0OmKbTDWqh3Lw6EIiNBZi5lxh3wz4puBD5ovqAMvxhHSdFKxE1CQe3m/07TeTX4lcJdAhE+1Sv65Z5P/ByvIGTRowIZ9igbtXnmrOsbTvgj+kHBNMuBu9OdVw8EeU4nC1A0cYmAHZOTRrLhra4Z8ywnSN6vZHAFTA2WnnMfQB3qz73ddsOZM8CACFDIPSgQXqebXEgqgeZcAeEe6pXasm1f8ew3igMtAHWac0Uc/jYdyAaP0xEBwFsmgUPqbJ0NE2UKj4EGcahiOzuyhagaHpnmtgcVgTcCMuua7YdyAHbA3ArQNscVFbb4635aD6fnYaTvxxi9UNP7ddMXaRWVBdAcaLk6bDXPZCNZ9uBXEsDUX1T2Cc9yjig6Z0EHg3LK8/aqf6MwJKchkXfks1+0+JtSq3qLPa23BRR1B+T/6nkfMaW1r9hPt/MLtYfTLEpP+T9FNoAAAAASUVORK5CYII=") }
.markdown-body h2:after, .markdown-body h2:before { content: ""; display: block; position: absolute; bottom: 0 }
.markdown-body h2:after { right: 0; width: 400px; height: 10px; border-top-right-radius: 24px; background: linear-gradient(90deg, rgba(255, 255, 255, 1), rgba(77, 208, 225, 1)); max-width: 50vw }
.markdown-body h3 { margin: 30px 0; font-size: 18px; position: relative; padding: 4px 32px; width: max-content }
.markdown-body h3:before { border-bottom: 2px solid rgba(77, 208, 225, 1); width: 100%; content: ""; display: block; height: 28px; position: absolute; left: 0; top: 0; bottom: -2px; margin: auto; background-size: 28px 28px; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABRklEQVRYR2NkGGDAOMD2M4w6YDQERkNg+ITAppcfY/8zMv3wF+NdTUrZQpUQ2PT6cz8Dw/8CkMWMDIwNvqK8jcQ6gmIHNN19EaXPx1XPyMCghrCUKcpPlGc5MY6gyAE+Fx52MjL8j3cU5a1UYWXtZGBkEAVb+p8hxU+Mby5NHQCxnKEMaskzJ37uFmUetkmMjAzrfUX4woixHBJlZAA0y2EmPPYU4enLkhGeQIqRJDsAh+UgO7duNpD3IcVykkOA2paT5ABaWE60A2hlOdEO8D3/4CMDIyMfWvySFefoaYSoROh74eFXBgYGLiTNVLGc+BC48PAnAwMDG9QBVLOcaAd8P5ox+x/jf5AjGLgYfnwnKqv9/8/PwPO/kFF/MSj0cAKiouD/0bgYoixFU8RovWgJIX1EOYCQIZTIjzpgNARGQ2DAQwAAvHBaIdB7zxsAAAAASUVORK5CYII="); background-repeat: no-repeat; animation: 2s infinite alternate h3animationbefore }
@keyframes h3AnimationBefore { 0% { width: 28px } 25% { width: 100% } 50% { width: 100% } 100% { width: 100% } }
.markdown-body h3:after { content: ""; display: block; width: 28px; height: 28px; position: absolute; border: 2px solid rgba(77, 208, 225, 1); border-radius: 50%; right: -15px; top: 0; bottom: 0; margin: auto; background-size: 28px 28px; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABRklEQVRYR2NkGGDAOMD2M4w6YDQERkNg+ITAppcfY/8zMv3wF+NdTUrZQpUQ2PT6cz8Dw/8CkMWMDIwNvqK8jcQ6gmIHNN19EaXPx1XPyMCghrCUKcpPlGc5MY6gyAE+Fx52MjL8j3cU5a1UYWXtZGBkEAVb+p8hxU+Mby5NHQCxnKEMaskzJ37uFmUetkmMjAzrfUX4woixHBJlZAA0y2EmPPYU4enLkhGeQIqRJDsAh+UgO7duNpD3IcVykkOA2paT5ABaWE60A2hlOdEO8D3/4CMDIyMfWvySFefoaYSoROh74eFXBgYGLiTNVLGc+BC48PAnAwMDG9QBVLOcaAd8P5ox+x/jf5AjGLgYfnwnKqv9/8/PwPO/kFF/MSj0cAKiouD/0bgYoixFU8RovWgJIX1EOYCQIZTIjzpgNARGQ2DAQwAAvHBaIdB7zxsAAAAASUVORK5CYII="); animation: 2s infinite alternate h3animationafter }
@keyframes h3AnimationAfter { 0% { } 10% { } 50% { transform: rotate(-1turn) } 100% { transform: rotate(-1turn) } }
.markdown-body h4 { font-size: 16px }
.markdown-body h5 { font-size: 15px }
.markdown-body h6 { margin-top: 5px }
.markdown-body p { line-height: inherit; margin: 22px 0; letter-spacing: 2px; font-size: 14px; word-spacing: 2px }
.markdown-body img { max-width: 80%; border-radius: 6px; display: block; margin: 20px auto !important; object-fit: contain; box-shadow: 0 0 16px rgba(110, 110, 110, 0.45) }
.markdown-body figcaption { display: block; font-size: 13px; color: rgba(43, 43, 43, 1) }
.markdown-body figcaption:before { content: ""; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAGFBMVEVHcExAuPtAuPpAuPtAuPpAuPtAvPxAuPokzOX5AAAAB3RSTlMAkDLqNegkoiUM7wAAAGBJREFUKM9jYBhcgMkBTUDVBE1BeDGqEtXychNUBeXlKEqACsrLQxB8lnCQQClCiWt5OYoSiAIkJVAF5eVBqAqAShTAAs7l5ShKWMwRAmAlSArASpAVgJUkCqIAscESHwCVVjMBK9JnbQAAAABJRU5ErkJggg=="); display: inline-block; width: 18px; height: 18px; background-size: 18px; background-repeat: no-repeat; background-position: center; margin-right: 5px; margin-bottom: -5px }
.markdown-body hr { border-top: 1px solid rgba(77, 208, 225, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body del { color: rgba(77, 208, 225, 1) }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(77, 208, 225, 0.08); color: rgba(38, 198, 218, 1); padding: 0.195em 0.4em }
.markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace; overflow: auto; position: relative; line-height: 1.75; box-shadow: 0 0 8px rgba(110, 110, 110, 0.45); border-radius: 4px; margin: 16px }
.markdown-body pre:before { content: ""; display: block; height: 30px; width: 100%; margin-bottom: -7px; background: url("data:image/png;base64,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") 10px 10px / 40px no-repeat }
.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 { color: rgba(77, 208, 225, 1); border-bottom: 1px solid rgba(77, 208, 225, 1); font-weight: 400; text-decoration: none; margin: 0 4px }
.markdown-body a:active, .markdown-body a:hover { background-color: rgba(77, 208, 225, 0.1) }
.markdown-body strong { color: rgba(38, 198, 218, 1) }
.markdown-body strong:before { content: "「" }
.markdown-body strong:after { content: "」" }
.markdown-body em { font-style: normal; color: rgba(77, 208, 225, 1); font-weight: 700 }
.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(77, 208, 225, 0.05) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { margin: 2em 0; padding: 24px 32px; border-left: 4px solid rgba(38, 198, 218, 1); background: rgba(77, 208, 225, 0.15); position: relative }
.markdown-body blockquote:before { content: "❝"; top: 8px; left: 8px; color: rgba(77, 208, 225, 1); font-size: 30px; line-height: 1; font-weight: 700; position: absolute; opacity: 0.7 }
.markdown-body blockquote:after { content: "❞"; font-size: 30px; position: absolute; right: 8px; bottom: 0; color: rgba(77, 208, 225, 1); opacity: 0.7 }
.markdown-body blockquote p { color: rgba(89, 89, 89, 1); line-height: 2 }
.markdown-body ol, .markdown-body ul { color: rgba(89, 89, 89, 1); 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 }
@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 { background: rgba(30, 30, 30, 1); color: rgba(220, 220, 220, 1) }
.hljs-keyword, .hljs-link, .hljs-literal, .hljs-name, .hljs-symbol { color: rgba(86, 156, 214, 1) }
.hljs-link { text-decoration: underline }
.hljs-built_in, .hljs-type { color: rgba(78, 201, 176, 1) }
.hljs-class, .hljs-number { color: rgba(184, 215, 163, 1) }
.hljs-meta-string, .hljs-string { color: rgba(214, 157, 133, 1) }
.hljs-regexp, .hljs-template-tag { color: rgba(154, 83, 52, 1) }
.hljs-formula, .hljs-function, .hljs-params, .hljs-subst, .hljs-title { color: rgba(220, 220, 220, 1) }
.hljs-comment, .hljs-quote { color: rgba(87, 166, 74, 1); font-style: italic }
.hljs-doctag { color: rgba(96, 139, 78, 1) }
.hljs-meta, .hljs-meta-keyword, .hljs-tag { color: rgba(155, 155, 155, 1) }
.hljs-template-variable, .hljs-variable { color: rgba(189, 99, 197, 1) }
.hljs-attr, .hljs-attribute, .hljs-builtin-name { color: rgba(156, 220, 254, 1) }
.hljs-section { color: rgba(255, 215, 0, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }
.hljs-bullet, .hljs-selector-attr, .hljs-selector-class, .hljs-selector-id, .hljs-selector-pseudo, .hljs-selector-tag { color: rgba(215, 186, 125, 1) }
.hljs-addition { background-color: rgba(20, 66, 18, 1) }
.hljs-addition, .hljs-deletion { display: inline-block; width: 100% }
.hljs-deletion { background-color: rgba(102, 0, 0, 1) }
鸿蒙版React Native架构
如图,React Native for OpenHarmony 在 React Native 的新架构(0.68以及之后的版本)的基础上,进行了鸿蒙化的适配。按照功能可以进行如下的划分:
- RN 应用代码:开发者实现的业务代码。
- RN 库代码:在 React Native 供开发者使用的组件和API的封装与声明。
- JSI(JavaScript Interface):JavaScript 与 CPP 之间进行通信的API。
- React Common:所有平台通用的 CPP 代码,用于对 RN 侧传过来的数据进行预处理。
- OpenHarmony 适配代码:接收并处理 React Common 传过来的数据,对接原生的代码,调用 ArkUI 的原生组件与 API。主要包括了两个部分:分别是 TurboModule 与 Fabric。
- OS代码:对接系统底层功能,根据适配层代码传过来的数据进行渲染,或完成对应的功能。
React Native库代码
在现行的 React Native 中,有很多属性是在React侧完成的封装,也有很多属性是平台独有的。为了达成这个效果,React Native 在JS侧根据Platform增加了很多判断。所以,React Native 的鸿蒙化适配也需要增加HarmonyOS相关的平台判断,与相应的组件属性的封装。为此,鸿蒙化团队提供了react-native-harmony的tgz包,并通过更改metro.config.js配置,将该tgz包应用到 Metro Bundler中。
React Native 还提供了很多库的封装,例如Codegen、打包工具等。为此,鸿蒙化团队提供了react-native-harmony-cli的包,对这些库进行了HarmonyOS平台的适配,用于向开发者提供相关的功能。
Fabric
Fabric 是 React Native 的组件渲染系统。接收 React Native 传过来的组件信息,处理后发送给原生OS,由OS完成页面的渲染。
在适配方案中,组件不通过复杂的流程对接到ArkUI的声明式范式上,而是直接使用XComponent对接到ArkUI的后端接口进行渲染,缩短了流程,提高了组件渲染的效率。C-API的性能收益包括以下的几个部分:
- C端最小化、无跨语言的组件创建和属性设置;
- 无跨语言前的数据格式转换,不需要将string,enum等数据类型转换为object,可以在CPP侧直接使用对应的数据进行处理;
- 可以进行属性Diff,避免重复设置,降低了属性设置的开销。
渲染流水线请参考渲染三阶段。
TurboModule
TurboModule 是 React Native 中用于 JavaScript 和原生代码进行交互的模块,为RN JS应用提供调用系统能力的机制。根据是否依赖 HarmonyOS系统相关的能力,可以分为两类:cxxTurboModule和ArkTSTurboModule。
- ArkTSTurboModule:
- ArkTSTurboModule为 React Native 提供了调用ArkTS原生API的方法。可以分为同步与异步两种。
- ArkTSTurboModule依赖NAPI进行原生代码与CPP侧的通信。包括JS与C之间的类型转换,同步和异步调用的实现等。
- cxxTurboModule:
- cxxTurboModule主要提供的是不需要系统参与的能力,例如NativeAnimatedTurboModule主要提供了数据计算的相关能力。
- cxxTurboModule不依赖于系统的原生API,为了提高相互通信的效率,一般是在cpp侧实现,这样可以减少native与cpp之间的通信次数,提高性能。
React Native线程模型
RNOH线程模型
RNOH的线程一共有3个:
enum TaskThread {
MAIN = 0, // main thread running the eTS event loop
JS, // React Native's JS runtime thread
BACKGROUND, // background tasks queue
};
MAIN/UI线程
RN业务主线程,也是应用主线,应用UI线程。该线程在应用中有唯一实例。
RN在MAIN线程中主要承担的业务功能是:
- ArkUI组件的生命周期管理:CREATE, UPDATE, INSERT, REMOVE, DELETE;
- ArkUI组件树管理;
- RN TurboModule业务功能运行;
- 交互事件、消息处理。
JS线程
JS线程通过虚拟机执行React(JS)代码,通过React代码与RN Common的核心代码交互完成React Native的Render阶段任务。
RN在JS线程中主要承担的业务功能是:
- 加载Bundle,执行Bundle依赖的React代码和Bundle的业务代码。
- 由React业务代码驱动,创建RN ShadowTree,设置ShadowTree的属性。
- 利用Yoga引擎进行组件布局,文本测量和布局。
- 比较完成布局的新、老ShadowTree,生成差异结果mutations。将mutations提交到MAIN线程触发Native的显示刷新。
- 交互事件、消息处理。
JS线程与RNInstance的实例绑定,有多个RNInstance,则有多个对应的JS线程。
BACKGROUND线程
BACKGROUND线程是RN的实验特性,开启BACKGROUND线程后,会将JS线程的部分布局、ShadowTree比较的任务迁移到该线程执行,从而降低JS线程的负荷。
由于开启BACKGROUND涉及复杂的线程间通信,在稳定性方面带来风险,因此正式商用版本中不要开启BACKGROUND线程。
RNOH线程的长期演进
MAIN线程和JS线程承担了RN框架的全部业务,在重载情况下可能会造成性能瓶颈。RN的业务也受同线程的其他应用代码的影响,造成执行延迟或阻塞等问题。
在长期演进时,可以考虑进行线程扩展:
- 增加唯一TM线程,将TurboModule的业务代码放到TM线程来执行,从而降低MAIN线程负荷。
- 增加单独的TIMER线程,确保时间基准稳定执行。
典型线程Trace图
- 线程号53130:MAIN线程
- 线程号53214:JS线程实例1
- 线程号53216:JS线程实例2
命令式组件
XComponent接入
CAPI 版本使用XComponent总共分成了两个步骤:
- createSurface的时候创建XComponentSurface;
- startSurface的时候将CPP的XComponentSurface连接到ArkUI的Xcomponent上。
createSurface的时候主要做了以下的操作:
- 创建并将XComponentSurface记录到Map中:
void RNInstanceCAPI::createSurface(
facebook::react::Tag surfaceId,
std::string const& moduleName) {
m_surfaceById.emplace(
surfaceId,
XComponentSurface(
···
surfaceId,
moduleName));
}
- 在XComponentSurface中创建rootView,用于挂载C-API的组件,并在Surface上统一处理Touch事件:
XComponentSurface::XComponentSurface(
···
SurfaceId surfaceId,
std::string const& appKey)
:
···
m_nativeXComponent(nullptr),
m_rootView(nullptr),
m_surfaceHandler(SurfaceHandler(appKey, surfaceId)) {
m_scheduler->registerSurface(m_surfaceHandler);
m_rootView = componentInstanceFactory->create(
surfaceId, facebook::react::RootShadowNode::Handle(), "RootView");
m_componentInstanceRegistry->insert(m_rootView);
m_touchEventHandler = std::make_unique<SurfaceTouchEventHandler>(m_rootView);
}
startSurface的时候主要做了以下的操作:
- 在ArkTS侧创建XComponent,并设置id,type与libraryname属性。其中:
- id:组件的唯一标识,又由InstanceID和SurfaceID共同组成,记录了此XComponent属于哪一个Instance与Surface;
- type:node,标识该XComponent是一个占位组件,组件的实现都在CAPI侧;
- libraryname:表示C-API组件在哪个so库中实现,并加载该so库,自动调用该so中定义的Init函数。当前React Native for OpenHarmony默认的so名字为rnoh_app。
XComponent({
id: this.ctx.rnInstance.getId() + "_" + this.surfaceHandle.getTag(),
type: "node",
libraryname: 'rnoh_app'
})
- 在CPP侧的Init中调用registerNativeXComponent函数,该函数中调用了OH_NativeXComponent_GetXComponentId用于获取ArkTS设置的id,并根据id找到对应的Instance与Surface。同时还要获取nativeXComponent对象,记录ArkTS侧的XComponent。
if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) !=
OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
···
}
std::string xcomponentStr(idStr);
std::stringstream ss(xcomponentStr);
std::string instanceId;
std::getline(ss, instanceId, '_');
std::string surfaceId;
std::getline(ss, surfaceId, '_');
- 调用OH_NativeXComponent_AttachNativeRootNode,将XComponentSurface中记录的rootView连接到ArkTS侧的XComponent上:
OH_NativeXComponent_AttachNativeRootNode(
nativeXComponent,
rootView.getLocalRootArkUINode().getArkUINodeHandle());
- 将rootView连接到XComponent后,rootView就作为CAPI组件的根节点,后续的子孙节点通过Mutation指令逐个插入到组件树上。
CAPI组件向上对接RN指令
- 在RN鸿蒙适配层中,SchedulerDelegate.cpp负责处理RN Common传递下来的指令。
void SchedulerDelegate::schedulerDidFinishTransaction(MountingCoordinator::Shared mountingCoordinator) {
...
}
- 在 MountingManagerCAPI.cpp 的didMount中对各个指令进行处理。
MountingManagerCAPI::didMount(MutationList const& mutations) {
...
}
在didMount函数中,先根据预先配置的arkTsComponentNames获取ArkTs组件和CAPI组件的指令,分别进行处理。其中CAPI组件的指令会在handleMutation方法中逐个遍历每个指令,根据指令的类型(Create 、Delete、Insert、Remove、Update)进行不同的处理。
- Create指令:接收到Create指令后,会根据指令的tag、componentName和componentHandle信息创建出一个对应组件类型的ComponentInstance,比如Image组件的Create指令,会创建对应的ImageComponentInstance。创建完组件之后,调用updateComponentWithShadowView方法设置组件的信息。其中,setLayout设置组件的布局信息,setEventEmitter设置组件的事件发送器,setState设置组件的状态,setProps设置组件的属性信息。
- Delete指令:根据接收到的Delete指令的tag,删除对应组件的ComponentInstance。
- Insert指令:根据接收到Insert指令中包含父节点的tag和子节点的tag,将子节点插入到对应的父节点上。
- Remove指令:接收到Remove指令中包含父节点的tag和子节点的tag,在父节点上移除对应的子节点。
- Update指令:接收到Update指令后,调用组件的setLayout、setEventEmitter、setState、setProps更新组件相关信息。
适配层事件分发逻辑
1.适配层事件的注册
当手势触碰屏幕后会命中相应的结点,通过回调发送对应事件,但是需要注册事件,如一个Stack节点注册了NODE_ON_CLICK事件。
StackNode::StackNode()
:ArkUINode(NativeNodeAPi::getInstance()->createNode(ArkUI_NodeType::ARKUI_NODE_STACK)),
m_stackNodeDelegate(nullptr)
{
maybeThrow(NativeNodeApi::getInstance()->registerNodeEvent(m_nodeHandle,NODE_ON_CLICK,0,this));
maybeThrow(NativeNodeApi::getInstance()->registerNodeEvent(m_nodeHandle,NODE_ON_HOVER,0,this));
}
SurfaceTouchEventHandler注册了NODE_TOUCH_EVENT事件。
SurfaceTouchEventHandler(
ComponentInstance::Shared rootView,
ArkTSMessageHub::Shared arkTSMessageHub,int rnInstanceId):
ArkTSMessageHub::Observer(arkTSMessageHub),
m_rootView(std::move(rootView)),
m_rnInstanceId(rnInstanceId)
{
ArkUINodeRegistry::getInstance().registerTouchHandler(
&m_rootView->getLocalRootArkUINode(),this);
NativeNodeApi::getInstance()->registerNodeEvent(
m_rootView->getLocalRootArkUINode().getArkUINodeHandle(),
NODE_TOUCH_EVENT,
NODE_TOUCH_EVENT,
this);
}
2.适配层事件的接收
ArkUINodeRegistry的构造中注册了一个回调,当注册了事件的节点被命中后,该事件通过回调传递处理。
ArkUINodeRegistry::ArkUINodeRegistry(ArkTSBridge::Shared arkTSBridge):m_arkTSBridge(std::move(arkTSBridge))
{
NativeNodeApi::getInstance()->registerNodeEventReceiver(
[](ArkUI_NodeEvent* event){
ArkUINodeRegistry::getInstance().receiveEvent(event);
});
}
3.适配层事件的处理
回调传递的参数event通过OH_ArkUI_NodeEvent_GetEventType获取事件类型,通过OH_ArkUI_NodeEvent_GetNodeHandle获取触发该事件的结点指针。
auto eventType = OHArkUI_NodeEvent_GetEventType(event);
auto node = OH_ArkUI_NodeEvent_GetNodeHandle(event);
首先判断事件类型是否为Touch事件,如果是,就从一个存储了所有TouchEventHandler的Map中通过结点指针作为key去查找对应的TouchEventHandler,如果没找到,这次Touch事件不处理。
if(eventType == ArkUI_NodeEventType::NODE_TOUCH_EVENT)
{
auto it = m_touchHandlerByNodeHandle.find(node);
if(it == m_touchHandlerByNodeHandle.end())
{
return;
}
}
如果找到了对应的TouchEventHandler,通过OH_ArkUI_NodeEvent_GetInputEvent获取输入事件指针,若输入事件指针不为空,通过OH_ArkUI_UIInputEvent_GetType判断输入事件指针的类型是否为Touch事件,如果不是,这次Touch事件不处理。
auto inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
if(inputEvent == nullptr || OH_ArkUI_UIInputEvent_GetType(inputEvent) != ArkUI_UIInputEvent_Type::ARKUI_UIINPUTEVENT_TYPE_TOUCH)
{
return;
}
如果上述两个条件都满足,就通过TouchEventHandler去处理Touch事件。
it->second->onTouchEvent(inputEvent);
如果事件类型不为Touch事件,就从一个存储了所有ArkUINode的Map中通结点指针作为key去查找对应的ArkUINode,若未找到,这次事件不处理。
auto it = m_nodeByHandle.find(node);
if(it == m_nodeByHandle.end())
{
return;
}
如果找了对应的ArkUINode,通过OH_ArkUI_NodeEvent_GetNodeComponentEvent获取组件事件指针,该指针的data字段保留了arkUI传递过来的参数,并通过ArkUINode处理该事件。
auto commponentEvent = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
if(commponentEvent != nullptr)
{
it->second->onNodeEvent(eventType,compenentEvent->data);
return;
}
4.Touch事件的传递给JS侧
上文中写明TouchEventHandler对Touch事件进行处理,以xcomponentSurface举例,xcomponentSurface有一个继承了TouchEventHandler的成员变量,这个成员变量通过dispatchTouchEvent处理这次Touch事件。
void onTouchEvent(ArkUI_UIInputEvent* event)override
{
m_touchEventDispatcher.dispatchTouchEvent(event,m_rootView);
}
对于Touch事件首先通过Touch的位置等因素,获取对应touchTarget(每个componentInstance就是一个touchTarget,下图的名字是eventTarget)。
class ComponentInstance:public TouchTarget,public std::enable_shared_from_this<ComponentInstance>
for(auto const& targetTouches:touchByTargetId)
{
auto it = m_touchTargetByTouchId.find(targetTouches.second.begin()->identifier);
if(it == m_touchTargetByTouchId.end())
{
continue;
}
auto eventTarget = it->second.lock();
if(eventTarget == nullptr)
{
m_touchTargetByTouchId.erase(it);
continue;
}
}
然后通过componentInstance保存的m_eventEmitter发送对应的事件给js侧,从而触发页面的刷新等操作。 Touch事件有以下四种类型:
- UI_TOUCH_EVENT_ACTION_DOWN
- UI_TOUCH_EVENT_ACTION_MOVE
- UI_TOUCH_EVENT_ACTION_UP
- UI_TOUCH_EVENT_ACTION_CANCEL
switch(action)
{
case UI_TOUCH_EVENT_ACTION_DOWN:
eventTarget->getTouchEventEmitter()->onTouchStart(touchEvent);
break;
case UI_TOUCH_EVENT_ACTION_MOVE:
eventTarget->getTouchEventEmitter()->onTouchMove(touchEvent);
break;
case UI_TOUCH_EVENT_ACTION_UP:
eventTarget->getTouchEventEmitter()->onTouchEnd(touchEvent);
break;
case UI_TOUCH_EVENT_ACTION_CANCEL:
default:
eventTarget->getTouchEventEmitter()->onTouchCancel(touchEvent);
break;
}
5、非Touch事件的传递给js侧
上文中写明,非Touch事件由ArkUINode处理,对于每个继承了ArkUINode的类,重载了onNodeEvent方法,以StackNode举例,说明RN适配层是如何区分Click事件和Touch事件。前文说明,StackNode注册了Click事件,所以通过回调,会走到StackNode的onNodeEvent部分,这里会先判断这个事件类型,这里是NODE_ON_CLICK类型,符合要求,但是对于第二个条件eventArgs[3].i32(即上文描述的arkUI传递过来的参数),如果是触屏手机,其值为2不满足eventArgs[3].i32 != 2的条件。
void StackNode::onNodeEvent(ArkUI_NodeEventType eventType,EventArgs& eventArgs)
{
if(eventType == ArkUI_NodeEventType::NODE_ON_CLICK && eventArgs[3].i32 != 2)
{
onClick();
}
if(eventType == ArkUI_NodeEventType::NODE_ON_HOVER)
{
if(m_stackNodeDelegate != nullptr)
{
if(eventArgs[0].i32)
{
m_stackNodeDelegate->onHoverIn();
}else
{
m_stackNodeDelegate->onHoverOut();
}
}
}
}
所以此时实际上不会触发Click的事件,因此Touch事件和Click事件不会冲突。如果触发了Click事件,StackNode会通过代理StackNodeDelegate发送事件。
void StackNode::onClick()
{
if(m_stackNodeDelegate != nullptr)
{
m_stackNodeDelegate->onClick();
}
}
其中ViewComponentInstance继承了StackNodeDelegate,所以实际上走的是ViewComponentInstance的onClick函数。
namespace rnoh
{
class ViewComponentInstance
:public CppComponentInstance<facebook::react::ViewShardowNode>,public StackNodeDelegate
{
}
}
这个函数通过ViewComponentInstance的m_eventEmitter发送事件给JS,从而触发页面的刷新。
void ViewComponentInstance::onClick()
{
if(m_eventEmitter != nullptr)
{
m_eventEmitter->dispatchEvent("click",[=](facebook:jsi::Runtime& runtime)
{auto payload = facebook::jsi::Object(runtime);
return payload;
});
}
}
鸿蒙版React Native启动流程
鸿蒙RN启动阶段分为RN容器创建、Worker线程启动、NAPI方法初始化、RN实例创建四个阶段,接下来加载bundle和界面渲染,类图如下所示:
React Native容器创建
EntryAbility
全局Ability,App的启动入口。
Index.ets
App页面入口。
RNApp.ets
- 配置appKey,和JS侧registerComponent注册的appKey关联;
- 配置初始化参数initialProps,传递给js页面;
- 配置jsBundleProvider,指定bundle加载路径;
- 配置ArkTS混合组件wrappedCustomRNComponentBuilder;
- 配置rnInstanceConfig,指定开发者自定义package,注入字体文件fontResourceByFontFamily,设置BG线程开关,设置C-API开关;
- 持有RNSurface,作为RN页面容器。
RNSurface.ets
RN页面容器,持有XComponent用于挂载ArkUI的C-API节点和响应手势事件。
Worker线程启动
TurboModule运行在worker线程,worker线程是在程序启动时创建。
WorkerThread.ts
EntryAbility创建时会创建RNInstancesCoordinator,RNInstancesCoordinator的构造函数中获取worker线程类地址,然后调用WorkerThread的create方法启动worker线程,如下:
const workerThread = new WorkerThread(logger, new worker.ThreadWorker(scriptUrl, { name: name }), onWorkerError)
RNOHWorker.ets
WorkerThread中配置的scriptUrl即RNOHWorker.ets路径,RNOHWorker.ets内部调用setRNOHWorker.ets的setRNOHWorker方法配置worker线程收发消息通道。
setRNOHWorker.ets
setRNOHWorker方法配置worker线程收发消息通道,createTurboModuleProvider方法注册系统自带和开发者自定义的运行在worker线程的TurboModule。
NAPI方法初始化
- RNOHAppNapiBridge.cpp
Init方法是静态方法,在程序启动时调用,配置了18个ArkTS调用C++的方法,如下:
registerWorkerTurboModuleProvider,
getNextRNInstanceId,
onCreateRNInstance, // 创建RN实例
onDestroyRNInstance, // 销毁RN实例
loadScript, // 加载bundle
startSurface,
stopSurface,
destroySurface,
createSurface, // 创建RN界面
updateSurfaceConstraints,
setSurfaceDisplayMode,
onArkTSMessage,
emitComponentEvent, // 给RN JS发消息
callRNFunction,
onMemoryLevel,
updateState,
getInspectorWrapper,
getNativeNodeIdByTag
- NapiBridge.ts
ArkTS侧RNInstance.ts、SurfaceHandle.ts调用C++的桥梁。
React Native实例创建
在RNInstance.ts中创建RN实例,分为以下步骤:
- 获取RNInstance的id:在RNInstanceRegistry.ets中通过NAPI调用getNextRNInstanceId方法获取。
- 注册ArkTS侧TurboModule:在RNInstance.ts中调用processPackage方法注册系统自带和开发者自定义的运行在UI线程上的TurboModule。
- 注册字体:在RNInstanceFactory.h中调用FontRegistry.h的registerFont方法注册应用侧扩展字体,接着通过图形接口注入字体信息。
- 注册RN官方能力和开发者自定义能力:RNInstanceFactory.h中通过PackageProvider.cpp的getPackage方法获取RN系统自带和开发者自定义TurboModule,接着注册系统View、系统自带TurboModule、开发者自定义View、开发者自定义TurboModule。
- 注册ArkTS混合组件:在RNInstanceFactory.cpp中注册ArkTS侧传递到C++的ArkTS组件。
- 初始化JS引擎:在RNInstanceInternal.cpp中初始化JS引擎Hermes或者JSVM,通过JS引擎驱动JS消息队列。
- 注册TM的JSI通道:在RNInstanceCAPI.cpp中调用createTurboModuleProvider创建TurboModuleProvider,注入__turboModuleProxy对象给JS侧。
- 注入Scheduler:在RNInstanceInternal.cpp中初始化Fabric的Scheduler对象,ReactCommon的组件绘制找到鸿蒙适配层注入的SchedulerDelegate才能进行界面绘制。
- 注册Fabric的JSI通道:在RNInstanceInternal.cpp中调用UIManagerBinding.cpp的createAndInstallIfNeeded方法注入nativeFabricUIManager对象给JS侧。
加载bundle
RN实例创建完毕则开始加载bundle,如下:
ArkTS侧加载bundle、C++侧加载bundle,切线程到ReactCommon的Instance.cpp中加载bundle:
RNApp.ets > RNInstance.ts > RNOHAppNapiBridge.cpp > RNInstanceInternal.cpp > Instance.cpp
总结
本文详细介绍了鸿蒙版 React Native 架构。包括按功能划分的架构组成,如 RN 应用代码、库代码、JSI、React Common、OpenHarmony 适配代码及 OS 代码等。还阐述了 Fabric、TurboModule、线程模型、命令式组件、启动流程等方面内容。启动流程分为 RN 容器创建、Worker 线程启动、NAPI 方法初始化、RN 实例创建及加载 bundle 等阶段。整体架构复杂且功能明确,为开发者提供了在鸿蒙平台上使用 React Native 的技术支持。
鸿蒙NEXT(五):鸿蒙版React Native架构浅析的更多相关文章
- React Native 架构演进
写在前面 上一篇(React Native 架构一览)从设计.线程模型等方面介绍了 React Native 的现有架构,本篇将分析这种架构的局限性,以及 React Native 正在进行的架构升级 ...
- React Native 架构一览
一.架构设计 整体上分为三大块,Native.JavaScript 与 Bridge: Native 管理 UI 更新及交互,JavaScript 调用 Native 能力实现业务功能,Bridge ...
- React-Native(五):React Native之Text学习
本章节主要学习Text的布局,仿照网易新网: 代码: /** * Sample React Native App * https://github.com/facebook/react-native ...
- NodeJS笔记(五) 使用React Native 创建第一个 Android APP
参考:原文地址 几个月前官方推出了快速创建工具包,由于对React Native不熟悉这里直接使用这2个工具包进行创建 1. create-react-native-app(下文简称CRNA): 2. ...
- Hybrid App 和 React Native 开发那点事
简介:Hybrid App(混合模式移动应用)开发是指介于Web-app.Native-App这两者之间的一种开发模式,兼具「Native App 良好用户交互体验的优势」和「Web App 跨平台开 ...
- React Native开发 - 搭建React Native开发环境
移动开发以前一般都是原生的语言来开发,Android开发是用Java语言,IOS的开发是Object-C或者Swift.那么对于开发一个App,至少需要两套代码.两个团队.对于公司来说,成本还是有的. ...
- 【React Native开发】React Native For Android环境配置以及第一个实例(1)
年9月15日也公布了ReactNative for Android,尽管Android版本号的项目公布比較迟,可是也没有阻挡了广大开发人员的热情.能够这样讲在2015年移动平台市场上有两个方向技术研究 ...
- reactNative-解决react native使用fetch函数 Network request failed 问题
解决react native使用fetch函数Network request failed问题 最近公司新开发一个app, 用react native架构好后,用xcode模拟器打开app,对接登陆接 ...
- 1000 千米高空俯瞰 React Native
一.历史:React Native 从开始到现在 React Native 的定位是通过 React 构建原生 App: A framework for building native apps wi ...
- React Native之新架构中的Turbo Module实现原理分析
有段时间没更新博客了,之前计划由浅到深.从应用到原理,更新一些RN的相关博客.之前陆续的更新了6篇RN应用的相关博客(传送门),后边因时间问题没有继续更新.主要是平时空余时间都用来帮着带娃了,不过还是 ...
随机推荐
- [SCOI2016] 幸运数字 题解
\(xor\) 最大值想到线性基,路径想到 \(lca\) 和树链剖分,由于没有修改用 \(lca\) 就可以.先用处理 \(fa\) 数组的方式处理倍增线性基(自然是得用线性基合并的),在求 \(l ...
- [TJOI2019] 甲苯先生的字符串 题解
有点水了-- 考虑相邻的不能放在一起,不相邻的可以,那么很容易想到转移方程: \[dp_{i,j}=\sum_{k=0}^{25}dp_{i-1,k}[j,k不相邻] \] 其中 \(dp_{i,j} ...
- Arduino 语法--数组
数组是一种可访问的变量的集合.Arduino的数组是基于C语言的,实现起来虽然有些复杂,但使用却很简单. 一.创建或声明一个数组 数组的声明和创建与变量一致,下面是一些创建数组的例子. arrayIn ...
- cgroup与systemd: 通过src rpm获取systemd源代码,添加日志并使用rpmbuild重新打包
问题起源 服务跑在富容器中.容器使用init进程作为一号进程,然后用systemd管理所有service. 在做一次升级时,nginx启动脚本有更新,原来是root拉起,现在进行了去root改造,使用 ...
- Docker - 在docker中部署Nginx
1.docker search 查找ngix 2.docker pull下载镜像 3.查看镜像列表 4.docker run启动容器 5.测试nginx容器是否启动成功 1.docker search ...
- 终于写完轮子一部分:tcp代理 了,记录一下
24年终自己立了flag: 25年做些轮子玩(用于浪费生命,赚不了钱) 所以25年就准备用c#写一个网络代理NZOrz(nginx知道吧,就那玩意儿干的事),包含 udp/tcp/http1 2 3, ...
- nginx 部署vue http、https
nignx配置文件 server { listen 80; server_name your_domain.com; return 301 https://$server_name$request_u ...
- mongodb logical sessions can't have multiple authenticated users
前言 使用 mongodb db.auth,切换用户时,报以下错误 logical sessions can't have multiple authenticated users 原因是 mongo ...
- dxTabbedMDIManager1关闭窗体
procedure TfrmJianKongXinXi.FormClose(Sender: TObject; var Action: TCloseAction);begin Action:=caFre ...
- 理解Rust引用及其生命周期标识(下)
在上一篇文章中,我们围绕 "引用必然存在来源" 这一基本概念,介绍了Rust中引用之间的关系,以及生命周期标记的实际意义.我们首先从最简单的单参数方法入手,通过示例说明了返回引用与 ...