您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > Android

混合开发架构|Android工程集成React Native、Flutter、ReactJs

时间:2023-03-06 12:53:18  来源:智云站长  作者:

架构设计说明

该篇文章,介绍并记录在大前端混合架构开发中的重要细节和流程。通过在Android/ target=_blank class=infotextkey>安卓原生工程中集成两大主流混合框架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确定版本号!!修改ReactNativeapp/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'
    }

集成嵌入原生工程

Groovy

// 【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

Groovy
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各个地方引入并使用。
在这里插入图片描述

ReactNative端源码

import RNBridge from '@bridge/index'
<TouchableOpacity onPress={()=>{RNBridge.toast({toast:'正在取号中,请稍后...'})}} >

Flutter仿抖音我的页面

Flutter热更新

Flutter热更新,通过动态.so文件的加载实现。

.so文件动态加载实现思路
以反射修改FlutterLoader.java类FlutterApplicationInfo.aotSharedLibraryName的值,从而修改了FlutterLoader将要加载的原libapp.solibapp**.so。之后,将用来替换的libapp**.so拷贝到原libapp.so所在的目录即可。拷贝的方式,如首先打包一个新的release-apk,然后解压提取出此时的libapp.so文件(修改名称为libapp**.so),放到目录assets/下。之后,当执行代码拷贝时,会将assets/目录下的so包拷贝到新指定的将会加载的libapp.so所在的目录,然后则顺理成章完成热更新。

在这里插入图片描述

在这里插入图片描述

多FlutterEngine引擎创建,多dart入口实现

flutter一个投资理财页,作为第二个dart入口。并通过下面定义的对应引擎启开。
在这里插入图片描述

多FlutterEngine引擎创建

通过FlutterEngine创建引擎实例对象。创建时传入的JFlutterLoader,重新定义了原FlutterLoader获取dart代码包的方式。之后,根据给定的DartEntrypoint开始执行Dart代码。并缓存已创建的Flutter引擎实例。其中传入给DartEntrypointmoduleName是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
}

多dart入口创建

// 在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侧发送来的信息。详尽设计实现,请移步查看



Tags:Android   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Android Emulator黑屏怎么办 Android模拟器黑屏解决方法
Android Emulator黑屏问题困扰了非常多的玩家,Android Emulator作为一款安卓模拟器,可以让你在电脑上运行和浏览安卓应用程序,但是程序本身不是很稳定,很容易会出现黑屏,启动不了...【详细内容】
2024-03-04  Search: Android  点击:(37)  评论:(0)  加入收藏
Android 谷歌三件套:解锁谷歌生态!
大家是不是遇到这个情况?当我们需要下载一些国外的游戏或者软件的时候,需要在手机里面安装Google Play商店,然后通过Google Play商店下载国外软件!为了帮助大家使用上各种好用的...【详细内容】
2024-01-02  Search: Android  点击:(113)  评论:(0)  加入收藏
Android开发中常见的Hook技术有哪些?
Hook技术介绍Hook技术是一种在软件开发中常见的技术,它允许开发者在特定的事件发生时插入自定义的代码逻辑。常见的应用场景包括在函数调用前后执行特定的操作,或者在特定的事...【详细内容】
2023-12-25  Search: Android  点击:(85)  评论:(0)  加入收藏
在Android应用开发中使用NFC功能
NFC介绍NFC是指“近场通讯”(Near Field Communication),它是一种短距离无线通信技术,允许设备在非接触或极短距离内进行通信。NFC通常用于移动支付、门禁系统、智能标签和其他...【详细内容】
2023-12-22  Search: Android  点击:(102)  评论:(0)  加入收藏
关于Android图像Bitmap类,你要知道的一切
Bitmap介绍Bitmap是一种图像文件格式,它由像素阵列组成,每个像素都有自己的颜色信息。在计算机图形学中,Bitmap图像可以被描述为一个二维的矩阵,其中每个元素代表一个像素的颜色...【详细内容】
2023-12-19  Search: Android  点击:(99)  评论:(0)  加入收藏
Android开发中如何进行单元测试?
单元测试介绍单元测试是软件开发中的一种测试方法,用于验证代码中的最小可测试单元(通常是函数或方法)是否按预期工作。单元测试通常由开发人员编写,旨在隔离和测试代码的特定部...【详细内容】
2023-12-11  Search: Android  点击:(167)  评论:(0)  加入收藏
我的手机我做主,如何为Android手机应用换图标?
作为一名Android用户,你是否曾经为自己的手机桌面感到单调而乏味?虽然Android系统的桌面定制性已经非常强大,但有时候我们还是希望能够在细节上做出一些改变,尤其是对于那些每天...【详细内容】
2023-12-10  Search: Android  点击:(61)  评论:(0)  加入收藏
了解Android系统架构中的HAL硬件抽象层
在Android系统中,HAL的存在使得不同厂商的硬件可以统一被上层的应用程序调用,从而提高了系统的兼容性和可移植性。HAL还可以帮助开发者更方便地开发应用程序,因为他们不需要为...【详细内容】
2023-12-06  Search: Android  点击:(203)  评论:(0)  加入收藏
谷歌 CEO 皮查伊建议 Android 用户不要侧载应用,称非常危险
iOS和Android的一个显著差异是,Android支持用户从第三方渠道安装应用程序(即“侧载”)。然而,谷歌似乎并不希望用户这样做。最近,在与Epic Store的法律诉讼中,谷歌首席执行官桑达...【详细内容】
2023-11-20  Search: Android  点击:(167)  评论:(0)  加入收藏
Android数据对象序列化原理与应用
序列化与反序列化「序列化」是将对象转换为可以存储或传输的格式的过程。在计算机科学中,对象通常是指内存中的数据结构,如数组、列表、字典等。通过序列化,可以将这些对象转换...【详细内容】
2023-11-14  Search: Android  点击:(273)  评论:(0)  加入收藏
▌简易百科推荐
Android Emulator黑屏怎么办 Android模拟器黑屏解决方法
Android Emulator黑屏问题困扰了非常多的玩家,Android Emulator作为一款安卓模拟器,可以让你在电脑上运行和浏览安卓应用程序,但是程序本身不是很稳定,很容易会出现黑屏,启动不了...【详细内容】
2024-03-04  18183游戏网    Tags:Android Emulator   点击:(37)  评论:(0)  加入收藏
Android开发中常见的Hook技术有哪些?
Hook技术介绍Hook技术是一种在软件开发中常见的技术,它允许开发者在特定的事件发生时插入自定义的代码逻辑。常见的应用场景包括在函数调用前后执行特定的操作,或者在特定的事...【详细内容】
2023-12-25  沐雨花飞蝶  微信公众号  Tags:Android   点击:(85)  评论:(0)  加入收藏
在Android应用开发中使用NFC功能
NFC介绍NFC是指“近场通讯”(Near Field Communication),它是一种短距离无线通信技术,允许设备在非接触或极短距离内进行通信。NFC通常用于移动支付、门禁系统、智能标签和其他...【详细内容】
2023-12-22  沐雨花飞蝶  微信公众号  Tags:Android   点击:(102)  评论:(0)  加入收藏
关于Android图像Bitmap类,你要知道的一切
Bitmap介绍Bitmap是一种图像文件格式,它由像素阵列组成,每个像素都有自己的颜色信息。在计算机图形学中,Bitmap图像可以被描述为一个二维的矩阵,其中每个元素代表一个像素的颜色...【详细内容】
2023-12-19  沐雨花飞蝶  微信公众号  Tags:Android   点击:(99)  评论:(0)  加入收藏
Android开发中如何进行单元测试?
单元测试介绍单元测试是软件开发中的一种测试方法,用于验证代码中的最小可测试单元(通常是函数或方法)是否按预期工作。单元测试通常由开发人员编写,旨在隔离和测试代码的特定部...【详细内容】
2023-12-11  沐雨花飞蝶  微信公众号  Tags:Android   点击:(167)  评论:(0)  加入收藏
一篇聊聊Jetpack Room实现数据存储持久性
Room介绍Room 是 Android Jetpack 组件库中的一部分,它是用于在 Android 应用中进行本地数据库访问和管理的库。Room 提供了一个抽象层,使开发者能够更轻松地访问 SQLite 数据...【详细内容】
2023-12-08  沐雨花飞蝶  微信公众号  Tags:Jetpack   点击:(143)  评论:(0)  加入收藏
了解Android系统架构中的HAL硬件抽象层
在Android系统中,HAL的存在使得不同厂商的硬件可以统一被上层的应用程序调用,从而提高了系统的兼容性和可移植性。HAL还可以帮助开发者更方便地开发应用程序,因为他们不需要为...【详细内容】
2023-12-06  沐雨花飞蝶  微信公众号  Tags:Android   点击:(203)  评论:(0)  加入收藏
我们一起聊聊 IntentService 与 Service 的区别?
Service介绍Service组件是Android应用开发中的四大组件之一,用于在后台执行长时间运行的操作或处理远程请求。它可以在没有用户界面的情况下执行任务,并且可以与其他应用组件...【详细内容】
2023-12-06  沐雨花飞蝶  微信公众号  Tags:IntentService   点击:(170)  评论:(0)  加入收藏
Android数据对象序列化原理与应用
序列化与反序列化「序列化」是将对象转换为可以存储或传输的格式的过程。在计算机科学中,对象通常是指内存中的数据结构,如数组、列表、字典等。通过序列化,可以将这些对象转换...【详细内容】
2023-11-14  沐雨花飞蝶  微信公众号  Tags:Android   点击:(273)  评论:(0)  加入收藏
你了解Android中的SELinux吗?
SELinux介绍SELinux(Security-Enhanced Linux)是一种安全增强的Linux操作系统,它通过强制访问控制(MAC)机制来提供更高级别的系统安全保护。相比于传统的Linux访问控制机制(DAC),SEL...【详细内容】
2023-11-09  沐雨花飞蝶  微信公众号  Tags:Android   点击:(264)  评论:(0)  加入收藏
站内最新
站内热门
站内头条