React Native嵌入Android原生項目中
開發(fā)環(huán)境準備
首先你要搭建好React Native for Android開發(fā)環(huán)境, 沒有搭建好的可以參考:React Native for Android Windows環(huán)境搭建
用Android Studio新建Android原生項目
我創(chuàng)建了一個名叫ReactNativeDemo的原生項目粟耻。
把React Native集成到原生項目當(dāng)中
利用Windows命令行在項目根目錄(ReactNativeDemo文件夾)下執(zhí)行下面三行命令:
npm init
npm install --save react react-native
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
這將在項目根目錄(ReactNativeDemo文件夾)創(chuàng)建node_modules文件夾(模塊)朱转,并添加React Native依賴妓柜。
針對上面三條命令的解釋
npm init
注意:
name的填寫由圖可知填默認的是不行的,它的要求是不能有大寫字母并且不能以數(shù)字開頭;
entry point的填寫入口文件名稱瓢谢,默認的是index.js,我們建立的入口文件是index.android.js驮瞧,所以填寫index.android.js氓扛。只要填寫的名稱與自己定義的入口文件名稱一致就行。
其他的項根據(jù)自己需求填寫即可论笔。
這個步驟會在項目的根目錄產(chǎn)生一個名稱為package.json的文件采郎,我們還需要修改我們的package.json文件:
在"scripts"節(jié)點下添加"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節(jié)點是自動生成的狂魔,我們可以把它刪除蒜埋,最后我的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語法在命令行方式下工作的開源文件傳輸工具。它被廣泛應(yīng)用在Unix最楷、多種Linux發(fā)行版中整份,并且有DOS和Win32、Win64下的移植版本籽孙。
所以可知上面這句話的意思是在對應(yīng)網(wǎng)址下下載.flowconfig文件烈评。
在windows下我們要使用curl命令會提示:curl不是內(nèi)部和外部命令,也不是可執(zhí)行文件或批處理命令犯建。讲冠。。
我們在windows下要使用curl命令比較麻煩胎挎。解決方法就是我們用下載工具從https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig 上把.flowconfig下載下來復(fù)制到項目根目錄沟启,或者是在項目根目錄下新建一個.flowconfig文件用瀏覽器訪問這個網(wǎng)址其中的內(nèi)容把其中的內(nèi)容復(fù)制到文件當(dāng)中忆家。
建立index.android.js文件
在項目的根目錄建立index.android.js文件并把下面的代碼復(fù)制進去:
'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路徑根據(jù)實際的來寫德迹,本文的如下:
url "$rootDir/node_modules/react-native/android"
}
}
}
注意:Android項目默認的依賴包的源為jcenter()芽卿,其中并不包含最新版的 React Native(它只到0.20.1)。
新版的React Native只在npm里發(fā)布胳搞,所以你需要增加一下依賴包的源卸例。在編譯完后,檢查項目External Libraries的
react-native版本如果為0.20.1肌毅,則說明maven的依賴源沒有添加成功筷转。這時候應(yīng)該是maven的路徑出問題了,你要檢查
路徑是否正確悬而,正確的結(jié)果為:
在項目的模塊(app)中的build.gradle文件中添加:
文件頭添加(可選):
apply from: "$rootDir/node_modules/react-native/react.gradle"
無法編譯通過的時候可以嘗試添加上面這句呜舒。
dependencies {
...
compile "com.facebook.react:react-native:+"
}
如果你想總是使用一個特定的版本,你需要把+替換成你已經(jīng)下載的React Native的版本號笨奠,
這個版本號應(yīng)該與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") //對應(yīng)index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發(fā)者支持,BuildConfig.DEBUG的值默認是false般婆,無法使用開發(fā)者菜單
.setUseDeveloperSupport(true) //開發(fā)者支持,開發(fā)的時候要設(shè)置為true到腥,不然無法使用開發(fā)者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應(yīng)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) {
//當(dāng)我們點擊菜單的時候打開發(fā)者菜單,一個彈窗(此處需要懸浮窗權(quán)限才能顯示)
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") //對應(yīng)index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發(fā)者支持蔚袍,BuildConfig.DEBUG的值默認是false乡范,無法使用開發(fā)者菜單
.setUseDeveloperSupport(true) //開發(fā)者支持,開發(fā)的時候要設(shè)置為true,不然無法使用開發(fā)者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應(yīng)index.android.js中AppRegistry.registerComponent('ReactNativeView', () => ReactNativeView)的ReactNativeView
mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeView", null);
setContentView(mReactRootView);
}
的setUseDeveloperSupport(BuildConfig.DEBUG)方法啤咽,設(shè)置開發(fā)者支持晋辆,BuildConfig.DEBUG的值默認是false,無法使用開發(fā)者支持(開發(fā)者菜單闰蚕、即時預(yù)覽等)栈拖,所以我們要把BuildConfig.DEBUG改為true。
另一種原生Activity寫法
MyReactNativeActivity
import com.facebook.react.ReactActivity;
public class MyReactNativeActivity extends ReactActivity {
/**
* 這里的ReactNativeView對應(yīng)index.android.js中AppRegistry.registerComponent('ReactNativeView', () => Root)的ReactNativeView
*/
@Override
protected String getMainComponentName() {
return "ReactNativeView";
}
}
兩種原生Activity寫法的對比:
第一種(官方例子的)
這種寫法的優(yōu)勢是可以利用React Native來寫我們界面中的某一塊區(qū)域没陡,就是利用原生布局的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")//對應(yīng)index.android.js
.addPackage(new MainReactPackage())
//.setUseDeveloperSupport(BuildConfig.DEBUG) //開發(fā)者支持,BuildConfig.DEBUG的值默認是false盼玄,無法使用開發(fā)者菜單
.setUseDeveloperSupport(true) //開發(fā)者支持,開發(fā)的時候要設(shè)置為true贴彼,不然無法使用開發(fā)者菜單
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//這里的ReactNativeView對應(yīng)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);
第二種
這種方式是優(yōu)勢是寫法簡單。但是無法局部使用React Native來布局埃儿。
AndroidManifest.xml相關(guān)
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>
網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
懸浮窗權(quán)限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
有懸浮窗權(quán)限才能顯示:
如果遇到React Native的一些組件不能使用可以嘗試在注冊Activity時添加主題為Theme.AppCompat.Light.NoActionBar器仗,看能否解決問題,因為一些組件依賴于這個主題:
<activity
android:name=".MyReactNativeActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
開發(fā)設(shè)置界面:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
運行應(yīng)用
啟動開發(fā)服務(wù)器
在項目的根目錄下運行:
npm start
這個命令運行的是我們package.json中配置的:
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
可以用瀏覽器訪問http://localhost:8081/index.android.bundle?platform=android 看看是否可以看到打包后的腳本。
第一次訪問通常需要十幾秒精钮,并且在命令行可以看到進度條威鹿。
構(gòu)建與運行你的程序
兩種方法:
- 直接利用Android Studio像平常一樣運行項目
- 在命令行中項目目錄下運行g(shù)radlew installDebug
如果你使用的是Android Studio為你構(gòu)建而不是Gradle構(gòu)建(gradlew installDebug),你要確保你在安裝應(yīng)用之前運行了npm start轨香,以防止它們之間出現(xiàn)沖突忽你。
效果:
MyReactActivity:
MyReactNativeActivity:
在Android Studio中打包成獨立安裝程序(release)
你可以使用Android Studio來創(chuàng)建你的App的發(fā)布版本!像以前創(chuàng)建原生應(yīng)用程序的發(fā)布版本一樣簡單臂容,只是有一個額外步驟:
在你打包你的發(fā)布版本之前要創(chuàng)建一個bundle文件科雳,這個bundle文件會創(chuàng)建在項目的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根據(jù)實際情況改為自己項目中的目錄糟秘,參考assets文件夾的目錄。
結(jié)果為:
如果報錯:
ENOENT: no such file or directory
你需要在你的app模塊中建立assets文件夾和index.android.bundle球散。
現(xiàn)在你可以對你的應(yīng)用程序進行打包發(fā)布了尿赚。
debug模式release模式React Native JS代碼調(diào)試的區(qū)別:
debug模式: 修改完js代碼打開開發(fā)者菜單點擊Reload就可以看到更新后的效果霍比,或者是開啟Live Reload(點擊Enable Live Reload)
這樣我們修改了js文件只要保存就會自動Reload最欠。
release模式: 修改完js代碼需要重新生成index.android.bundle 文件,點擊run之后才能看到效果。因為正式版發(fā)布后是無法
依賴本地服務(wù)器去更新index.android.bundle嘁灯,需要把index.android.bundle打包到apk中才能運行。
更新React Naive版本
1.打開項目目錄下的package.json文件躲舌,然后在dependencies模塊下找到react-native丑婿,將當(dāng)前版本號改到最新(或指定)版本號,如:
{
"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.項目的根目錄執(zhí)行:
npm install
安裝最新的React Native版本,成功后可能會出現(xiàn)如下類似警告:
npm WARN react-native@0.38.0 requires a peer of react@15.4.1 but none was installed.
3.根據(jù)警告執(zhí)行:
npm install –save react@15.4.1
更新最新的React且項目下package.json 的 dependencies下的react版本會被修改為 15.4.1
4.新版本的npm包通常還會包含一些動態(tài)生成的文件,這些文件是在運行react-native init創(chuàng)建新
項目時生成的约计,比如iOS和Android的項目文件诀拭。為了使老項目的項目文件也能得到更新
(不重新init),你需要在命令行中運行:
react-native upgrade
這一命令會檢查最新的項目模板煤蚌,然后進行如下操作:
- 如果是新添加的文件耕挨,則直接創(chuàng)建。
- 如果文件和當(dāng)前版本的文件相同尉桩,則跳過筒占。
- 如果文件和當(dāng)前版本的文件不同,則會提示你一些選項:查看兩者的不同蜘犁,選擇保留你的版本或是用新的模板覆蓋翰苫。你可以按下h鍵來查看所有可以使用的命令。
注意:如果你有修改原生代碼,那么在使用upgrade升級前奏窑,先備份导披,再覆蓋。覆蓋完成后埃唯,使用比對工具找出差異盛卡,將你之前修改的代碼逐步搬運到新文件中。
5.執(zhí)行:
react-native -v
通過如上命令來看最新的版本筑凫,檢測是否升級成功滑沧!
問題與解決方案
打不開開發(fā)者菜單
查看AndroidManifest.xml文件中是否加入了懸浮窗權(quán)限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
并且你要在手機上開啟你要調(diào)試的應(yīng)用的懸浮窗權(quán)限,不同的手機開啟方式不同巍实,可以自行搜索開啟方法滓技。
錯誤 "/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模式令漂,引發(fā)的這個錯誤,你可以檢查一下是否是debug模式丸边,如果不是改為debug再試一下叠必。
- ReactInstanceManager的setUseDeveloperSupport(BuildConfig.DEBUG)方法值是否正確,設(shè)置開發(fā)者支持妹窖,BuildConfig.DEBUG的值默認是false纬朝,無法使用開發(fā)者支持(開發(fā)者菜單、即時預(yù)覽等)骄呼,所以我們要把BuildConfig.DEBUG改為true共苛。
錯誤
解決方法:
把Android Studio自動生成的文件夾androidTest和test刪除,并修改項目的模塊(app)的build.gradle文件:
修改前:
修改后:
其他問題可以參考React Native for Android Windows環(huán)境搭建
<font size=5>源代碼</font>