開發(fā)語言:ReactNative 0.59.5 Android
開發(fā)環(huán)境:VSCode Android Studio 3.4
1、項目目錄
首先,我們按照建立一下目錄結(jié)構(gòu),其中:
Code目錄放置所有公用的ReactNative腳本,包,以及相關(guān)配置。
Android目錄放置原Android項目。
Code (根目錄)
--Android(一級目錄)
2、開發(fā)環(huán)境準備
2.1、package.json配置
在Code目錄下創(chuàng)建package.json文件,編輯文件輸入以下內(nèi)容。
{
"name": "AppName",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}
2.2、React和React Native模塊安裝
在Code目錄下使用控制臺執(zhí)行以下語句來安裝React Native。
yarn add react-native
- 注意,執(zhí)行完以上命令后,可能會出現(xiàn)以下提示內(nèi)容,表示我們需要安裝指定版本的React(此例子中需要安裝版本為16.8.3的React)。
warning " > react-native@0.59.5" has unmet peer dependency "react@16.8.3".
在Code目錄下使用控制臺執(zhí)行以下語句來安裝指定版本的React
yarn add react@16.8.3
3、配置maven
3.1、在app的build.gradle文件中添加React Native依賴
dependencies {
...
implementation "com.facebook.react:react-native:+" // 新增React Native依賴
}
3.2、在項目的build.gradle文件中添加maven的依賴入口
allprojects {
repositories {
...
maven { url "$rootDir/../../node_modules/react-native/android"} //注意url路徑應(yīng)該指向根目錄code中的node_modules,
}
}
3.3、在AndroidManifest.xml聲明網(wǎng)絡(luò)權(quán)限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.reactnativedemo" >
<uses-permission android:name="android.permission.INTERNET" /> //新增網(wǎng)絡(luò)權(quán)限
...
</manifest>
3.4、在AndroidManifest.xml配置調(diào)試Activity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.reactnativedemo" >
<uses-permission android:name="android.permission.INTERNET" />
<application
...
//調(diào)試Activiy,使用搖一搖呼出
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
4、腳本創(chuàng)建
在Code根目錄下創(chuàng)建Scripts文件夾,用于存放React Native的腳本文件
Code (根目錄)
--Scripts(一級目錄,用于存放所有React Native的腳本)
然后我們可以在Scripts目錄下開始寫ReactNative的腳本了。
首先我們創(chuàng)建一個FrameText.js,然后寫入如下內(nèi)容:
import React, { Component } from 'react'
import { View, Text } from 'react-native'
export default class FrameText extends Component {
render() {
return (
<View style={{
width:200,
height:100,
backgroundColor: '#CDDAF5'
}}>
<Text>{"我來自ReactNative,我是FrameText"}</Text>
</View>
);
}
}
// 整體js模塊的名稱
export { FrameText }
在FrameText中,我們創(chuàng)建了一個簡單的組件,供其他腳本使用。
然后我們再創(chuàng)建一個index.js,然后寫入如下內(nèi)容
import {AppRegistry} from 'react-native'
import {FrameText} from 'FrameText'
// 整體js模塊的名稱
AppRegistry.registerComponent('Component-1', () => FrameText);
在index中,我們注冊了FrameText組件,供app使用,我們可以在index.js注冊很多不同的組件,app可以通過我們注冊的名字(本例中為Component-1)來創(chuàng)建這些組件,下面我們來看看怎么在app內(nèi)使用他們。
5、Android項目修改
本例中期望在app的一個controller內(nèi),同時使用原生語言與ReactNative腳本分別顯示2個View,現(xiàn)在我們來看看是如果在原生app中加載ReactNative的View。

5.1、創(chuàng)建 MyReactNativeBridge
新建一個Java文件,創(chuàng)建MyReactNativeBridge類,顧名思義,此類負責(zé)橋接原生app與ReactNative,注意,一個app中最好只創(chuàng)建一個橋接實例,所以使用單利模式創(chuàng)建。
package com.example.reactnativedemo;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.shell.MainReactPackage;
public class MyReactNativeBridge {
private static final ReactInstanceManager mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(MyApplication.getInstance())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("Scripts/index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
//注冊彈出調(diào)試菜單的Activity,發(fā)布版可以無需注冊
.setCurrentActivity(MyApplication.getInstance().getCurrentActivity())
.build();
private MyReactNativeBridge() {
}
public static ReactInstanceManager getInstance() {
return mReactInstanceManager;
}
}
5.2、創(chuàng)建并使用ReactRootView
ReactRootView為ReactNative腳本描述的View,它繼承FrameLayout,我們可以通過MyReactNativeBridge來創(chuàng)建ReactRootView。
public class MainActivity extends AppCompatActivity {
private ReactRootView mReactRootView;
LinearLayout reactView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
reactView = findViewById(R.id.reactLayout);
MyApplication.getInstance().setCurrentActivity(this);
mReactRootView = new ReactRootView(this.getApplicationContext());
mReactRootView.startReactApplication(MyReactNativeBridge.getInstance(), "Component-1", null);
reactView.addView(mReactRootView);
}
}
現(xiàn)在我們在真機上運行模擬器,并且命令開啟ReactNative服務(wù)器(yarn start),就可以看到上面例子中的畫面了。
6、原生與ReactNative通信
6.1、原生向ReactNative傳遞數(shù)據(jù)
注意我們在startReactApplication時的語句的第三個參數(shù)initialProperties,代表由原生向ReactNative傳遞的參數(shù)。
mReactRootView.startReactApplication(MyReactNativeBridge.getInstance(), "Component-1", null);
現(xiàn)在修改創(chuàng)建語句和腳本
- Java代碼:通過initialProperties向JS傳遞參數(shù)
public class MainActivity extends AppCompatActivity {
private Bundle mockData = new Bundle();
protected void onCreate(Bundle savedInstanceState) {
...
mockData.putString("content", "初始化");
mReactRootView = new ReactRootView(this.getApplicationContext());
mReactRootView.startReactApplication(MyReactNativeBridge.getInstance(), "Component-1", mockData);
reactView.addView(mReactRootView);
}
}
- JS腳本:通過this.props["content"]使用原生App傳遞的參數(shù)(也可以使用this.props.content)
...
export default class FrameText extends Component {
render() {
return (
<View style={{
width:200,
height:100,
backgroundColor: '#CDDAF5'
}}>
<Text>{this.props["content"]}</Text>
</View>
);
}
}
...
重新編譯代碼后,再次運行App,可以看到界面變成如下的樣子

ReactRootView還有一個方法setAppProperties,我們可以通過這個方法來對已經(jīng)創(chuàng)建的View傳遞參數(shù)。
- Java代碼:通過setAppProperties屬性向已創(chuàng)建的View傳遞參數(shù)
protected void onCreate(Bundle savedInstanceState) {
...
mockData.putString("content", "重加載");
mReactRootView.setAppProperties(mockData);
...
}
重新編譯代碼后,再次運行App,可以看到界面變成如下的樣子

6.2、ReactNative向原生傳遞數(shù)據(jù)
我們使用ReactContextBaseJavaModule來定義一個ReactNative中的NativeModules類型。在ReactContextBaseJavaModule中,我們可以使用@ReactMethod注解來提供結(jié)構(gòu)供腳本回調(diào)。
首先我們創(chuàng)建一個MyReactNativeBridge.swift文件
package com.example.reactnativedemo;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class MyReactNativeCommunication extends ReactContextBaseJavaModule {
public MyReactNativeCommunication(ReactApplicationContext reactContext) {
super(reactContext);
}
//提供Test方法
@ReactMethod
public void test(String str) {
System.out.println(str);
}
//提供在NativeModules中該類的名字
@Override
public String getName() {
return "MyReactNativeCommunication";
}
}
然后我們使用MyReactNativePackage來注冊這個類,否則腳本無法使用
package com.example.reactnativedemo;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyReactNativePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new MyReactNativeCommunication(reactContext));
return modules;
}
}
然后在MyReactNativeBridge將MyReactNativePackage添加到mReactInstanceManager中
public class MyReactNativeBridge {
private static final ReactInstanceManager mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(MyApplication.getInstance())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("Scripts/index")
.addPackage(new MainReactPackage())
.addPackage(new MyReactNativePackage())//新增MyReactNativePackage
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.setCurrentActivity(MyApplication.getInstance().getCurrentActivity())
.build();
}
最后修改JS代碼,調(diào)用test方法
import { NativeModules } from 'react-native'
export default class FrameText extends Component {
render() {
NativeModules.MyReactNativeCommunication.test("我回來啦");
...
}
}
重新編譯代碼后,再次運行App,可以看到Android的輸出 “我回來啦”