React Native嵌入Android原生項目中
開發環境準備
首先你要搭建好React Native for Android開發環境, 沒有搭建好的可以參考:React Native for Android Windows環境搭建
用Android Studio新建Android原生項目
我創建了一個名叫ReactNativeDemo的原生項目。
把React Native集成到原生項目當中
利用Windows命令行在項目根目錄(ReactNativeDemo文件夾)下執行下面三行命令:
npm init
npm install --save react react-native
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
這將在項目根目錄(ReactNativeDemo文件夾)創建node_modules文件夾(模塊),并添加React Native依賴。
針對上面三條命令的解釋
npm init


注意:
name的填寫由圖可知填默認的是不行的,它的要求是不能有大寫字母并且不能以數字開頭;
entry point的填寫入口文件名稱,默認的是index.js,我們建立的入口文件是index.android.js,所以填寫index.android.js。只要填寫的名稱與自己定義的入口文件名稱一致就行。
其他的項根據自己需求填寫即可。
這個步驟會在項目的根目錄產生一個名稱為package.json的文件,我們還需要修改我們的package.json文件:
在"scripts"節點下添加"start": "node node_modules/react-native/local-cli/cli.js start"。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node node_modules/react-native/local-cli/cli.js start"
}
其中的test節點是自動生成的,我們可以把它刪除,最后我的package.json為:
{
"name": "reactnativedemo",
"version": "1.0.0",
"description": "",
"main": "index.android.js",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.4.1",
"react-native": "^0.38.0"
}
}
npm install --save react react-native



curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
curl是利用URL語法在命令行方式下工作的開源文件傳輸工具。它被廣泛應用在Unix、多種Linux發行版中,并且有DOS和Win32、Win64下的移植版本。
所以可知上面這句話的意思是在對應網址下下載.flowconfig文件。
在windows下我們要使用curl命令會提示:curl不是內部和外部命令,也不是可執行文件或批處理命令。。。
我們在windows下要使用curl命令比較麻煩。解決方法就是我們用下載工具從https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig 上把.flowconfig下載下來復制到項目根目錄,或者是在項目根目錄下新建一個.flowconfig文件用瀏覽器訪問這個網址其中的內容把其中的內容復制到文件當中。
建立index.android.js文件
在項目的根目錄建立index.android.js文件并把下面的代碼復制進去:
'use strict';
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class Root extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Welcome!</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('ReactNativeView', () => Root);
添加依賴
在項目的根目錄的build.gradle中加入:
allprojects {
repositories {
jcenter()
maven {
//添加react native依賴,url路徑根據實際的來寫,本文的如下:
url "$rootDir/node_modules/react-native/android"
}
}
}
注意:Android項目默認的依賴包的源為jcenter(),其中并不包含最新版的 React Native(它只到0.20.1)。
新版的React Native只在npm里發布,所以你需要增加一下依賴包的源。在編譯完后,檢查項目External Libraries的
react-native版本如果為0.20.1,則說明maven的依賴源沒有添加成功。這時候應該是maven的路徑出問題了,你要檢查
路徑是否正確,正確的結果為:

在項目的模塊(app)中的build.gradle文件中添加:
文件頭添加(可選):
apply from: "$rootDir/node_modules/react-native/react.gradle"
無法編譯通過的時候可以嘗試添加上面這句。
dependencies {
...
compile "com.facebook.react:react-native:+"
}
如果你想總是使用一個特定的版本,你需要把+替換成你已經下載的React Native的版本號,
這個版本號應該與package.json中的react-native的版本號("react-native": "^0.38.0")一致的。如本例中的0.38.0:
dependencies {
...
compile "com.facebook.react:react-native:0.38.0"
}
添加原生Activity文件:
官方教程的寫法:
MyReactActivity
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import com.facebook.react.BuildConfig;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
public class MyReactActivity extends Activity 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())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android") //對應index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發者支持,BuildConfig.DEBUG的值默認是false,無法使用開發者菜單
.setUseDeveloperSupport(true) //開發者支持,開發的時候要設置為true,不然無法使用開發者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應index.android.js中AppRegistry.registerComponent('ReactNativeView', () => ReactNativeView)的ReactNativeView
mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeView", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@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(this);
}
}
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
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);
}
}
<font color="#EF7A7A">注意:</font>
官方教程的寫法中這里:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android") //對應index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發者支持,BuildConfig.DEBUG的值默認是false,無法使用開發者菜單
.setUseDeveloperSupport(true) //開發者支持,開發的時候要設置為true,不然無法使用開發者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應index.android.js中AppRegistry.registerComponent('ReactNativeView', () => ReactNativeView)的ReactNativeView
mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeView", null);
setContentView(mReactRootView);
}
的setUseDeveloperSupport(BuildConfig.DEBUG)方法,設置開發者支持,BuildConfig.DEBUG的值默認是false,無法使用開發者支持(開發者菜單、即時預覽等),所以我們要把BuildConfig.DEBUG改為true。
另一種原生Activity寫法
MyReactNativeActivity
import com.facebook.react.ReactActivity;
public class MyReactNativeActivity extends ReactActivity {
/**
* 這里的ReactNativeView對應index.android.js中AppRegistry.registerComponent('ReactNativeView', () => Root)的ReactNativeView
*/
@Override
protected String getMainComponentName() {
return "ReactNativeView";
}
}
兩種原生Activity寫法的對比:
第一種(官方例子的)
這種寫法的優勢是可以利用React Native來寫我們界面中的某一塊區域,就是利用原生布局的addView()方法把mReactRootView加入到布局中,比如:
activity_my_react.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#9DB16D"
android:textSize="40sp"
android:text="原生控件TextView" />
<LinearLayout
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
MyReactActivity修改
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_react);
mReactLayout = (LinearLayout) findViewById(R.id.layout);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")//對應index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發者支持,BuildConfig.DEBUG的值默認是false,無法使用開發者菜單
.setUseDeveloperSupport(true) //開發者支持,開發的時候要設置為true,不然無法使用開發者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應index.android.js中AppRegistry.registerComponent('ReactNativeView', () => Root)的ReactNativeView
mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeView", null);
mReactLayout.addView(mReactRootView);
}
index.android.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet
} from 'react-native'
export default class Root extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>React Native組件</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#65A35F',
},
welcome: {
fontSize: 40,
textAlign: 'center',
margin: 10,
}
});
AppRegistry.registerComponent('ReactNativeView', () => Root);
第二種
這種方式是優勢是寫法簡單。但是無法局部使用React Native來布局。
AndroidManifest.xml相關
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="top.cokernut.reactnativetonative">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:name=".MyApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MyReactActivity"
android:label="MyReactActivity">
</activity>
<activity
android:name=".MyReactNativeActivity"
android:label="MyReactNativeActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
網絡權限:
<uses-permission android:name="android.permission.INTERNET" />
懸浮窗權限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
有懸浮窗權限才能顯示:

如果遇到React Native的一些組件不能使用可以嘗試在注冊Activity時添加主題為Theme.AppCompat.Light.NoActionBar,看能否解決問題,因為一些組件依賴于這個主題:
<activity
android:name=".MyReactNativeActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
開發設置界面:

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
運行應用
啟動開發服務器
在項目的根目錄下運行:
npm start

這個命令運行的是我們package.json中配置的:
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
可以用瀏覽器訪問http://localhost:8081/index.android.bundle?platform=android 看看是否可以看到打包后的腳本。
第一次訪問通常需要十幾秒,并且在命令行可以看到進度條。
構建與運行你的程序
兩種方法:
- 直接利用Android Studio像平常一樣運行項目
- 在命令行中項目目錄下運行gradlew installDebug
如果你使用的是Android Studio為你構建而不是Gradle構建(gradlew installDebug),你要確保你在安裝應用之前運行了npm start,以防止它們之間出現沖突。
效果:

MyReactActivity:


MyReactNativeActivity:


在Android Studio中打包成獨立安裝程序(release)
你可以使用Android Studio來創建你的App的發布版本!像以前創建原生應用程序的發布版本一樣簡單,只是有一個額外步驟:
在你打包你的發布版本之前要創建一個bundle文件,這個bundle文件會創建在項目的assets目錄中,并且這個文件會包含在你的apk包中,
在你的項目根目錄中運行:
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/
app/src/main根據實際情況改為自己項目中的目錄,參考assets文件夾的目錄。

結果為:

如果報錯:
ENOENT: no such file or directory
你需要在你的app模塊中建立assets文件夾和index.android.bundle。
現在你可以對你的應用程序進行打包發布了。
debug模式release模式React Native JS代碼調試的區別:
debug模式: 修改完js代碼打開開發者菜單點擊Reload就可以看到更新后的效果,或者是開啟Live Reload(點擊Enable Live Reload)
這樣我們修改了js文件只要保存就會自動Reload。
release模式: 修改完js代碼需要重新生成index.android.bundle 文件,點擊run之后才能看到效果。因為正式版發布后是無法
依賴本地服務器去更新index.android.bundle,需要把index.android.bundle打包到apk中才能運行。
更新React Naive版本
1.打開項目目錄下的package.json文件,然后在dependencies模塊下找到react-native,將當前版本號改到最新(或指定)版本號,如:
{
"name": "reactnativedemo",
"version": "1.0.0",
"description": "",
"main": "index.android.js",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.4.1",
"react-native": "^0.38.0"
}
}
react-native的npm包的最新版本可以去這里查看,或使用npm info react-native命令查看。
2.項目的根目錄執行:
npm install
安裝最新的React Native版本,成功后可能會出現如下類似警告:
npm WARN react-native@0.38.0 requires a peer of react@15.4.1 but none was installed.
3.根據警告執行:
npm install –save react@15.4.1
更新最新的React且項目下package.json 的 dependencies下的react版本會被修改為 15.4.1
4.新版本的npm包通常還會包含一些動態生成的文件,這些文件是在運行react-native init創建新
項目時生成的,比如iOS和Android的項目文件。為了使老項目的項目文件也能得到更新
(不重新init),你需要在命令行中運行:
react-native upgrade
這一命令會檢查最新的項目模板,然后進行如下操作:
- 如果是新添加的文件,則直接創建。
- 如果文件和當前版本的文件相同,則跳過。
- 如果文件和當前版本的文件不同,則會提示你一些選項:查看兩者的不同,選擇保留你的版本或是用新的模板覆蓋。你可以按下h鍵來查看所有可以使用的命令。
注意:如果你有修改原生代碼,那么在使用upgrade升級前,先備份,再覆蓋。覆蓋完成后,使用比對工具找出差異,將你之前修改的代碼逐步搬運到新文件中。
5.執行:
react-native -v
通過如上命令來看最新的版本,檢測是否升級成功!
問題與解決方案
打不開開發者菜單
查看AndroidManifest.xml文件中是否加入了懸浮窗權限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
并且你要在手機上開啟你要調試的應用的懸浮窗權限,不同的手機開啟方式不同,可以自行搜索開啟方法。
錯誤 "/data/data/package-name/lib-main/libgnustl_shared.so" is 32-bit instead of 64-bit
取消掉所有的64位的.so文件,全部加載32位的.so文件。
- 在項目的根目錄的build.gradle中加入:
android.useDeprecatedNdk=true.
- 在項目的模塊(app)中的build.gradle文件中添加:
android {
...
defaultConfig {
...
ndk {
abiFilters "armeabi-v7a", "x86"
}
packagingOptions {
exclude "lib/arm64-v8a/librealm-jni.so"
}
}
}
錯誤:Could not get BatchedBridge,make sure your bundle is packaged correctly
- build模式選擇了release模式,引發的這個錯誤,你可以檢查一下是否是debug模式,如果不是改為debug再試一下。
- ReactInstanceManager的setUseDeveloperSupport(BuildConfig.DEBUG)方法值是否正確,設置開發者支持,BuildConfig.DEBUG的值默認是false,無法使用開發者支持(開發者菜單、即時預覽等),所以我們要把BuildConfig.DEBUG改為true。
錯誤

解決方法:
把Android Studio自動生成的文件夾androidTest和test刪除,并修改項目的模塊(app)的build.gradle文件:
修改前:

修改后:

其他問題可以參考React Native for Android Windows環境搭建
<font size=5>源代碼</font>