React Native 混合開發(Android篇)

在React Native的應用場景中,有時候一個APP只有部分頁面是由React Native實現的,好比:咱們經常使用的攜程App,它的首頁下的不少模塊都是由React Native實現的,這種開發模式被稱爲混合開發。html

混合開發的一些其餘應用場景:java

在原有項目中加入RN頁面,在RN項目中加入原生頁面node

RNHybrid

原生頁面中嵌入RN模塊react

Native-RN-page

RN頁面中嵌入原生模塊android

RN-Native-page

以上這些都屬於React Native混合開發的範疇,那麼如何進行React Native混合開發呢?ios

在這篇文章中我將向你們介紹React Native混合開發的流程,須要掌握的技術,以及一些經驗技巧,與該文章配套的還有React Native與Android 混合開發講解的視頻教程git

React Native混合開發的教程咱們分爲上下兩篇,上篇主要介紹如何在現有的Android應用上進行React Native混合開發,下篇主要介紹如何在現有的iOS應用上進行React Native混合開發github

將React Native集成到現有的Android應用中須要以下幾個主要步驟:npm

  • 首先,你須要有一個React Native項目;
  • 爲已存在的Android應用添加React Native所須要的依賴;
  • 建立index.js並添加你的React Native代碼;
  • 建立一個Activity來承載React Native,在這個Activity中建立一個ReactRootView來做爲React Native服務的容器;
  • 啓動React Native的Packager服務,運行應用;
  • (可選)根據須要添加更多React Native的組件;
  • 運行、調試、打包、發佈應用;
  • 升職加薪、迎娶白富美,走向人生巔峯!;

1. 建立一個React Native項目

在作混合開發以前咱們首先須要建立一個沒有Android和iOS模塊的React Native項目。咱們能夠經過兩種方式來建立一個這樣的React Native項目:json

  • 經過npm安裝react-native的方式添加一個React Native項目;
  • 經過react-native init來初始化一個React Native項目;

經過npm安裝react-native的方式添加一個React Native項目

第一步:建立一個名爲RNHybridApp的目錄,而後在該目錄下添加一個包含以下信息的package.json

{
  "name": "RNHybrid",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  }
}

第二步:在爲package.json添加react-native

在該目錄下執行:

npm install --save react-native

執行完上述命令以後,你會看到以下警告:

npm-install--save-react-native.png

其中,有一條警告npm WARN react-native@0.55.4 requires a peer of react@16.3.1 but none is installed告訴咱們須要安裝react@16.3.1

npm install --save react@16.3.1

至此,一個不含Android和iOS模塊的React Native項目便建立好了。此過程所遇到的更多問題可查閱:React Native與Android 混合開發講解的視頻教程

提示:npm 會在你的目錄下建立一個node_modulesnode_modules體積很大且是動態生成了,建議將其添加到.gitignore文件中;

經過react-native init來初始化一個React Native項目

除了上述方式以外,咱們也能夠經過react-native init命令來初始化一個React Native項目。

react-native init RNHybrid

上述命令會初始化一個完成的名爲RNHybrid的React Native項目,而後咱們將裏面的androidios目錄刪除,替換成已存在Android和iOS項目。

2. 添加React Native所須要的依賴

在上文中咱們已經建立了個一個React Native項目,接下來咱們來看一下如何將這個React Native項目和咱們已經存在的Native項目進行融合。

在進行融合以前咱們須要將已經存在的Native項目放到咱們建立的RNHybrid下,好比:我有一個名爲RNHybridAndroid的Android項目,將其放到RNHybrid目錄下:

RNHybrid
├── RNHybridAndroid
├── package.json
├── node_modules
└── .gitignore

第一步:配置maven

接下來咱們須要爲已經存在的RNHybridAndroid項目添加 React Native依賴,在RNHybrid/RNHybridAndroid/app/build.gradle文件中添加以下代碼:

dependencies {
    compile 'com.android.support:appcompat-v7:23.0.1'
    ...
    compile "com.facebook.react:react-native:+" // From node_modules
}

app-build.gradle

而後,咱們爲RNHybridAndroid項目配置使用的本地React Native maven目錄,在RNHybrid/RNHybridAndroid/build.gradle文件中添加以下代碼:

allprojects {
    repositories {
        mavenLocal()
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
        ...
    }
    ...
}

maven-directory-to-build.gradle

提示:爲確保你配置的目錄正確,能夠經過在Android Studio中運行Gradle sync 看是否有 「Failed to resolve: com.facebook.react:react-native:0.x.x」 的錯誤出現,沒有錯誤則說明配置正確,不然說明配置路由有問題。 此過程所遇到的更多問題可查閱:React Native與Android 混合開發講解的視頻教程

第二步:配置權限

接下來咱們爲APP運行配置所須要的權限:檢查你項目中的AndroidManifest.xml文件中看是否有以下權限:

<uses-permission android:name="android.permission.INTERNET" />

若是沒有,則須要將上述權限添加到AndroidManifest.xml中。

另外,若是你須要用到RN的Dev Settings功能:

DevSettingsActivity

則須要在AndroidManifest.xml文件中添加以下代碼:

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

提示:上述圖片就是RN 開發調試彈框中的Dev Settings功能,打開該功能會彈出上圖的一個界面,這個界面就是DevSettingsActivity。

第三步:指定要ndk須要兼容的架構(重要)

Android不能同時加載多種架構的so庫,如今不少Android第三方sdks對abi的支持比較全,可能會包含armeabi, armeabi-v7a,x86, arm64-v8a,x86_64五種abi,若是不加限制直接引用會自動編譯出支持5種abi的APK,而Android設備會從這些abi進行中優先選擇某一個,好比:arm64-v8a,但若是其餘sdk不支持這個架構的abi的話就會出現crash。以下圖:

libgnustl_shared.so"-is-32-bit-instead-of-64-bit

怎麼解決呢:

app/gradle 文件中添加以下代碼:

defaultConfig {
....
    ndk {
        abiFilters "armeabi-v7a", "x86"
    }
}

上述代碼的意思是,限制打包的so庫只包含armeabi-v7ax86此過程所遇到的更多問題可查閱:React Native與Android 混合開發講解的視頻教程

可參考:libgnustl_shared.so」 is 32-bit instead of 64-bit

3.建立index.js並添加你的React Native代碼

經過上述兩步,咱們已經爲RNHybridAndroid項目添加了React Native依賴,接下來咱們來開發一些JS代碼。

在RNHybrid目錄下建立一個index.js文件並添加以下代碼:

import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('App1', () => App);

上述代碼,AppRegistry.registerComponent('App1', () => App);目的是向React Native註冊一個名爲App1的組件,而後我會在第四步給你們介紹如何在Android中加載並顯示出這個組件。

另外,在上述代碼中咱們引用了一個App.js文件:

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} from 'react-native';

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          this is App
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
 });

這個App.js文件表明了咱們React Native的一個頁面,在這個頁面中顯示了this is App的文本內容。

以上就是爲本次演示所添加的React Native代碼,你也能夠根據須要添加更多的React Native代碼以及組件出來。

4. 爲React Native建立一個Activity來做爲容器

通過上述三、4步,咱們已經爲RNHybridAndroid項目添加了React Native依賴,而且建立一些React Native代碼和註冊了一個名爲App1的組件,接下來咱們來學習下如何在RNHybridAndroid項目中使用這個App1組件。

建立RNPageActivity

首先咱們須要建立一個Activity來做爲React Native的容器,

public class RNPageActivity 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")
            .setJSMainModulePath("index")
            .addPackage(new MainReactPackage())
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
        // 這個"App1"名字必定要和咱們在index.js中註冊的名字保持一致AppRegistry.registerComponent()
        mReactRootView.startReactApplication(mReactInstanceManager, "App1", null);

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}

參數說明

  • setBundleAssetName:打包時放在assets目錄下的JS bundle包的名字,App release以後會從該目錄下加載JS bundle;
  • setJSMainModulePath:JS bundle中主入口的文件名,也就是咱們上文中建立的那個index.js文件;
  • addPackage:向RN添加Native Moudle,在上述代碼中咱們添加了new MainReactPackage()這個是必須的,另外,若是咱們建立一些其餘的Native Moudle也須要經過addPackage的方式將其註冊到RN中。須要指出的是RN除了這個方法外,也提供了一個addPackages方法用於批量向RN添加Native Moudle;
  • setUseDeveloperSupport:設置RN是否開啓開發者模式(debugging,reload,dev memu),好比咱們經常使用開發者彈框;
  • setInitialLifecycleState:經過這個方法來設置RN初始化時所處的生命週期狀態,通常設置成LifecycleState.RESUMED就行,和下文講的Activity容器的生命週期狀態關聯;
  • mReactRootView.startReactApplication:它的第一個參數是mReactInstanceManager,第二個參數是咱們在index.js中註冊的組件的名字,第三個參數接受一個Bundle來做爲RN初始化時傳遞給JS的初始化數據,它的具體用法我會在React Android 混合開發講解的視頻教程中再具體的講解;

在中AndroidManifest.xml註冊一個RNPageActivity

Android系統要求,每個要打開的Activity都要在AndroidManifest.xml中進行註冊:

<activity
    android:name=".RNPageActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
    android:windowSoftInputMode="adjustResize"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar" />

上述代碼中咱們爲RNPageActivity添加了一個@style/Theme.AppCompat.Light.NoActionBar類型的theme,這也是React Native UI組件所要求的主題。

爲ReactInstanceManager添加Activity的生命週期回調

一個 ReactInstanceManager能夠被多個activities或fragments共享,因此咱們須要在Activity的生命週期中回調ReactInstanceManager的對於的方法。

@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
public void onBackPressed() {
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onBackPressed();
    } else {
        super.onBackPressed();
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostDestroy(this);
    }
    if (mReactRootView != null) {
        mReactRootView.unmountReactApplication();
    }
}

從上述代碼中你會發現有個不屬於Activity生命週期中的方法onBackPressed,添加它的目的主要是爲了當用戶單擊手機的返回鍵以後將事件傳遞給JS,若是JS消費了這個事件,Native就再也不消費了,若是JS沒有消費這個事件那麼RN會回調invokeDefaultOnBackPressed代碼。

@Override
public void invokeDefaultOnBackPressed() {
    super.onBackPressed();
}

此過程更細緻的講解可查閱:React Native與Android 混合開發講解的視頻教程

添加開發者菜單

在RN中有個很好用的工具開發者菜單,咱們平時調試RN應用時對它的使用頻率很高,接下來咱們來爲RNHybridAndroid添加開着菜單。

public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (getUseDeveloperSupport()) {
        if (keyCode == KeyEvent.KEYCODE_MENU) {//Ctrl + M 打開RN開發者菜單
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
	}
    return super.onKeyUp(keyCode, event);
}

經過上代碼便可監聽Ctrl + M來打開RN開發者菜單。

ctrl+m-android

另外,RN也提供了雙擊R來快速加載JS的功能,經過以下代碼便可打開該功能:

public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (getUseDeveloperSupport()) {
        if (keyCode == KeyEvent.KEYCODE_MENU) {//Ctrl + M 打開RN開發者菜單
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        boolean didDoubleTapR = Assertions.assertNotNull(mDoubleTapReloadRecognizer).didDoubleTapR(keyCode, getCurrentFocus());
        if (didDoubleTapR) {//雙擊R 從新加載JS
            mReactInstanceManager.getDevSupportManager().handleReloadJS();
            return true;
        }
    }
    return super.onKeyUp(keyCode, event);
}

此過程更細緻的講解可查閱:React Native與Android 混合開發講解的視頻教程

使用ReactActivity來做爲RN容器

在上述的代碼中咱們都是經過ReactInstanceManager來建立和加載JS的,而後重寫了Activity的生命週期來對ReactInstanceManager進行回調,另外,重寫了onKeyUp來啓用開發者菜單等功能。

另外,查看RN的源碼你會發如今RN sdk中有個叫ReactActivity的Activity,該Activity是RN官方封裝的一個RN容器。另外,在經過react-native init命令初始化的一個項目中你會發現有個MainActivity是繼承ReactActivity的,接下來咱們就來繼承ReactActivity來封裝一個RN容器。

public class ReactPageActivity extends ReactActivity implements IJSBridge{
    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "App1";
    }
}

另外,咱們須要實現一個MainApplication並添加以下代碼:

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
      );
    }

    @Override
    protected String getJSMainModuleName() {
      return "index";
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

上述代碼的主要做用是爲ReactActivity提供ReactNativeHost,查看源碼你會發如今ReactActivity中使用了ReactActivityDelegate,在ReactActivityDelegate中會用到MainApplication中提供的ReactNativeHost

protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
}

另外實現了MainApplication以後須要在AndroidManifest.xml中添加MainApplication

<application
        android:name=".MainApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...

以上就是經過繼承ReactActivity的方式來做爲RN容器的。

那麼這兩種方式各有什麼特色:

  • 經過ReactInstanceManager的方式:靈活,可定製性強;
  • 經過繼承ReactActivity的方式:簡單,可定製性差;

此過程更細緻的講解可查閱:React Native與Android 混合開發講解的視頻教程

5. 運行React Native

通過上述的步驟,咱們已經完成了對一個現有Android項目RNHybridAndroid添加了RN,而且經過兩種方式分別建立了一個RNPageActivityReactPageActivity的Activity來加載咱們在JS中註冊的名爲App1的RN 組件。

接下來咱們來啓動RN服務器,運行RNHybridAndroid項目打開RNPageActivityReactPageActivity來查看效果:

npm start

RNHybrid的根目錄運行上述命令,來啓動一個RN本地服務:

npm-start

而後咱們打開AndroidStudio,點擊運行按鈕或者經過快捷鍵Ctrl+R來將RNHybridAndroid安裝到模擬器上:

this-is-app-android

6. 添加更多React Native的組件

咱們能夠根據須要添加更多的React Native的組件:

import { AppRegistry } from 'react-native';
import App from './App';
import App2 from './App2';

AppRegistry.registerComponent('App1', () => App);
AppRegistry.registerComponent('App2', () => App);

而後,在Native中根據須要加載指定名字的RN組件便可。

7. 調試、打包、發佈應用

調試

調試這種混合的RN應用和調試一個純RN應用時同樣的,都是經過上文中說講到的RN 開發者菜單,另外搭建也能夠經過學習最新版React Native+Redux打造高質量上線App課程來掌握更多RN調試的技巧。

打包

雖讓,經過上述步驟,咱們將RN和咱們的RNHybridAndroid項目作了融合,但打包RNHybridAndroid你會發現裏面並不包含JS部分的代碼,若是要將JS代碼打包進Android Apk包中,能夠經過以下命令:

react-native bundle --platform android --dev false --entry-file index.js --bundle-output RNHybridAndroid/app/src/main/assets/index.android.bundle --assets-dest RNHybridAndroid/app/src/main/res/

參數說明

  • --platform android:表明打包導出的平臺爲Android;
  • --dev false:表明關閉JS的開發者模式;
  • -entry-file index.js:表明js的入口文件爲index.js
  • --bundle-output:後面跟的是打包後將JS bundle包導出到的位置;
  • --assets-dest:後面跟的是打包後的一些資源文件導出到的位置;

提示:JS bundle必定要正確放到你的Android言語的assets目錄下這個和咱們上文中配置的setBundleAssetName("index.android.bundle")進行對應。

發佈應用

經過上述步驟咱們完成了將RN代碼打包並生成JS bundle,並放到了assets目錄下,接下來咱們就能夠來經過Android Studio或者命令的方式來release咱們的RN混合Android應用了。

我在以前發表過React Native發佈APP之簽名打包APK的博文, 須要的同窗能夠去看一下,在這篇文章中就不在重複了。

更多React Native混合開發的實用技巧,可學習與此文章配套的視頻課程:《React Native與Android 混合開發講解》

參考

相關文章
相關標籤/搜索