Android 项目集成 React Native

如果不是一个从零开始的项目,那么更多情况下是需要把 React Native 作为一个功能模块嵌入到现有应用中,这篇文章简单记录一下怎么集成 RN 到现有 Android 项目中,以及一些坑点。

OS:macOS 10.13.4

node:v9.10.1

npm:5.8.0

AS:3.1.0

创建 npm 工程

按照传统 React Native 的代码使用 npm 进行“构建”,并且 Android 项目中一些依赖也来自于这个工程。

找一个你喜欢的位置,执行一下命令初始化一个 npm 项目:

mkdir rn && cd rn
npm init -y

然后需要安装 react 和 react-native :

npm install --save react react-native

接着创建一个入口文件和一些必要目录:

touch index.android.js
mkdir app

完成以上操作后,会得到一个如下的目录结构:

├── app
├── index.android.js
├── node_modules
├── package-lock.json
└── package.json

再向 index.android.js 中填入如下内容:

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

class HelloWorld extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello    RN!!</Text>
      </View>
    )
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

AppRegistry.registerComponent('HelloRN', () => HelloWorld);

以上代码在屏幕正中央显示 “HelloRN” 。

最后修改一下 package.json 文件:

{
  "name": "rn",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start", // 启动开发服务器
    "bundle-android": "node node_modules/react-native/local-cli/cli.js bundle --platform android --dev false --entry-file index.android.js --bundle-output ../rndemo/src/release/assets/index.android.bundle --assets-dest ../rndemo/src/release/res/", // 打包 JS
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^16.3.1",
    "react-native": "^0.55.1"
  }
}

具体的路径根据实际情况修改。

创建 Android 工程

在和 rn 同级目录创建一个工程,然后修改根目录的 build.gradle 如下:

allprojects {
    repositories {
//  ...
        maven{
            url "$rootDir/rn/node_modules/react-native/android"
        }
    }
}

这里很容易猜到,在 $rootDir/rn/node_modules/react-native/android 中肯定有一些 jar 包。

然后在 app module 的build.gradle 文件里添加:

dependencies{
  //...
  implementation "com.facebook.react:react-native:+"
}

添加权网络权限

AndroidManifest.xml 文件里添加网络权限:

<uses-permission android:name="android.permission.INTERNET" />

添加开发者菜单

这个一般用于开发时,可以直接从本地服务器自动拉取最新 JavaScript 代码,还是建议加上。正式发版时,还是需要去掉这个,所以还需要简单配置下。

在 src 目录下创建一个 debug 目录,然后创建一个 AndroidManifest.xml 文件并填入以下内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dashmrl.rndemo">

    <application>
        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>

这样在正式打包的时候就可以去掉这个 DevSettingsActivity 了。

添加 DrawOverlays 权限

开发时,错误信息会显示在红底悬浮窗中,在 Android M 及以上的系统中需要用户授权才能显示,所以为了便于debug,需要开启这一权限。在 Activity 中添加如下代码:

@Override
public void onCreate(Bunlde savedStateInstance){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                                       Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                // SYSTEM_ALERT_WINDOW permission not granted...
            }
        }
    }
}

这里只是大概的流程,具体的看场景。

添加 RN 的宿主 Activity

public class HelloActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setJSMainModulePath("index.android")// 开发服务器 js bundle 所在目录
                .setBundleAssetName("index.android.bundle")
                .addPackage(new MainReactPackage())
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        // 注意这里的 HelloRN 必须对应“index.android.js”中的
        // “AppRegistry.registerComponent()”的第一个参数
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
        setContentView(mReactRootView);
    }
    @Override
    protected void onPause() {
        super.onPause();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostPause(this);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostResume(this, this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostDestroy();
        }
    }
// 一般情况下,需要先将返回键事件交由 JS 处理
    @Override
    public void onBackPressed() {
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onBackPressed();
        } else {
            super.onBackPressed();
        }
    }
// 当 JS 不想处理返回键事件的时候,就会调用这个方法
    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
//  默认情况下,抖一抖手机显示配置对话框,但是模拟器无法抖一抖
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }
}

然后把这个 Activity 注册就好了:

 <activity
   android:name=".HelloActivity"
   android:label="@string/app_name"
   android:theme="@style/Theme.AppCompat.Light.NoActionBar">
 </activity>

第4行避免出现工具栏,现在一般也都会使用这个主题。

开车

完成上面的配置就可以运行了。

启动开发服务器

进入 rn 目录,执行:

npm start

然后会看到如下输出:

> rn@1.0.0 start /Volumes/Files/Projects/Android/Android_Demos/rn
> node node_modules/react-native/local-cli/cli.js start

Scanning folders for symlinks in /Volumes/Files/Projects/Android/Android_Demos/rn/node_modules (14ms)
┌──────────────────────────────────────────────────────────────────────────────┐
│                                                                              │
│  Running Metro Bundler on port 8081.                                         │
│                                                                              │
│  Keep Metro running while developing on any JS projects. Feel free to        │
│  close this tab and run your own Metro instance if you prefer.               │
│                                                                              │
│  https://github.com/facebook/react-native                                    │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

Looking for JS files in
   /Volumes/Files/Projects/Android/Android_Demos/rn

Metro Bundler ready.

Loading dependency graph, done.

运行 Android 应用

启动开发服务器后,就可以和正常开发 Android 应用时一样启动应用了。

运行后可以看到终端里有如下的输出:

 BUNDLE  [android, dev] ./index.android.js ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ 86.3% (446/480)

效果如下:

image

这时候使用 cmd + M 键呼出菜单,就可以进行一些调试的配置了。

坑点

当显示 Perf Monitor 时就会造成开发热更新失败,所以只能先关闭这个,只在真正性能调优的时候开启。

打包 App

最终还是要打包成一个 App,需要先将 JS 打包,然后再 build 应用。

进入 rn 目录,执行以下命令:

npm run bundle-android

assets、res 目录要提前创建好,然后文件就会输出到相应目录下去。

release 文件夹这里专门用来放 JS bundle ,不需要添加到版本控制系统里面去

然后走正常的 Android 打包流程就好了。

Demo 地址

参考

https://reactnative.cn/docs/0.51/integration-with-existing-apps.html

2026-06-01

写于 2018 年,基于 RN 0.51 / Node 9 / npm 5。Android 与 React Native 的集成方式已显著演进:Metro bundler 取代旧 Packager、Hermes 成为默认引擎、新架构(JSI/Fabric/TurboModules)已正式发布,index.android.js 入口文件约定也已废弃。内容仅供参考,实际集成请以官方最新文档为准。