- 背景
近期由于項目需要,在已經(jīng)開發(fā)了幾個版本的原生App中集成了ReactNative,新版的模塊都使用RN開發(fā)。
此次集成的工作大部分是參照RN中文文檔進行的,但是中文文檔也有一些坑和描述不充分的地方,所以我參照著文檔把自己集成的過程以及遇到的問題整理了下,希望能給遇到相同問題的人起到參考作用。
ReactNative中文文檔官方地址:http://reactnative.cn/docs/0.45/integration-with-existing-apps.html#content - 集成過程
2.1 Android
2.1.1 在應用中添加js代碼
打開控制臺,進入項目根目錄,執(zhí)行以下幾個命令:
$npm init
$npm install --save react react-native
$curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
npm init命令是創(chuàng)建package.json文件,之后會要求輸入信息,除了name之外其他的直接按enter默認就行了。
npm install則創(chuàng)建了node_modules目錄并把react和react-native下載到了其中。執(zhí)行此命令時,要是控制臺輸出了版本不一致的警告信息,例如:
npm WARN react-native@0.45.1 requires a peer of react@16.0.0-alpha.12 but none was installed.
則繼續(xù)執(zhí)行 npm i -S react@16.0.0-alpha.12 (這里版本跟警告信息一致)。
至于第三步curl命令,其實質(zhì)是下載.flowconfig配置文件,這個文件用于約束js代碼的寫法。非必需,可跳過。
執(zhí)行完成之后我的package.json是這樣的:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.0.0-alpha.12",
"react-native": "^0.45.1"
}
}
接下來還需要把啟動腳本放進去:
"start": "node node_modules/react-native/local-cli/cli.js start"
注意下啟動腳本的路徑,如果集成的項目目錄結構不一樣的話會報找不到啟動腳本之類的錯誤,到時根據(jù)具體目錄結構修改就好了。
2.1.2 在項目根目錄下創(chuàng)建index.android.js文件:
'use strict';
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class test extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, My Hybrid App!</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('test', () => test);
2.1.3 打開你的android項目,在app中 build.gradle 文件中添加 React Native 依賴:
dependencies {
...
compile "com.facebook.react:react-native:+" // From node_modules.
}
接著,在 AndroidManifest.xml 清單文件中聲明網(wǎng)絡權限:
<uses-permission android:name="android.permission.INTERNET" />
如果需要訪問 DevSettingsActivity 界面,也需要在 AndroidManifest.xml 中聲明:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
在項目的 build.gradle 文件中為 React Native 添加一個 maven 依賴的入口,必須寫在 "allprojects" 代碼塊中:
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "../node_modules/react-native/android"
}
}
...
}
這里maven路徑官網(wǎng)上寫的是$rootDir/../node_modules/react-native/android,這個跟目錄結構有關,跟我一樣在根目錄上操作的話是不需要$rootDir的
2.1.4 同步gradle
遇到以下錯誤:
Warning:Conflict with dependency 'com.google.code.findbugs:jsr305'. Resolved versions for app (3.0.0) and test app (2.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.
在build.bundle里面加入:
allprojects {
...
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
}
}
2.1.5 添加原生代碼
2.1.5.1 新建一個Activity,代碼如下:
public class MyReactActivity 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("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// 注意這里的HelloWorld必須對應“index.android.js”中的
// “AppRegistry.registerComponent()”的第一個參數(shù)
mReactRootView.startReactApplication(mReactInstanceManager, "test", 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();
}
}
@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);
}
}
2.1.5.2 在AndroidManifest.xml中聲明:
<activity
android:name=".MyReactActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
應用在真機上調(diào)試時需開啟懸浮窗權限才能正確顯示紅屏錯誤,官網(wǎng)上有前往開啟的代碼,但是我覺得有點麻煩了,直接進應用管理中心開啟就好了,真正發(fā)布的時候也不需要顯示紅屏錯誤。
2.1.6 運行程序
2.1.6.1 在根目錄下使用npm start命令開啟后臺服務,然后運行應用,不出意外的話應該可以看到一片紅色。
這是后臺啟動的窗口:

App首次啟動頁面:

紅屏沒有出現(xiàn)的話注意檢查下懸浮窗權限是否開啟。
搖晃下手機,會出現(xiàn)以下界面:

點擊Dev Setting進入設置頁面,再點擊 Debug server host &...設置服務器的IP跟端口,這個時候要注意下手機跟電腦要在同一個局域網(wǎng)下:

設置之后返回到頁面,再次搖晃下手機,就出現(xiàn)成功的頁面啦!


2.2 iOS
2.2.1 新建package.json文件
可以新建也可以直接前面android項目的package.json拷貝到根目錄下。
注意這里iOS使用的最新版本的react native在安裝pod之后會有點問題,可能是官網(wǎng)上的中文文檔沒有及時更新的原因,我這里暫時用的還是之前我集成的版本:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ../ExampleIOS/node_modules/react-native/local-cli/cli.js start"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.0.0-alpha.6",
"react-native": "^0.43.4"
}
}
官網(wǎng)在一開始建議把iOS代碼都放到一個 ios/文件夾下,但這對于一個已經(jīng)開發(fā)多次的項目來說操作并不簡單,所以這一步也可以不做,只需把package.json里面啟動腳本的路徑修改一下即可。
修改之后的路徑為:
"start": "node ../ExampleIOS/node_modules/react-native/local-cli/cli.js start"
2.2.2 安裝依賴包
在根目錄下執(zhí)行命令:
npm install
安裝過程中可能會遇到跟2.1.1一樣的警告信息,這時操作也一樣。
2.2.3 安裝pod
2.2.3.1 如果你的項目還沒安裝pod則先執(zhí)行pod init,然后在創(chuàng)建了的Podfile文件下加入:
# 'node_modules'目錄一般位于根目錄中
# 但是如果你的結構不同,那你就要根據(jù)實際路徑修改下面的`:path`
pod 'React', :path => '../ExampleIOS/node_modules/react-native', :subspecs => [
'Core',
'DevSupport', # 如果RN版本 >= 0.43,則需要加入此行才能開啟開發(fā)者菜單
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 這個模塊是用于調(diào)試功能的
# 在這里繼續(xù)添加你所需要的模塊
]
# 如果你的RN版本 >= 0.42.0,請加入下面這行
pod "Yoga", :path => "../ExampleIOS/node_modules/react-native/ReactCommon/yoga"
然后執(zhí)行pod install 命令開始安裝??吹揭韵聢D片就說明安裝成功了。

2.2.3.2 如果你已經(jīng)安裝了pod,則直接在Podfile中加入上面的內(nèi)容,然后使用
pod update --verbose --no-repo-update命令更新pod就可以了。在這里有個需要注意的地方,安裝pod之后就要關閉項目,使用.xcworkspace文件打開項目了。
2.2.4 代碼集成
2.2.4.1 創(chuàng)建index.ios.js文件
在項目根目錄下創(chuàng)建index.ios.js文件,內(nèi)容跟之前的index.android.js一樣即可。
'use strict';
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class test extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, My Hybrid App!</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('test', () => test);
2.2.4.2 在需要挑戰(zhàn)的ViewContrller中引入RCTRootView.h,跳轉(zhuǎn)代碼:
NSURL *jsCodeLocation = [NSURL
URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"test"
initialProperties :@{}
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self presentViewController:vc animated:YES completion:nil];
2.2.5 使用npm start開啟后臺,啟動程序:


由于蘋果的安全訪問限制可能會出現(xiàn)訪問不到的原因,這時候需要往info.plist加入下面這段代碼:
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
然后重新運行程序就可以看到以下結果啦??!


- 大功告成!接下來我把項目上傳到github,有需要的可以下下來參考下:
Android:
https://git.oschina.net/GStick/androidiosfeatreactnative.git
iOS:
https://git.oschina.net/GStick/androidiosfeatreactnative.git - 之后會繼續(xù)更新集成之后開發(fā)中用到的東西,像加載離線bundle文件、交互之類的,有時間的話還是Android/iOS雙管齊下,敬請期待。