本文主要介紹:
1、ReactNative是什么?
2、怎么安裝ReactNative?
3、ReactNative與原生交互。
最近公司項目上需要用到ReactNative,于是著手開始研究使用。
一:先看看相關的一些資料
ReactNative簡介:
2015年3月,React Native開源。它的目的就是允許web開發者更多的基于現有經驗實現媲美原生App的用戶體驗。開發者只需學習JavaScript就能輕易為任何平臺高效地編寫代碼。做到Learn once, write anywhere.
總結一下就是:APP上的用戶體驗很好,但是APP又分平臺ios和android。要開發兩套代碼,成本太大。
疑問,這個是怎么做到的???
設想一下有沒有這樣的一個框架,即在android和ios之上再抽象一層框架,調用這個框架的視圖,運行到真實的手機上顯示時,這個視圖會調用android或者ios原生的視圖控件去展示呢。
答案就是ReactNative!
看圖-React Native設計思路:
Android中的實現:
ReactNative中把index.android.js編譯成Android工程目錄下assets里的index.android.bundle文件。
在Android中,導入的ReactNative的jar包中的代碼會根據這個bundle文件做相應的轉換,轉換成Android中“對應的代碼”去執行。
二:安裝配置篇
我在公司從事Android應用的開發。使用的是這樣的
環境:Windows7、 Android Studio2.2、Gradle3.3、 JDK1.8 、NDK、Python、Node、react-native0.48、SDK、Android SDK Tools相關、Android真機
資料:
參考ReactNative中文網
地址:http://reactnative.cn/docs/0.48/getting-started.html#content
1、首先確保你的工程在Android Studio的Terminal窗口命令中,使用“gradle installDebug”命令能把項目編譯運行起來。
2、安裝Python、Node、安裝Android SDK Tools相關(參照ReactNative中文網)
3、配置JDK、NDK、SDK環境變量
4、在你想創建項目的文件夾下執行以下命令:
react-native init demo
當創建了對應的工程文件夾:demo時,電腦連接上手機,執行以下命令:
cd demo
react-native run-android
備注:
react-native run-android 命令會啟動一個Js服務器,
并安裝測試的apk在你的手機中。程序啟動后,使用菜單鍵或者搖一搖,可以加載調試菜單,從菜單中可以做以下事情:
1、可以配置服務器地址:電腦IP:8081,如192.168.43.77:8081
(注意8081端口不能被其他程序占用,如果占用自行百度查詢解決方式;如果不小心開啟了兩個Server,就是原來的沒關閉,然后又執行npm start,你可以使用Ctrl + C 退出一個)
2、可以重新加載(Reload)Js服務器上的Js文件進行展示;
可以remote Debug(使用Chrome瀏覽器);
...
以上步驟成功運行,說明demo可以跑通;接下來看原生應用添加ReactNative是怎么配置的。
5、將代碼管理服務器(git或svn)上的原生應用工程clone到對應你創建的demo/android文件夾下。
6、工程根目錄下build.gradle文件中添加:
allprojects {
repositories {
maven {
//重點
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"http://添加這一句,意思就是在本地的node_modules文件夾中去加載Library:react-native0.48.jar
}
}
}
7、
在Module目錄下的build.gradle中配置ndk支持的so文件路徑
(根據項目中是否用到其他so庫,考慮添加)
defaultConfig {
applicationId "com.android.demo"
minSdkVersion 20
targetSdkVersion 25
//支持分包
multiDexEnabled true
//重點
ndk {
//顯示指定支持的ABIs .so文件的路徑
abiFilters 'armeabi',"armeabi-v7a", "x86"
}
}
8、
在Module目錄下的build.gradle中引入React-native包
dependencies {
compile fileTree(include: ['*.jar'], dir: '../libs')
//重點
compile 'com.facebook.react:react-native:+'
}
9、修改demo\android\gradle\wrapper目錄下的gradle-wrapper.properties文件
distributionUrl參數為本地gradle文件。如:distributionUrl=file:///D:/gradle-3.3-all.zip
10、然后用AndroidStudio打開項目,會提示配置NDK。在提示框中配置相應的路徑就可以了。(如果沒提示,那直接編譯就可以了)
11、demo\android\test目錄下創建assets文件夾,然后使用命令行到工程目錄下執行命令:
如:(注意目錄路徑的修改)
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output
D:/workspace/demo/android/test/assets/index.android.bundle
--assets-dest D:/workspace/demo/android/test/res/
這個命令會在demo/android/test/assets目錄下生成index.android.bundle和index.android.bundle.meta文件。
注意這是“一行”命令!
12、開啟命令行窗口,cd到工程目錄,執行命令 npm start 啟動js服務器。然后在AS中運行工程。
備注:
1、關閉防火墻
2、當有Js服務器在運行的時,在AS中編譯Rebuild或者運行一貫應用有時候回提示不能刪除build目錄下的文件,
導致項目編譯報錯的情況,需要關閉Js服務器。重新編譯運行即可。
3、如果有armeabi相關的錯誤:在demo\android\test\libs目錄下復制armeabi文件夾,替換名稱為armeabi-v7a
三:ReactNative與原生交互
原生與Js通信
1、創建ReactContextBaseJavaModule的子類
2、重寫getName()函數,用于返回一個字符串,這個字符串在JavaScript端標記這個模塊。
3、暴露一個函數給javascript端,并且使用注解@ReactMethod進行標記,
該函數的返回值必須為void,React Native的跨語言訪問是異步進行的,
所以想要給JavaScript返回一個值的唯一辦法是使用回調函數或者發送事件。
4、在JS文件中需要:import {
NativeModules
} from 'react-native';
然后通過React.NativeModules.getName返回的字符串.標記的方法名,調用原生應用的方法。
例子:
@Override
public String getName() {
return "CurrentDetailModule";
}
//Module中聲明的方法
@ReactMethod
public void receiveDataFromJS(String money) {
Toast.makeText(getReactApplicationContext(), money, Toast.LENGTH_LONG).show();
}
//JS端調用Nactive中的方法有兩種:
//第一種方式調用:
React.NativeModules.CurrentDetailModule.receiveDataFromJS
//第二種方式調用:創建一個my.js文件,復制粘貼 以下內容
'use strict'
var {NativeModules} = require('react-native');
module.exports = NativeModules.MyModule;
在另一個js文件中這樣調用
var toastExa = require('./my');
toast() {
toastExa.show('showtext');
}
以上,是最簡單的調用。當Native需要傳遞數據到JS當中時,有三種方式:
第一種:
//由JS端調用,Native端回調數據給JS
@ReactMethod
public void getLoginState(Callback callback) {
try {
WritableMap map = Arguments.createMap();
map.putString("isLogin", "0");
callback.invoke(map);
} catch (IllegalViewOperationException e) {
Logger.i("React-Native:getLoginState"));
}
}
//JS端代碼:
var bridge = NativeModules.CurrentDetailModule;
bridge.getLoginState((properties) => {
if (properties["isLogin"] == '0') {
bridge.gotoLogin();
} else {
//
}
})
第二種:
//主動發送數據的方式
@ReactMethod
public void sendDataToJs() {
WritableMap params = Arguments.createMap();
params.putString("SendData", "1234");
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("eventName", params);
}
// JS端代碼
componentWillMount() {
this.callNative.bind(this);
DeviceEventEmitter.addListener('eventName', this.onScanningResult);
}
onScanningResult = (e) => {
this.setState({
money: e.SendData,
});
}
第三種:Promises,跟第一種類似
@ReactMethod
public void measureLayout(Promise promise) {
try {
//measureLayout(mMeasureBuffer);
WritableMap map = Arguments.createMap();
map.putDouble("relativeX", PixelUtil.toDIPFromPixel(mMeasureBuffer[0]));
map.putDouble("relativeY", PixelUtil.toDIPFromPixel(mMeasureBuffer[1]));
promise.resolve(map);
} catch (IllegalViewOperationException e) {
promise.reject(e.getMessage());
}
}
//在JS端調用:
async function measureLayout() {
try {
var {
relativeX,
relativeY,
width,
height,
} = await UIManager.measureLayout(100, 100);
console.log(relativeX + ':' + relativeY + ':' + width + ':' + height);
} catch (e) {
console.error(e);
}
}
measureLayout();
原生應用與ReactNative相互傳遞數據時,相對應的數據類型,如下:
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
注:Flexbox是React Native開發中的核心布局。flexbox是Flexible Box的縮寫,既為彈性布局。可以簡單快速地完成各種伸縮設計。
JSX 是 JavaScript 語法的擴展。HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX語法,它允許 HTML 與 JavaScript 的混寫。