96SEO 2026-02-20 10:10 8
Native相关的知识以一个Android开发者的视角对React

项目组织和构建流程有了一些粗浅的认识同时也对RN混合开发项目如何搭建又了一点小小的思考。
https://reactnative.cn/docs/environment-setup
创建好项目后我们来分析下就是一个标准的RN项目其中有一个node_modules是项目的依赖包。
根项目和一个ios也就是说android和ios项目是作为RN项目的子项目存在的。
来看下android下的代码是一个标准的Android项目直接使用Android
可以看到除了一个标准的Android项目外还有一个gradle-plugin的。
file(../node_modules/react-native-community/cli-platform-android/native_modules.gradle);
applyNativeModulesSettingsGradle(settings)还通过includeBuild引入了一个RN插件
includeBuild(../node_modules/react-native/gradle-plugin)再来接着看看根目录下build.gradle文件中的内容
{google()mavenCentral()}dependencies
{classpath(com.android.tools.build:gradle)//RN插件classpath(com.facebook.react:react-native-gradle-plugin)classpath(org.jetbrains.kotlin:kotlin-gradle-plugin)}
//应用了一个叫做com.facebook.react.rootproject的插件
com.facebook.react.rootproject接着看下app目录下的build.gradle文件
file(../node_modules/react-native)//
../node_modules/react-native/codegen//
file(../node_modules/react-native/codegen)//
../node_modules/react-native/cli.js//
file(../node_modules/react-native/cli.js)/*
MyApplication.android.bundle////
file(../js/MyApplication.android.js)////
https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle//
$rootDir/my-custom-hermesc/bin/hermesc////
rootProject.ext.ndkVersioncompileSdk
rootProject.ext.compileSdkVersionnamespace
com.yzq.rn_project_analysisdefaultConfig
com.yzq.rn_project_analysisminSdkVersion
rootProject.ext.minSdkVersiontargetSdkVersion
rootProject.ext.targetSdkVersionversionCode
file(debug.keystore)storePassword
https://reactnative.dev/docs/signed-apk-android.signingConfig
signingConfigs.debugminifyEnabled
enableProguardInReleaseBuildsproguardFiles
getDefaultProguardFile(proguard-android.txt),
Pluginimplementation(com.facebook.react:react-android)implementation(com.facebook.react:flipper-integration)if
{implementation(com.facebook.react:hermes-android)}
file(../../node_modules/react-native-community/cli-platform-android/native_modules.gradle);
applyNativeModulesAppBuildGradle(project)
可以看到工程的依赖配置也比较的清晰主要是配置了一些Android的基本配置然后应用了RN的插件和脚本。
三方库在RN中有着非常重要的地位因为RN本身的功能是有限的所以需要依赖一些三方库来实现一些功能。
三方库一般提供了跨平台的支持对前端开发同学来讲是非常友好的不需要去了解原生的开发技术就可以实现一些原生的功能。
react-native-device-info安装完成后会发现项目根目录下的package.json文件中多了一条依赖
react-native-device-info;Button
{DeviceInfo.getAndroidId().***n((id)
使用起来非常简单可以看到这里实际上完全不需要关心native端的代码就可以实现一些原生的功能。
正常来讲如果我们在原生项目中使用三方库是需要引入三方库的jar包或者aar包的大部分sdk还需要进行初始化操作然后才能调用相关的方法
react-native-device-info就能让RN项目使用原生的功能这是怎么做到的呢
这个module是怎么引入的呢正常来讲在Android中我们想要引入一个本地的module需要在settings.gradle中include进来然后在build.gradle中引入依赖。
但是再次去看看settings.gradle和build.gradle文件发现并没有类似的代码那这个module是怎么引入的呢
file(“…/node_modules/react-native-community/cli-platform-android/native_modules.gradle”);includeBuild(‘…/node_modules/react-native/gradle-plugin’)
我们先来分析一下react-native-gradle-plugin这个插件,这个插件是RN项目的核心插件它的作用是管理RN项目的依赖和配置。
com.facebook.reactcom.facebook.react.rootproject
该插件在项目的根目录下的build.gradle文件中被应用了
该插件的作用是确保app项目在库项目之前被配置以便在库项目被配置时可以使用app项目的配置**
{it.evaluationDependsOn(:app)}}}
代码非常少其作用就是是确保app项目在库项目之前被配置以便在库项目被配置时可以使用app项目的配置。
简单说就是app中会有一些rn相关的配置一些三方库中也会用到这些配置此时需要确保app项目的配置在库项目之前被配置以确保其他模块能够正常使用。
{//检查JVM版本不能低于17checkJvmVersion(project)//创建react配置val
project.extensions.create(react,
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)?:
project.rootProject.extensions.create(privateReact,
PrivateReactExtension::class.java,
如果项目中使用了com.android.application插件也就是app模块中会执行以下代码*/project.pluginManager.withPlugin(com.android.application)
下面代码实际上就是把用户自定义的配置赋值给rootExtension就是把用户自定义的配置传递给上面创建好的一个私有配置项
privateReact*/rootExtension.root.set(extension.root)rootExtension.reactNativeDir.set(extension.reactNativeDir)rootExtension.codegenDir.set(extension.codegenDir)rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)println(rootExtension
${rootExtension.root.get()})println(rootExtension
${rootExtension.reactNativeDir.get()})println(rootExtension
${rootExtension.codegenDir.get()})println(rootExtension
${rootExtension.nodeExecutableAndArgs.get()})/***
项目配置完成后执行以下代码*/project.afterEvaluate
extension.reactNativeDir.get().asFileval
ReactAndroid/gradle.properties)//获取版本号和groupNameval
readVersionAndGroupStrings(propertiesFile)val
versionAndGroupStrings.firstval
versionAndGroupStrings.second//配置依赖主要是做了依赖替换和统一版本的逻辑configureDependencies(project,
groupString)//配置仓库configureRepositories(project,
reactNativeDir)}//配置NDKconfigureReactNativeNdk(project,
extension)//配置App的构建配置字段configureBuildConfigFieldsForApp(project,
默认8081configureDevPorts(project)//处理老版本配置兼容性configureBackwardCompatibilityReactMap(project)//配置Java工具链确保项目中的
版本configureJavaToolChains(project)//根据不同的构建类型配置不同的任务project.extensions.getByType(AndroidComponentsExtension::class.java).apply
-//配置react任务用于执行react-native的打包操作project.configureReactTasks(variant
extension)}}//配置react-native-codegen用于生成所需代码configureCodegen(project,
ConfigurationconfigureBuildConfigFieldsForLibraries(project)configureNamespaceForLibraries(project)project.pluginManager.withPlugin(com.android.library)
Jvm.current()?.javaVersion?.majorVersionprintln(jvmVersion:
{project.logger.error(********************************************************************************ERROR:
$jvmVersion********************************************************************************.trimIndent())exitProcess(1)}}创建react配置
project.extensions.create(react,
privateReact如果已经存在则获取用于在app项目和库项目之间共享配置
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)?:
project.rootProject.extensions.create(privateReact,
PrivateReactExtension::class.java,
objects.directoryProperty().convention(root.dir(node_modules/react-native/codegen))
如果项目中使用了com.android.application插件也就是app模块中会执行以下代码
用户自定义的配置赋值给rootExtension就是把用户自定义的配置传递给上面创建好的一个私有配置项
project.pluginManager.withPlugin(com.android.application)
defaults).rootExtension.root.set(extension.root)rootExtension.reactNativeDir.set(extension.reactNativeDir)rootExtension.codegenDir.set(extension.codegenDir)rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)}配置依赖主要是做了依赖替换和统一版本的逻辑,这也就是为什么在app的build.gradle中的react
extension.reactNativeDir.get().asFile
ReactAndroid/gradle.properties)//获取版本号和groupName
readVersionAndGroupStrings(propertiesFile)
readVersionAndGroupStrings方法实际上就是从/node_modules/reactnative/ReactAndroid/gradle.properties文件中读取版本号和group字符串
readVersionAndGroupStrings(propertiesFile:
{println(readVersionAndGroupStrings:
Properties()propertiesFile.inputStream().use
reactAndroidProperties.load(it)
reactAndroidProperties[INTERNAL_VERSION_NAME]
(versionStringFromFile.startsWith(0.0.0)
{$versionStringFromFile-SNAPSHOT}
reactAndroidProperties[INTERNAL_PUBLISHING_GROUP]
DEFAULT_INTERNAL_PUBLISHING_GROUPreturn
groupString)}configureDependencies方法主要做了依赖替换和统一版本的逻辑
DEFAULT_INTERNAL_PUBLISHING_GROUP)
{println(configureDependencies:
return//遍历所有项目project.rootProject.allprojects
${eachProject.name})//遍历项目的所有配置eachProject.configurations.all
configuration.resolutionStrategy
用于配置解析策略一般用于配置依赖替换和强制使用指定版本*/configuration.resolutionStrategy.dependencySubstitution
{//获取依赖替换列表getDependencySubstitutions(versionString,groupString).forEach
-//将指定的依赖替换为目标依赖it.substitute(it.module(module)).using(it.module(dest)).because(reason)}}//强制使用指定版本configuration.resolutionStrategy.force(${groupString}:react-android:${versionString},${groupString}:flipper-integration:${versionString},)//如果用户没有选择使用夜间版本进行本地开发则强制使用hermes-android指定版本if
(!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY)
development.configuration.resolutionStrategy.force(${groupString}:hermes-android:${versionString})}}}}getDependencySubstitutions方法主要是生成需要进行依赖替换的列表
getDependencySubstitutions(versionString:
DEFAULT_INTERNAL_PUBLISHING_GROUP):
react-native替换为react-androiddependencySubstitution.add(Triple(com.facebook.react:react-native,${groupString}:react-android:${versionString},The
https://github.com/facebook/react-native/issues/35210.))//
hermes-engine替换为hermes-androiddependencySubstitution.add(Triple(com.facebook.react:hermes-engine,${groupString}:hermes-android:${versionString},The
https://github.com/facebook/react-native/issues/35210.))//
com.facebook.react则修改react-android和hermes-android的Maven
DEFAULT_INTERNAL_PUBLISHING_GROUP)
{dependencySubstitution.add(Triple(com.facebook.react:react-android,${groupString}:react-android:${versionString},The
group.))dependencySubstitution.add(Triple(com.facebook.react:hermes-android,${groupString}:hermes-android:${versionString},The
dependencySubstitution}配置仓库源这个比较简单就是配置了一些依赖所需的仓库地址
{println(configureRepositories:
$reactNativeDir)project.rootProject.allprojects
(hasProperty(INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO))
property(INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO)
StringmavenRepoFromURI(File(mavenLocalRepoPath).toURI())}//
nightlies.mavenRepoFromUrl(https://oss.sonatype.org/content/repositories/snapshots/)repositories.mavenCentral
npmmavenRepoFromURI(File(reactNativeDir,
../jsc-android/dist).toURI())repositories.google()mavenRepoFromUrl(https://www.jitpack.io)}}}配置NDKNative
configureReactNativeNdk(project:
{project.pluginManager.withPlugin(com.android.application)
{project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
(!project.isNewArchEnabled(extension))
(ext.externalNativeBuild.cmake.path
{ext.externalNativeBuild.cmake.path
File(extension.reactNativeDir.get().asFile,ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt)}//
ext.defaultConfig.externalNativeBuild.cmake.argumentsif
it.startsWith(-DPROJECT_BUILD_DIR)
{cmakeArgs.add(-DPROJECT_BUILD_DIR${project.layout.buildDirectory.get().asFile})}if
it.startsWith(-DREACT_ANDROID_DIR)
{cmakeArgs.add(-DREACT_ANDROID_DIR${extension.reactNativeDir.file(ReactAndroid).get().asFile})}if
{cmakeArgs.add(-DANDROID_STLc_shared)}//
it.startsWith(-DANDROID_USE_LEGACY_TOOLCHAIN_FILE)
{cmakeArgs.add(-DANDROID_USE_LEGACY_TOOLCHAIN_FILEON)}val
project.getReactNativeArchitectures()//
{ext.defaultConfig.ndk.abiFilters.addAll(architectures)}}}}配置App的构建配置字段
应用或库项目中启用buildConfig并添加了两个自定义的布尔类型的构建配置字段用于表示新架构是否启用以及是否启用了
configureBuildConfigFieldsForApp(project:
{project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
trueext.defaultConfig.buildConfigField(boolean,IS_NEW_ARCHITECTURE_ENABLED,project.isNewArchEnabled(extension).toString())ext.defaultConfig.buildConfigField(boolean,
project.isHermesEnabled.toString())}}project.pluginManager.withPlugin(com.android.application,
action)project.pluginManager.withPlugin(com.android.library,
project.properties[reactNativeDevServerPort]?.toString()
project.properties[reactNativeInspectorProxyPort]?.toString()
{project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
-ext.defaultConfig.resValue(integer,react_native_dev_server_port,devServerPort)ext.defaultConfig.resValue(integer,
react_native_inspector_proxy_port,
inspectorProxyPort)}}project.pluginManager.withPlugin(com.android.application,
action)project.pluginManager.withPlugin(com.android.library,
configureBackwardCompatibilityReactMap(project:
(project.extensions.extraProperties.has(react))
project.extensions.extraProperties.get(react)
{project.logger.error(********************************************************************************ERROR:
it.********************************************************************************.trimIndent())}
project.extensions.extraProperties.set(react,
react.internal.disableJavaVersionAlignment
(input.hasProperty(INTERNAL_DISABLE_JAVA_VERSION_ALIGNMENT))
{return}input.rootProject.allprojects
react.internal.disableJavaVersionAlignment
(project.hasProperty(INTERNAL_DISABLE_JAVA_VERSION_ALIGNMENT))
{project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
ext-ext.compileOptions.sourceCompatibility
JavaVersion.VERSION_17ext.compileOptions.targetCompatibility
JavaVersion.VERSION_17}}project.pluginManager.withPlugin(com.android.application,
action)project.pluginManager.withPlugin(com.android.library,
action)project.pluginManager.withPlugin(org.jetbrains.kotlin.android)
{project.extensions.getByType(KotlinTopLevelExtension::class.java).jvmToolchain(17)}project.pluginManager.withPlugin(org.jetbrains.kotlin.jvm)
{project.extensions.getByType(KotlinTopLevelExtension::class.java).jvmToolchain(17)}}
//根据不同的构建类型配置不同的任务project.extensions.getByType(AndroidComponentsExtension::class.java).apply
-//配置react任务用于执行react-native的打包操作project.configureReactTasks(variant
extension)}}configureReactTasks
Project.configureReactTasks(variant:
variant.name.capitalizeCompat()val
this.layout.buildDirectory.get().asFile//
generated/assets/react/variant/index.android.bundleval
generated/res/react/$targetPath)//
generated/assets/react/variant/index.android.bundleval
generated/assets/react/$targetPath)//
generated/sourcemaps/react/variant/index.android.bundle.mapval
generated/sourcemaps/react/$targetPath)//
intermediates/sourcemaps/react/variant/index.android.bundle.packager.map//
intermediates/sourcemaps/react/variant/index.android.bundle.compiler.mapval
intermediates/sourcemaps/react/$targetPath)//
(config.enableHermesOnlyInVariants.get().isNotEmpty())
{config.enableHermesOnlyInVariants.get().contains(variant.name)
config.debuggableVariants.get().any
}//配置新架构打包选项configureNewArchPackagingOptions(project,
variant)//配置JS引擎打包选项configureJsEnginePackagingOptions(config,
isHermesEnabledInThisVariant)if
tasks.register(createBundle${targetName}JsAndAssets,
{it.root.set(config.root)it.nodeExecutableAndArgs.set(config.nodeExecutableAndArgs)it.cliFile.set(cliFile)it.bundleCommand.set(config.bundleCommand)it.entryFile.set(detectedEntryFile(config,
entryFileEnvVariable))it.extraPackagerArgs.set(config.extraPackagerArgs)it.bundleConfig.set(config.bundleConfig)it.bundleAssetName.set(config.bundleAssetName)it.jsBundleDir.set(jsBundleDir)it.resourcesDir.set(resourcesDir)it.hermesEnabled.set(isHermesEnabledInThisVariant)it.minifyEnabled.set(!isHermesEnabledInThisVariant)it.devEnabled.set(false)it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)it.jsSourceMapsDir.set(jsSourceMapsDir)it.hermesCommand.set(config.hermesCommand)it.hermesFlags.set(config.hermesFlags)it.reactNativeDir.set(config.reactNativeDir)}//将生成的资源目录添加到源集variant.sources.res?.addGeneratedSourceDirectory(bundleTask,BundleHermesCTask::resourcesDir)variant.sources.assets?.addGeneratedSourceDirectory(bundleTask,BundleHermesCTask::jsBundleDir)}}
配置react-native-codegen用于生成所需代码帮助我们避免编写重复代码的工具。
configureBuildConfigFieldsForLibraries(appProject:
{appProject.rootProject.allprojects
-subproject.pluginManager.withPlugin(com.android.library)
{subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
configureNamespaceForLibraries(appProject:
{appProject.rootProject.allprojects
-subproject.pluginManager.withPlugin(com.android.library)
{subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl
subproject.extensions.getByType(LibraryExtension::class.java)val
android.sourceSets.getByName(main).manifest.srcFilemanifestFile.takeIf
-getPackageNameFromManifest(file)?.let
packageName}}}}}}}如果项目中使用了com.android.library插件也就是library模块中会执行以下代码
配置react-native-codegen用于生成所需代码帮助我们避免编写重复代码的工具。
到这里我们基本就清楚了react-native-gradle-plugin这个插件的主要作用
做了一些编译环境的检查创建了一些配置项用于在app项目和库项目之间共享配置统一替换项目中的react-native相关的依赖并确保版本一致配置任务包括打包生成代码等
但是插件中并没有看到RN三方库依赖处理的逻辑所以并没有解答我们一开始的问题我们接着分析。
ReactNative项目构建分析与思考之native_modules.gradle
感谢阅读觉有有帮助点赞支持如果有任何疑问或建议欢迎在评论区留言。
如需转载请注明出处喻志强的博客
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback