項(xiàng)目連接:HelloRN
ReactNative使用手冊(cè)
把React Native組件植入到Android應(yīng)用中官方步驟:
首先,我有一句MMP,不知當(dāng)講不當(dāng)講,這是玩RN以來遇到的巨坑,當(dāng)你看到這篇文章的時(shí)候奔脐,從開始完成這個(gè)項(xiàng)目到現(xiàn)在已經(jīng)過去2天半了俄周,我已經(jīng)按照官方文檔集成在5次以上,已經(jīng)瀏覽文章30篇以上髓迎,包括在# facebook/react-native的issues
看了很多相關(guān)的問題峦朗,最終走出來了
1. 首先當(dāng)然要了解你要植入的React Native組件。
2. 在Android項(xiàng)目根目錄中使用npm來安裝`react-native` 排龄,這樣同時(shí)會(huì)創(chuàng)建一個(gè)`node_modules/`的目錄波势。
3. 創(chuàng)建js文件,編寫React Native組件的js代碼橄维。
4. 在`build.gradle`文件中添加`com.facebook.react:react-native:+`尺铣,以及一個(gè)指向`node_nodules/`目錄中的`react-native`預(yù)編譯庫(kù)的`maven`路徑。
5. 創(chuàng)建一個(gè)React Native專屬的`Activity`争舞,在其中再創(chuàng)建`ReactRootView`凛忿。
6. 啟動(dòng)React Native的Packager服務(wù),運(yùn)行應(yīng)用竞川。
7. 根據(jù)需要添加更多React Native的組件店溢。
8. 在真機(jī)上[運(yùn)行](https://reactnative.cn/docs/0.40/running-on-device-android.html)、[調(diào)試](https://reactnative.cn/docs/0.40/debugging.html)委乌。
9. [打包](https://reactnative.cn/docs/0.40/signed-apk-android.html)床牧。
10. 發(fā)布應(yīng)用,升職加薪遭贸,走向人生巔峰戈咳!
算然官網(wǎng)給出了方法步驟,但是文檔過于簡(jiǎn)單革砸,而且還有很多巨坑除秀,所以本文還是有一定價(jià)值的,當(dāng)然在上面的步驟中有一些我們沒必要關(guān)注算利,但是主要步驟還是有的,接下來就按照這個(gè)步驟去完成把RN嵌入到android原生項(xiàng)目中
把React Native組件植入到Android應(yīng)用
第一步:引入react-native
在androidstudio的Terminal窗口中輸入 npm init
,接著會(huì)提示你輸入一些東西(除了項(xiàng)目名字其他都可直接回車使用默認(rèn)值)泳姐,如下圖
當(dāng)我們輸完的時(shí)候?qū)⒐こ糖袚Q到project模式下效拭,可以看到工程多了一個(gè)package.json的文件
{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "賴天兵",
"license": "ISC"
}
看到這個(gè)文件有一種很熟悉但又陌生的感覺,但是它的作用都應(yīng)該猜到了胖秒,這個(gè)和我們build.gradle中的配置是一樣的作用缎患,其實(shí)就是配置工程的一些屬性
第二步:添加react和react_native模塊
在Terminal窗口中輸入:npm install --save react react-native
并執(zhí)行,然后就是靜靜的等待阎肝,如果有報(bào)錯(cuò)挤渔,自己手動(dòng)敲"--"這個(gè)符號(hào),因?yàn)樵诓煌南到y(tǒng)下“--”可能是不一樣的,完成后就可以看到工程多了一個(gè)node_modules模塊
第三步:在命令行中運(yùn)行curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
(這步對(duì)Windows來說真的坑)
直接運(yùn)行會(huì)提示你:'curl' 不是內(nèi)部或外部命令风题,也不是可運(yùn)行的程序
所以看來我的首先處理Windows運(yùn)行curl命令的問題了
curl是利用URL語(yǔ)法在命令行方式下工作的開源文件傳輸工具判导。它被廣泛應(yīng)用在Unix嫉父、多種Linux發(fā)行版中,并且有DOS和Win32眼刃、Win64下的移植版本绕辖。
所以首先要解決Windows下支持curl
命令的問題Windows下安裝使用curl命令
提示有可能你找不到| curl-7.33.0-win64-ssl-sspi.zip
再提示一下當(dāng)你按照上面Windows下安裝使用curl命令走到了下圖步驟時(shí)
這時(shí)候應(yīng)該在命令窗口中輸入curl -v -X OPTIONS https://www.baidu.com/
,這是文檔沒有說的擂红,而是直接輸入到命令行里了仪际,第一次看如果不知道這個(gè),那會(huì)很懵逼的
不過遺憾的是我按照上門的連接文檔一步步走下來還是無法在任何地方使用curl命令(我的電腦win10 64位)
所以我用了最簡(jiǎn)單的方法昵骤,直接復(fù)制我們下載的curl.exe
到工程根目錄树碱,這樣在本工程的根目錄就能運(yùn)行curl命令了
但是:真想爆粗口,還是生成不了.flowconfig
文件变秦,
所以真正最直接的解決方式來了:自己在項(xiàng)目的根目錄下創(chuàng)建.flowconfig
文本文件成榜,然后打開.flowconfig連接復(fù)制文本到剛剛創(chuàng)建的文件中即可
走到這里突然想感嘆一句,我饒了一大圈到底是為了啥
第三步:在package.json文件中的scripts
里面配置啟動(dòng)腳本"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"
}
}
其實(shí)在官方文檔中第三步已經(jīng)完了伴栓,但是畢竟我是踏過了很多巨坑的伦连,看了很多篇博客的我告訴你這步還沒完,照常理這里還應(yīng)該配置打包用index.android.js生成index.andriod.bundle的配置(你也可以嘗試不做钳垮,絕對(duì)會(huì)出現(xiàn)一些經(jīng)典的bug惑淳,這里就不提了,我會(huì)有一篇專門的文章記錄這些bug)
配置生成發(fā)布饺窿、打包時(shí)所需要bundle文件的配置
在很多博客中會(huì)這樣解決這個(gè)問題歧焦,像上面完整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"
這一句在官方文檔中沒有說明,但是最好加上(雖然我加上后肚医,至少我這里沒有卵用绢馍,據(jù)說是新版本的RN不支持自動(dòng)生成打包所需的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/"
但是這是錯(cuò)的舰涌,打包的時(shí)候我們都要根據(jù)自己項(xiàng)目目錄的結(jié)果做一些調(diào)整,不過在上面package.json中我已經(jīng)做過調(diào)整了你稚,但是還是無法自動(dòng)完成打包bundle
解決:我們先在main下面創(chuàng)建assets文件夾瓷耙,然后進(jìn)入工程根目錄打開cmd輸入
react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false
(注意-output后面的參數(shù)根據(jù)自己的項(xiàng)目目錄結(jié)構(gòu)來寫)
成功后的項(xiàng)目
你或許看過很多在安卓原始項(xiàng)目中嵌套R(shí)N,并按照他們步驟一步步完成了刁赖,但是最后就是看不到效果搁痛,這里就是其中一個(gè)重要的原因
如果這步完成了,那么恭喜你宇弛,你基本上能看到效果了鸡典,后面可能還要出bug,但是都是些容易解決的固定的了
第四步:在項(xiàng)目根目錄中創(chuàng)建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,
},
});
//這里的第一個(gè)參熟名字要和我們創(chuàng)建的這個(gè)工程項(xiàng)目名一樣
AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);
提示:在本系列RN博客的第二篇?jiǎng)?chuàng)建HelloWorld的時(shí)候說過枪芒,registerComponent()注冊(cè)時(shí)名字必須和項(xiàng)目名字保持一致
第五步:添加ReactNative依賴
首先在APP的build.gradle
中添加
dependencies { ... compile "com.facebook.react:react-native:+" // From node_modules. }
其次在工程的build.gradle
中添加進(jìn)入本地ReactNative倉(cāng)庫(kù)的路徑彻况,但是官方源文檔寫的路徑是
$rootDir/../node_modules/react-native/android
如果你直接復(fù)制使用谁尸,恭喜你你又入坑了
因?yàn)槲覀兺ㄟ^之前第二步添加的添加react和react_native模塊,默認(rèn)是項(xiàng)目的根目錄下疗垛,而官方文檔給出的路徑其實(shí)是多了“../”所以這就是一個(gè)坑(說到這里提一句症汹,網(wǎng)上很多文章都是直接用的官方路徑,但是項(xiàng)目結(jié)構(gòu)又和我的一樣贷腕,我真懷疑他們寫那個(gè)文章的時(shí)候只是copy別人文章背镇,自己根本沒有成功,這也是很多人按照別人文章一步步到最后還是報(bào)錯(cuò)的又一個(gè)原因)泽裳,這里路徑應(yīng)該對(duì)應(yīng)自己工程中module的路徑
我的工程目錄
所以我的路徑應(yīng)該是
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/node_modules/react-native/android"
}
}
...
}
添加完成后記得同步以下哦瞒斩,不過等待的時(shí)間過于漫長(zhǎng)(最好開VPN,我是開了VPN才同步完成的)涮总,可以先往后繼續(xù)看文章
第六步:在清單文件中添加網(wǎng)絡(luò)請(qǐng)求權(quán)限,必須添加的
官方原話Next, make sure you have the Internet permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
以及調(diào)試需要用到的權(quán)限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>
第七步:在清單文件中注冊(cè)DevSettingsActivity
,此步驟可以省略,功能就是重載JavaScript
如果您需要訪問DevSettingsActivity添加到您的AndroidManifest.xml:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
這只在開發(fā)服務(wù)器重新加載JavaScript時(shí)才真正用于開發(fā)模式胸囱,因此,如果需要瀑梗,可以在發(fā)布版本中將其剝離烹笔。
第八步:把之前項(xiàng)目自動(dòng)創(chuàng)建的MainActivity中代碼改成如下代碼,但是注意當(dāng)APP需要支持5.0以下的機(jī)型那么需要使用com.android.support:appcompat
包中的AppCompatActivity
類抛丽,而不能直接使用Activity
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")
//這里需要注意谤职,官方文檔setJSMainModuleName在新 版本中找不到,替換為setJSMainModulePath
// .setJSMainModuleName("index.android")
//這里的路徑是相對(duì)于根目錄的亿鲜,填入index.android即可
.setJSMainModulePath("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//注意這里的`moduleName`參數(shù)必須和工程名字允蜈,也是就是在`index.android.js`中AppRegistry.registerComponent()注冊(cè)的名字一樣
mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
接下來(這個(gè)步驟是可以跳過的),我們需要將一些活動(dòng)生命周期回調(diào)傳遞給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官網(wǎng)中代碼的話蒿柳,是會(huì)出錯(cuò)的饶套,因?yàn)镽eactNative版本更新了,方法名字更改和安卓更加同步了
(也是可跳過的)我們還需要將按鈕事件傳遞給React Native:
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
作用:這允許JavaScript控制用戶按下硬件后退按鈕時(shí)發(fā)生的情況(例如垒探,實(shí)現(xiàn)導(dǎo)航)妓蛮。當(dāng)JavaScript不處理背按時(shí),您的invokeDefaultOnBackPressed方法將被調(diào)用圾叼。默認(rèn)情況下仔引,這只是完成你的Activity。
最后(這個(gè)步驟也是可以跳過的)褐奥,我們需要連接開發(fā)菜單。默認(rèn)情況下翘簇,這是通過(憤怒)激發(fā)設(shè)備來激活的撬码,但這在模擬器中并不是很有用。所以我們?cè)诎聪掠布藛伟粹o時(shí)顯示它(Ctrl + M如果您使用的是Android Studio模擬器版保,請(qǐng)使用它):
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
第九步(此步驟可以跳過):配置權(quán)限以便開發(fā)中的紅屏錯(cuò)誤能正確顯示
如果您的應(yīng)用定位到Android API level 23
或更高版本,請(qǐng)確保您已overlay
為開發(fā)版本啟用權(quán)限。你可以檢查它Settings.canDrawOverlays(this);
震嫉。這在開發(fā)版本中是必需的行您,因?yàn)楸仨氃谒衅渌翱谥巷@示原始開發(fā)錯(cuò)誤。由于在API級(jí)別23中引入了新的權(quán)限系統(tǒng)龄砰,用戶需要批準(zhǔn)它。這可以通過將以下代碼添加到onCreate()方法中的Activity文件中來實(shí)現(xiàn)。OVERLAY_PERMISSION_REQ_CODE是將負(fù)責(zé)將結(jié)果傳遞回活動(dòng)的類的字段微谓。
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的權(quán)限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)) {
// SYSTEM_ALERT_WINDOW permission not granted...
}
}
}
}
最后在明確下index.android.js和mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null)
和package.json及項(xiàng)目名字豺型,到底那幾個(gè)保持一致(我也是集成了5次以上,總結(jié)出來的买乃,如果有不對(duì)請(qǐng)留言指出)
必須一樣的:工程名字(HelloRN)和AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);
以及mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
三者必須一樣姻氨。
當(dāng)你出現(xiàn)以下錯(cuò)誤,基本就是這個(gè)原因了
而package.json中的"name": "hellorn",
算然給我的感覺應(yīng)該也和其他一樣才對(duì)剪验,但是確實(shí)可以不一樣肴焊,而且當(dāng)你在第一步的時(shí)候輸入項(xiàng)目名字,如果輸了大寫功戚,還提示你必須小寫娶眷。而且我們?cè)诔跏蓟臅r(shí)候"main": "index.js",
這一步默認(rèn)是index.js
,從字段來看我們后面創(chuàng)建的index.android.js名字也應(yīng)該叫“index.js”才對(duì)疫铜,畢竟package.json類似于我們的配置文件茂浮,但是我們沒這樣做,發(fā)現(xiàn)也沒有任何問題壳咕。
運(yùn)行應(yīng)用
- 首先運(yùn)行服務(wù)
在項(xiàng)目的根文件夾下席揽,命令行運(yùn)行如下命令,啟動(dòng)測(cè)試服務(wù)器(可以直接在Androidstudio的Termin中輸入谓厘,但是注意當(dāng)前所在位置)幌羞。
npm start
或者:
react-native start
- 接下來像開發(fā)安卓項(xiàng)目一樣直接運(yùn)行項(xiàng)目
效果
當(dāng)出現(xiàn)這個(gè)界面時(shí)別提多嗨皮了,哭去了
如果報(bào)錯(cuò)
或是
解決:在app的build.gradle 中添加
|
|
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
喜歡請(qǐng)點(diǎn)贊竟稳,或是關(guān)注属桦,后續(xù)將完善發(fā)布更多的文章,你的鼓勵(lì)就是我的動(dòng)力(程序員最大的動(dòng)力莫過于同行的鼓勵(lì))