Android原生项目集成React Native模块:从配置到实战避坑指南

165 阅读3分钟

读者可通过本篇文章深入了解,在现有原生项目基础上,集成RN功能步骤及注意事项,推荐各位阅读。


一、环境准备

1. 创建 package.json(项目根目录)

{
  "name": "YourProject",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "yarn react-native start",
    "bundle-android": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/"
  },
  "dependencies": {
    "react": "17.0.2",
    "react-native": "0.66.5"
  }
}

关键点

  • react 版本需与 RN 0.66.5 匹配(官方推荐 17.0.2)
  • 添加 bundle-android 脚本用于生产环境打包 JS Bundle

2. 安装依赖

yarn install

二、Android 工程配置

1. 修改根目录 build.gradle

allprojects {
    repositories {
        google()
        mavenCentral()
        // 添加 React Native 本地仓库路径
        maven {
            url "$rootDir/node_modules/react-native/android"
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/node_modules/jsc-android/dist")
        }
    }
}

作用:让 Gradle 从本地 node_modules 目录加载 RN 依赖

2. 修改 App 模块 build.gradle

android {
    compileSdkVersion 30
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 30
        ndk {
            abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
        }
    }
}

dependencies {
    implementation "com.facebook.react:react-native:0.66.5"  // 指定版本
    implementation "org.webkit:android-jsc:+"
    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"  // RN 依赖的兼容库
    
    // Fresco 版本需与 RN 0.66.5 匹配(通过查看 node_modules/react-native/react-native-0.66.5.pom 确定)
    implementation 'com.facebook.fresco:fresco:2.5.0'
    implementation 'com.facebook.fresco:animated-gif:2.5.0'
}

兼容性说明

  • minSdkVersion ≥ 21(RN 0.66.5 最低要求)
  • Fresco 版本需通过检查 node_modules/react-native/react-native-0.66.5.pom 确认

三、初始化 React Native 环境

1. 创建 MainApplication(继承 Application 并实现 ReactApplication

public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.asList(
                new MainReactPackage()
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, false);  // 初始化 Fresco 等原生模块
    }
}

2. 创建 ReactActivity(用于加载 RN 页面)

public class ReactNativeActivity extends ReactActivity {
    @Override
    protected String getMainComponentName() {
        return "YourApp";  // 与 JS 端注册的组件名一致
    }
}

四、配置 AndroidManifest.xml

<manifest>
    <!-- 添加网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".MainApplication"
        ...>
        <!-- 添加 RN Activity -->
        <activity
            android:name=".ReactNativeActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" />
    </application>
</manifest>

五、Fragment中添加ReactFragment

// Fragment嵌套子Fragment
val transaction = childFragmentManager.beginTransaction()
        val reactNativeFragment: Fragment = ReactFragment.Builder()
            .setComponentName("MyReactNativeApp")
            .build()
        transaction.add(R.id.ll_rn, reactNativeFragment).commit()

// Activity中添加
Fragment reactNativeFragment = new ReactFragment.Builder()
                    .setComponentName("MyReactNativeApp") 
                    .setLaunchOptions(getLaunchOptions("test message"))
                    .build();

            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.reactNativeFragment, reactNativeFragment)
                    .commit();
// 上面setComponentName中的字符串是index.js文件中AppRegistry.registerComponent重点的字符串内容,要一致
AppRegistry.registerComponent(
  'MyReactNativeApp',
  () => HelloWorld
);
                    
                    
// MainActivity需要实现implements DefaultHardwareBackBtnHandler

@Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

添加getLaunchOptions方法,该方法允许你将属性传递到组件。这是可选的,如果不需要传递任何属性,可以删除setLaunchOptions。

private Bundle getLaunchOptions(String message) {
    Bundle initialProperties = new Bundle();
    initialProperties.putString("message", message);
    return initialProperties;
}

六、JS Bundle 加载配置

1. 开发环境(Debug 模式)

  • 启动 Metro 服务:
    yarn start
    
  • ReactNativeActivity 中通过 ReactNativeHost 动态加载 Metro 服务提供的 Bundle

2. 生产环境(Release 模式)

生成预编译的 JS Bundle:

npm run bundle-android

输出文件

  • android/app/src/main/assets/index.android.bundle
  • android/app/src/main/res/ 中的静态资源

七、常见问题解决

1. Gradle 下载超时

修改 gradle-wrapper.properties

distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.3.3-all.zip

(Gradle 版本需与 RN 0.66.5 兼容)

2. 依赖冲突

app/build.gradle 中强制指定版本:

configurations.all {
    resolutionStrategy {
        force 'com.facebook.fresco:fresco:2.5.0'
    }
}

3. 原生版本兼容性问题

需要根据原生项目的compileSdkVersion及gradle版本,集成合适的RN版本,否则容易出现compileSdkVersion不一致问题。

android = [
            compileSdkVersion: 30,
            buildToolsVersion: "30.0.3",
            supportSdkVersion: "30.0.3",
            minSdkVersion    : 21,
            targetSdkVersion : 30,
            versionCode      : 36,
            versionName      : rootProject.VERSION_NAME
    ]


七、版本兼容性参考表

组件RN 0.66.5 要求版本兼容性说明
compileSdkVersion30+推荐与原生项目保持一致
Fresco2.5.0通过 POM 文件验证
OkHttp3.12.1需与 RN 内置版本一致
nodev16.20.2需与 RN 当前版本兼容

集成完成后,可通过 ReactNativeActivity 跳转至 RN 页面。若需进一步优化(如新架构启用),可参考 RN 0.66.5 官方文档调整配置。

八、作为全屏Activity及Fragment同时存在,需要在 index.js 中注册多个组件

import React from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import FragmentTest from './fragment_test'

class HelloWorld extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello, World</Text>
      </View>
    );
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center'
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10
  }
});
// 全屏Activity入口
AppRegistry.registerComponent(
  'MyReactNativeApp',
  () => HelloWorld
);

// Fragment 入口
AppRegistry.registerComponent('FragmentComponent', () => FragmentTest);

两种方案适用场景

八、RN与原生交互