項目連接:https://github.com/93Laer/HelloRN HelloRN
把React Native組件植入到Android應用中官方步驟:首先,我有一句MMP,不知當講不當講,這是玩RN以來遇到的巨坑,當你看到這篇文章的時候,從開始完成這個項目到現在已經過去2天半了,我已經按照官方文檔集成在5次以上,已經瀏覽文章30篇以上,包括在facebook/react-native的issues看了很多相關的問題,最終走出來了
1. 首先當然要了解你要植入的React Native組件。
2. 在Android項目根目錄中使用npm來安裝`react-native` ,這樣同時會創建一個`node_modules/`的目錄。
3. 創建js文件,編寫React Native組件的js代碼。
4. 在`build.gradle`文件中添加`com.facebook.react:react-native:+`,以及一個指向`node_nodules/`目錄中的`react-native`預編譯庫的`maven`路徑。
5. 創建一個React Native專屬的`Activity`,在其中再創建`ReactRootView`。
6. 啟動React Native的Packager服務,運行應用。
7. 根據需要添加更多React Native的組件。
8. 在真機上[運行](https://reactnative.cn/docs/0.40/running-on-device-android.html)、[調試](https://reactnative.cn/docs/0.40/debugging.html)。
9. [打包](https://reactnative.cn/docs/0.40/signed-apk-android.html)。
10. 發布應用,升職加薪,走向人生巔峰!
算然官網給出了方法步驟,但是文檔過於簡單,而且還有很多巨坑,所以本文還是有一定價值的,當然在上面的步驟中有一些我們沒必要關注,但是主要步驟還是有的,接下來就按照這個步驟去完成把RN嵌入到android原生項目中
把React Native組件植入到Android應用第一步:引入react-native在androidstudio的Terminal窗口中輸入 npm init,接著會提示你輸入一些東西(除了項目名字其他都可直接回車使用默認值),如下圖
當我們輸完的時候將工程切換到project模式下,可以看到工程多了一個package.json的文件
{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "賴天兵",
"license": "ISC"
}
看到這個文件有一種很熟悉但又陌生的感覺,但是它的作用都應該猜到了,這個和我們build.gradle中的配置是一樣的作用,其實就是配置工程的一些屬性
第二步:添加react和react_native模塊在Terminal窗口中輸入:npm install --save react react-native並執行,然後就是靜靜的等待,如果有報錯,自己手動敲"--"這個符號,因為在不同的系統下「--」可能是不一樣的,完成後就可以看到工程多了一個node_modules模塊
第三步:在命令行中運行curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig(這步對Windows來說真的坑)直接運行會提示你:'curl' 不是內部或外部命令,也不是可運行的程序
所以看來我的首先處理Windows運行curl命令的問題了
curl是利用URL語法在命令行方式下工作的開源文件傳輸工具。它被廣泛應用在Unix、多種Linux發行版中,並且有DOS和Win32、Win64下的移植版本。
所以首先要解決Windows下支持curl命令的問題Windows下安裝使用curl命令
提示有可能你找不到| curl-7.33.0-win64-ssl-sspi.zip
再提示一下當你按照上面Windows下安裝使用curl命令走到了下圖步驟時
這時候應該在命令窗口中輸入curl -v -X OPTIONS https://www.baidu.com/,這是文檔沒有說的,而是直接輸入到命令行裡了,第一次看如果不知道這個,那會很懵逼的
不過遺憾的是我按照上門的連接文檔一步步走下來還是無法在任何地方使用curl命令(我的電腦win10 64位)
所以我用了最簡單的方法,直接複製我們下載的curl.exe到工程根目錄,這樣在本工程的根目錄就能運行curl命令了
但是:真想爆粗口,還是生成不了.flowconfig文件,
所以真正最直接的解決方式來了:自己在項目的根目錄下創建.flowconfig文本文件,然後打開 .flowconfig連接複製文本到剛剛創建的文件中即可走到這裡突然想感嘆一句,我饒了一大圈到底是為了啥
第三步:在package.json文件中的scripts裡面配置啟動腳本"start": "node node_modules/react-native/local-cli/cli.js start",{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node node_modules/react-native/local-cli/cli.js start",
"bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
},
"author": "賴天兵",
"license": "ISC",
"dependencies": {
"react": "^16.2.0",
"react-native": "^0.53.3"
}
}
其實在官方文檔中第三步已經完了,但是畢竟我是踏過了很多巨坑的,看了很多篇博客的我告訴你這步還沒完,照常理這裡還應該配置打包用index.android.js生成index.andriod.bundle的配置(你也可以嘗試不做,絕對會出現一些經典的bug,這裡就不提了,我會有一篇專門的文章記錄這些bug)
配置生成發布、打包時所需要bundle文件的配置在很多博客中會這樣解決這個問題,像上面完整package.json中的scripts代碼一樣添加
, "bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
這一句在官方文檔中沒有說明,但是最好加上(雖然我加上後,至少我這裡沒有卵用,據說是新版本的RN不支持自動生成打包所需的bundle文件了),但是這裡需要修改一下,很多博客都直接是
"bundle-android": "react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output android/app/src/main/assets/index.android.bundle –sourcemap-output android/app/src/main/assets/index.android.map –assets-dest android/app/src/main/res/"
但是這是錯的,打包的時候我們都要根據自己項目目錄的結果做一些調整,不過在上面package.json中我已經做過調整了,但是還是無法自動完成打包bundle
解決:我們先在main下面創建asserts文件夾,然後進入工程根目錄打開cmd輸入
react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false(注意-output後面的參數根據自己的項目目錄結構來寫)
成功後的項目
你或許看過很多在安卓原始項目中嵌套RN,並按照他們步驟一步步完成了,但是最後就是看不到效果,這裡就是其中一個重要的原因
如果這步完成了,那麼恭喜你,你基本上能看到效果了,後面可能還要出bug,但是都是些容易解決的固定的了
第四步:在項目根目錄中創建index.android.js文件在index.android.js中編寫我們的代碼
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class HelloWorldApp extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello world! I am from ReactNattive!!</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);
提示:在本系列RN博客的第二篇創建HelloWorld的時候說過,registerComponent()註冊時名字必須和項目名字保持一致
第五步:添加ReactNative依賴首先在APP的build.gradle中添加
dependencies { ... compile "com.facebook.react:react-native:+" // From node_modules. }
其次在工程的build.gradle中添加進入本地ReactNative倉庫的路徑,但是官方源文檔寫的路徑是
$rootDir/../node_modules/react-native/android
如果你直接複製使用,恭喜你你又入坑了
因為我們通過之前第二步添加的添加react和react_native模塊,默認是項目的根目錄下,而官方文檔給出的路徑其實是多了「../」所以這就是一個坑(說到這裡提一句,網上很多文章都是直接用的官方路徑,但是項目結構又和我的一樣,我真懷疑他們寫那個文章的時候只是copy別人文章,自己根本沒有成功,這也是很多人按照別人文章一步步到最後還是報錯的又一個原因),這裡路徑應該對應自己工程中module的路徑
我的工程目錄
所以我的路徑應該是
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/node_modules/react-native/android"
}
}
...
}
添加完成後記得同步以下哦,不過等待的時間過於漫長(最好開VPN,我是開了VPN才同步完成的),可以先往後繼續看文章
第六步:在清單文件中添加網絡請求權限,必須添加的官方原話Next, make sure you have the Internet permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
以及調試需要用到的權限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>
如果您需要訪問DevSettingsActivity添加到您的AndroidManifest.xml:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
這只在開發伺服器重新加載JavaScript時才真正用於開發模式,因此,如果需要,可以在發布版本中將其剝離。
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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 MainActivity 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())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
接下來(這個步驟是可以跳過的),我們需要將一些活動生命周期回調傳遞給ReactInstanceManager:
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onDestroy();
}
}
注意:如果你直接copy官網中代碼的話,是會出錯的,因為ReactNative版本更新了,方法名字更改和安卓更加同步了
(也是可跳過的)我們還需要將按鈕事件傳遞給React Native:
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
作用:這允許JavaScript控制用戶按下硬體後退按鈕時發生的情況(例如,實現導航)。當JavaScript不處理背按時,您的invokeDefaultOnBackPressed方法將被調用。默認情況下,這只是完成你的Activity。
最後(這個步驟也是可以跳過的),我們需要連接開發菜單。默認情況下,這是通過(憤怒)激發設備來激活的,但這在模擬器中並不是很有用。所以我們在按下硬體菜單按鈕時顯示它(Ctrl + M如果您使用的是Android Studio模擬器,請使用它):
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
如果您的應用定位到Android API level 23或更高版本,請確保您已overlay為開發版本啟用權限。你可以檢查它Settings.canDrawOverlays(this);。這在開發版本中是必需的,因為必須在所有其他窗口之上顯示原始開發錯誤。由於在API級別23中引入了新的權限系統,用戶需要批准它。這可以通過將以下代碼添加到onCreate()方法中的Activity文件中來實現。OVERLAY_PERMISSION_REQ_CODE是將負責將結果傳遞迴活動的類的欄位。
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);
}
}
最後,onActivityResult()必須重寫該方法(如下面的代碼所示)以處理一致UX的權限Accepted或Denied。
@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)) {
}
}
}
}
最後在明確下index.android.js和mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null)和package.json及項目名字,到底那幾個保持一致(我也是集成了5次以上,總結出來的,如果有不對請留言指出)
必須一樣的:工程名字(HelloRN)和AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);以及mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);三者必須一樣。
當你出現以下錯誤,基本就是這個原因了
而package.json中的"name": "hellorn",算然給我的感覺應該也和其他一樣才對,但是確實可以不一樣,而且當你在第一步的時候輸入項目名字,如果輸了大寫,還提示你必須小寫。而且我們在初始化的時候"main": "index.js",這一步默認是index.js,從欄位來看我們後面創建的index.android.js名字也應該叫「index.js」才對,畢竟package.json類似於我們的配置文件,但是我們沒這樣做,發現也沒有任何問題。
運行應用npm start
或者:
react-native start
當出現這個界面時別提多嗨皮了,哭去了
如果報錯或是
解決:在app的build.gradle 中添加{
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
喜歡請點讚,或是關注,後續將完善發布更多的文章,你的鼓勵就是我的動力(程式設計師最大的動力莫過於同行的鼓勵)