ReactNative

本文主要介紹:
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設計思路:


圖片1.png

Android中的實現:
ReactNative中把index.android.js編譯成Android工程目錄下assets里的index.android.bundle文件。
在Android中,導入的ReactNative的jar包中的代碼會根據這個bundle文件做相應的轉換,轉換成Android中“對應的代碼”去執行。

無標題.png

二:安裝配置篇
我在公司從事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 的混寫。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容