混合开发架构|Android工程集成React NativeFlutterReactJs
Posted 学术袁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了混合开发架构|Android工程集成React NativeFlutterReactJs相关的知识,希望对你有一定的参考价值。
混合开发架构|android工程集成React Native、Flutter、ReactJs
架构设计说明
该篇文章,介绍并记录在大前端混合架构开发中的重要细节和流程。通过在安卓原生工程中集成两大主流混合框架React Native、Flutter,以及ReactJs[Vue],集成三类模块module的架构的混合设计。并分别在这些主流技术栈的业务创作中,自己造轮子、使用新颖架构设计及核心技术去实现。并在编码过程中还会创造常用工具,沉浸式状态栏、底部导航栏、Flutter热更新、Flutter多入口、
tab1 | tab2 | tab3 | tab4 | tab5 |
---|---|---|---|---|
仿招商银行首页 | 仿即时通讯 | 仿工商银行首页 | 仿抖音我的页面 | 仿唯品会分类 |
在原生工程中创建一个首页,在首页中使用五个TAB
。
TAB1
,使用原生Java+Kotlin编码,仿招商银行首页,使用优秀架构设计,完成列表各个模块的独立解耦。TAB2
,使用原生Java+Kotlin编码,仿微信,通过Android Socket实现IM的即时通讯。TAB3
,使用React Native编码,仿工商银行首页。TAB4
,使用Flutter编码,仿抖音我的页面。TAB5
,使用ReactJs编码,仿唯品会分类页面。
创建安卓原生工程
Android Studio版本 | gradle 插件版本 | gradle 版本 | kotlin版本 | JDK 版本 | 其他 |
---|---|---|---|---|---|
3.6 | 3.6.0 | gradle-6.7.1-all.zip(原5.6.4) | 1.5.31 | JDK11 | compileSdkVersion 31、buildToolsVersion “30.0.0” 、minSdkVersion 21、targetSdkVersion 30 |
创建Flutter
Android Studio版本 | gradle 插件版本 | gradle 版本 | JDK 版本 | 其他 |
---|---|---|---|---|
3.6 | 3.6.0 | gradle-5.6.4-all.zip | JDK11 | compileSdkVersion 31、buildToolsVersion “30.0.0” 、minSdkVersion 21、targetSdkVersion 30 |
集成嵌入原生工程
创建React Native
与创建Flutter相比较,React Native工程创建时,复杂很多。创建时候会遇到创建失败问题,成功创建后,启动Metro服务也会遇到报错问题。而这些问题都与nodejs版本
,React Naitve版本
有关。请详细阅读官方搭建环境的文档,
React Native 创建指令:
npx react-native init hibrid_rn --version 0.67.0
Metro服务启动指令:npx react-native start
Android apk 编译安装指令:yarn android
node版本 | React Native版本 | JDK 版本 | 说明 |
---|---|---|---|
v16.17.0 | 0.67.0 | JDK11 | 详细版本号,请移步查看代码 |
解决RN报错问题
若有报错,下面
如下报错信息,则是node版本号使用不当导致~
/node_modules/@react-native-community/cli/build/commands/doctor/healthchecks/index.js:48
catch
^
SyntaxError: Unexpected token
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:617:28)
at Object.Module._extensions…js (module.js:664:10)
… … …
若有报错,下面
Could not find react-native-0.71.0-rc.0-debug.aar (com.facebook.react:react-native:0.71.0-rc.0).
Could not determine the dependencies of task ':app:lintVitalRelease'.
> Could not resolve all artifacts for configuration ':app:debugCompileClasspath'.
> Could not find react-native-0.71.0-rc.0-debug.aar (com.facebook.react:react-native:0.71.0-rc.0).
解决方案, 指定ReactNative确定版本号!!修改ReactNative中app/build.gradle
下
implementation "com.facebook.react:react-native:+"
改为implementation "com.facebook.react:react-native:0.67.0"
若有报错,下面
Execution failed for task ':app:mergeDebugNativeLibs'.
More than one file was found with OS independent path 'lib/x86_64/libfbjni.so'
解决方案, 在app/build.gradle
android 中添加截图中报错的如lib/x86_64/libfbjni.so
packagingOptions
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/x86/libfbjni.so'
pickFirst 'lib/x86_64/libfbjni.so' // 截图中有这个报错,这里添加该so修复
pickFirst 'lib/armeabi-v7a/libfbjni.so'
pickFirst 'lib/arm64-v8a/libfbjni.so'
集成嵌入原生工程
// 【app/build.gradle】下进行配置
// 【配置共三步】rn第一步配置:start
project.ext.react = [
entryFile : "index.android.js",
enableHermes: false,
bundleInDebug:true,
bundleInBeta:true
]
def enableHermes = project.ext.react.get("enableHermes", false);
def jscFlavor = 'org.webkit:android-jsc:+'
def safeExtGet(prop, fallback)
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
// 【配置共三步】rn第一步配置:end
dependencies
implementation fileTree(dir: 'libs', include: ['*.jar'])
......
// 【配置共三步】rn第二步配置:start
if (enableHermes)
def hermesPath = "../../hibrid_rn/node_modules/hermesvm/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
else
implementation jscFlavor
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
// 【配置共三步】rn第二步配置:end
// 【project/build.gradle】下进行配置
allprojects
repositories
......
// 【配置共三步】rn第三步配置:start
maven
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/../hibrid_rn/node_modules/react-native/android"
maven
// Android JSC is installed from npm
url("$rootDir/../hibrid_rn/node_modules/jsc-android/dist")
//【配置共三步】rn第三步配置:end
参数字段说明:
entryFile : "index.android.js"
,表示配置加载安卓资源入口文件名称。def hermesPath = "../../hibrid_rn/node_modules/hermesvm/android/"
,表示当enableHermes==true
时,引入执行引擎Hermes。否则,使用javascriptCore执行引擎。
Hermes 是一个可选的 React Native 功能。如果要启用Hermes,需要确保 React Native项目的版本在
0.60.2版本
以上,并且还需要对android/app/build.gradle
做以下更改。我们这里配置enableHermes: false
project.ext.react = [
entryFile: "index.js",
enableHermes: true // 配置开启Hermes引擎
]
RN集成后,启动报错
在原生工程中集成了RN之后,将React Native作为原生工程Activity下LayoutView的一部分,测试集成状态。发现从Native启动React Native报以下错误~
ReactNative: Exception in native call java.lang.RuntimeException: Unable to load script. Make sure you're either running Metro (run 'npx react-native start') or that your bundle 'rn/index.android.bundle' is packaged correctly for release.
报错信息说,Metro服务未启动,或者说找不到bundle资源包。而事实是当前Metro服务已启动,且直接启动React Native工程是OK的。对此寻到以下两种解决方案 :
- 对当前
React Native
工程代码进行打包,并将打包后的bundle资源
拷贝到Native工程的/main/assets/rn
目录下。然后在Native运行则无问题。 - VSCode终端执行打包指令:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ../HybridArcPro/app/src/main/assets/rn/index.android.bundle --assets-dest ../HybridArcPro/app/src/main/res/
- 非打包处理。需要对Native和React Native端同时进行配置。①对安装的debug包APP配置服务IP和端口号。②配置网络权限,
application标签
中配置tools:targetApi="28" android:allowBackup="true"
。③启动Metro服务。
底部导航栏架构设计
使用java语言,自定义首页底部导航栏布局控件(下图实现效果+导航源码),自定义UML介绍~
TabBtnLayoutBottomNav
底部导航栏布局自定义View。继承自FrameLayout,实现自接口ITabLayout。底部导航栏布局,内部摆放TabBtnBottom
,TabBtnBottom
底部导航栏布局中的单个Tab。继承自RelativeLayout,实现自I接口ITab(ITab继承了点击事件的监听接口OnTabSelectedListener)。TabBtnLayoutBottomNav
内部封装单个Tab集合List<OnTabSelectedListener>
(包含所有TabBtnBottom和TabBtnLayoutBottomNav添加的监听OnTabSelectedListener),当用户点击Tab时,点击事件通过TabBtnBottom.setOnClickListener
触发集合List的遍历,此时将点击事件传递给每个TabBtnBottomz,同时TabBtnLayoutBottomNav添加的监听回调。由此单个Tab和TabBtnLayoutBottomNav产生了点击事件的关联,并能为集成fragment点击切换显示做下伏笔。
TabBtnFragmentLayout
,显示fragment页面的**自定义布局控件**,放在布局文件TabBtnLayoutBottomNav中。由TabBtnLayoutBottomNav添加的监听回调index,方法setCurrentItem
获得指示并显示相应fragment页面。TabBtnFragmentAdapter
,显示fragment页面的适配器类。具体指示显示fragment页面逻辑,实现在方法instantiateItem
中。
原生仿招商银行首页
原生Android Socket 即时通讯
React Native仿工商银行首页
使用RN仿工商银行首页(图标自己费劲吧啦找的),然后实现StatusBar和TitleBar
随**滑动渐变**
效果。此处由原生启动并打开RN,效果如下~
原生传递props初始对象,RN使用
// React Native中配置bundle
val bundle = Bundle()
rnBundle.putCharSequence("device-info","设备信息对象")
rnBundle.putCharSequence("state","用户登录状态")
mReactRootView!!.startReactApplication(mReactInstanceManager, "hibrid_rn", bundle)
// 在对应的ReactNative的Coponent中获取,则可通过this.props得到!
原生与RN通信
已实现效果介绍
在Native端桥接类JRNBridge.kt
中定义了一个调用安卓Toast的方法,供ReactNative端调用。效果如下
开发中注意事项
这里以本项目作为示例,介绍下在实现RN和C端通信时,开发步骤逻辑。
1, 逻辑源码C端中定义桥接类工具JRNBridge.kt
。桥接类继承自ReactContextBaseJavaModule.java,实现方法getName
—— 获取到的name会在ReactNative端使用,
// bridge/index.js
import NativeModules from 'react-native'
module.exports = NativeModules.RNBridge // 这里的 RNBridge 就是getName得到。
2, ReactNative和C端Native进行通信的方法,需要加注解@ReactMethod
。
3, 创建JReactPackage.kt 继承ReactPackage.kt
,重写方法createNativeModules和createViewManagers
。重写方法createNativeModules
是将JReactPackage添加到NativeModule列表为之后注册。
createNativeModules | createViewManagers |
---|---|
ReactNative调用Native方法时重写并添加NativeModule | Native UI,作为ReactNative UI是重写并添加ViewManager |
4, 将MainReactPackage()和JReactPackage()添加注册到ReactInstanceManager中。
其中MainReactPackage()及JReactPackage()必须, 否则报错'StatusBarManager' could not be found. Verify that a module by this name is registered in the native binary.
JReactPackage()否则报错, 找不到RNBridge.toast(toast:'正在取号中,请稍后...')
。
5, ReactNative中导出在NativeModules中已注册的JRNBridge。然后在ReactNative各个地方引入并使用。
import RNBridge from '@bridge/index'
<TouchableOpacity onPress=()=>RNBridge.toast(toast:'正在取号中,请稍后...') >
Flutter仿抖音我的页面
Flutter热更新
Flutter热更新,通过动态.so文件的加载实现。
.so文件动态加载实现思路
以反射修改FlutterLoader.java类
中FlutterApplicationInfo.aotSharedLibraryName
的值,从而修改了FlutterLoader将要加载的原libapp.so
为libapp**.so
。之后,将用来替换的libapp**.so
拷贝到原libapp.so
所在的目录即可。拷贝的方式,如首先打包一个新的release-apk,然后解压提取出此时的libapp.so文件(修改名称为libapp**.so),放到目录assets/下。之后,当执行代码拷贝时,会将assets/目录下的so包拷贝到新指定的将会加载的libapp.so
所在的目录,然后则顺理成章完成热更新。
多FlutterEngine引擎创建,多dart入口实现
flutter一个投资理财页,作为第二个dart入口。并通过下面定义的对应引擎启开。
通过FlutterEngine
创建引擎实例对象。创建时传入的JFlutterLoader
,重新定义了原FlutterLoader获取dart代码包
的方式。之后,根据给定的DartEntrypoint开始执行Dart代码。并缓存已创建的Flutter引擎
实例。其中传入给DartEntrypoint
的moduleName是dart入口名称
,findAppBundlePath是flutter资产目录flutterAssetsDir
// 初始化,根据moduleName(dart入口名称)创建多个Flutter引擎
private fun initFlutterEngine(context: Context, moduleName: String): FlutterEngine?
var flutterEngine:FlutterEngine = FlutterEngine(context, JFlutterLoader.get(), FlutterJNI())
flutterEngine.dartExecutor
.executeDartEntrypoint(DartExecutor.DartEntrypoint(JFlutterLoader.get().findAppBundlePath(), moduleName))
FlutterEngineCache.getInstance().put(moduleName, flutterEngine) // 缓存起来
return flutterEngine
// 在flutter的dart代码中main.dart
// 至少有一个默认入口,如 'main'
void main()
runApp(const MyApp());
init();
// 此时,可仿照默认入口,通过注解,创建多个不同的dart入口 - 工行的'投资理财'详情页面
@pragma('vm:entry-point')
void finance()
runApp(FinanceEntryApp());
结合Flutter源码,分析从原生端启动Flutter
原生与Flutter通信
类型 | 说明 |
---|---|
MethodChannel | 用于传递方法调用invokeMethod 一次性通信:如Flutter调用埋点功能。 |
这里且介绍MethodChannel,在Native与Flutter间如何通信~Flutter侧发送,Native侧接收处理。
Flutter侧发送
,结合源码看,通过创建一个MethodChannel实例
并指定渠道名称name
。且两侧的name
须一致相同。然后使用MethodChannel实例调用执行方法invokeMethod
,该方法传入Native侧将被调用方法名称method
及通信消息内容arguments
。之后,便启动了由Flutter向Native侧传递调用。
Native侧接收处理
,创建一个与Flutter侧渠道名称name
相同的MethodChannel实例
。使用MethodChannel实例调用执行方法setMethodCallHandler
,用以匹配Flutter侧方法名称method
接收处理Flutter侧发送来的信息。详尽设计实现,请移步查看
ReactJs仿唯品会分类页面
工程源码地址
原生项目如何从零开始集成 React Native
一、混合开发
App 混合开发,指的是一个 App 部分功能用 Native 构建,其他功能使用跨端框架进行构建,最常见的场景是,Native 作为一个可工程,其实业务开发使用垮端框架进行开发。目前,比较流行的跨端框架有 H5、React Native、Flutter、布局动态化等。
而在以 Native 与 React Native 混合开发中,同一个 App 中,混合开发通常有以下几种形态:
那究竟有哪些公司在使用 React Native 呢?
首先,是一些大型 App 中,比如美团、携程、京东、58 等。这些大型 App 一般都非常复杂,整个框架需要包含模块化、组件化、插件化、跨端等能力。相比纯 React Native 工程,大型 App 的实际业务开发还需要考虑如何在已有原生项目中引入 React Native,作为原生框架的扩展能力。比如,部分业务可能对开发效率、多端一致性、动态更新能力有较强要求,就可以使用 React Native 来实现。
除了大型 App 外,如果想要在已上线的项目引入 React Native,也需要使用混合开发模式。因为原生改造成 React Native 并不是那么简单的事情,毕竟开发语言、页面管理、模块管理完全是另一套东西,而且一些原生页面,如启动页、首页等,出于性能考虑,大都
以上是关于混合开发架构|Android工程集成React NativeFlutterReactJs的主要内容,如果未能解决你的问题,请参考以下文章