整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )

碼個蛋發表於2019-03-03

原文連結:
motalks.cn/2016/10/26/…
轉載請註明來源。

由於公司的 Win 7 系統的桌上型電腦效能比較好,所以我又在 Windows 系統上又走了一遍 React Native 開發環境搭建和整合 React Native 到現有的 Android 專案的過程。在整合 React Native 這塊 Mac 和 Windows 系統的差異倒是很小,坑比較多是環境搭建那塊,回頭在把 Windows 下的 React Native 環境搭建寫寫。最後還有一個忠告,買 Mac Book Pro 請直接上頂配,都是淚。

在已有專案中初始化 React Native

在專案根目錄下執行下面三行命令,進行初始化 React Native 的流程。

npm init
npm install --save react react-native
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig複製程式碼

下面分步講解每個命令的作用:

關於 npm init 命令

這步操作會在專案根目錄生成 package.json 檔案。主要是些專案資訊,這裡會生成之後步驟需要的 ”scripts“ 欄位,自己的過程忘記截圖了,用了 天空oo7 的圖。

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
img

接下來,將 package.json 檔案中 scripts 欄位下新增下面這句,同時也可以將 ”test“:"no" 這句刪去。下面這句的作用應該是配置 ”start“ 命令的路徑的。

"start": "node node_modules/react-native/local-cli/cli.js start"複製程式碼

關於 install --save react react-native 命令

用於初始化 React 和 React Native 相關檔案,安裝完成後會在專案的根目錄下看到 node_modules 資料夾。

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
圖片來自 天空007

關於 curl 命令

curl是利用URL語法在命令列方式下工作的開原始檔傳輸工具。它被廣泛應用在Unix、多種Linux發行版中,並且有DOS和Win32、Win64下的移植版本。

由於 Mac OS 是基於Unix 核心,所以 Mac 在網路暢通的情況下,這條命令很愉快的就執行完畢了,而後會在你專案根目錄下生成一個 .flowconfig 檔案。

在 Windows 上你會絕望的看到命令列視窗顯示 “ ' curl' 不是內部或外部命令,也不是可執行的程式或批處理檔案" 的提示。你可以按照《 Windows 下安裝使用 curl 命令》教程去使用該命令,也可以直接在專案的根目錄下新建個 .flowconfig 檔案,再將 raw.githubusercontent.com/facebook/re… 的內容複製到該檔案中。方便網路不暢的同學,已將該網頁配置資訊複製在下面(該配置資訊隨時會更新,建議還是到網站獲取實時配置資訊)。

[ignore]

# We fork some components by platform.
.*/*[.]android.js

# Ignore templates with `@flow` in header
.*/local-cli/generator.*

# Ignore malformed json
.*/node_modules/y18n/test/.*\.json

# Ignore the website subdir
<PROJECT_ROOT>/website/.*

# Ignore BUCK generated dirs
<PROJECT_ROOT>/\.buckd/

# Ignore unexpected extra @providesModule
.*/node_modules/commoner/test/source/widget/share.js

# Ignore duplicate module providers
# For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root
.*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js
.*/node_modules/jest-runtime/build/__tests__/.*

[include]

[libs]
Libraries/react-native/react-native-interface.js
flow/

[options]
module.system=haste

esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable

experimental.strict_type_args=true

munge_underscores=true

module.name_mapper='^image![a-zA-Z0-9$_-]+複製程式碼
-> 'GlobalImageStub' module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\) -> 'RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-3]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy unsafe.enable_getters_and_setters=true [version] ^0.33.0

新增 index.android.js 檔案到專案根目錄

在根目錄下新建 index.android.js 檔案,複製下面這段內容進去即可。

'use strict';

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

class HelloWorld extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello, World</Text>
      </View>
    )
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

AppRegistry.registerComponent('HelloWorld', () => HelloWorld);複製程式碼

已有專案的相關配置

module 級別的 build.gradle 配置修改

在你 app 資料夾下的 build.gradle 檔案(module級別的gradle)新增 React Native 的依賴。

dependencies {
    compile "com.facebook.react:react-native:+" // From node_modules.
}複製程式碼

這裡的 "+" 號表示跟隨最新的 React Native 版本,也可以指定具體的版本號。

project 級別的 build.gradle 配置修改

在你專案根目錄 的 build.gradle 檔案(project級別的gradle)新增

allprojects {
    repositories {
        jcenter()
        maven {
              // All of React Native (JS, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
    }
}複製程式碼

這裡要注意的是在 allprojects 節點下新增。這是因為Android專案預設的依賴包的源 jcenter() 並不包含最新版的 React Native(它只到0.20.1)。新版的 React Native 都只在 npm 裡釋出,因此你需要增加一下依賴包的源。在編譯完後,檢查 External Libraries 的 react-native 版本,若為 0.20.1 則說明 maven 的依賴源沒有新增成功。可以更改 url 路徑為 "$rootDir/node_modules/react-native/android" 試試。目前獲取到的最新版本應該是0.35.0。

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
圖片來自 天空007

新增原生程式碼

需要注意點:從0.29.0版本開始,在生命週期方法 onResume(), onPause() 中mReactInstanceManager呼叫的方法改為 onHostResume(), onHostPause() 。但是現在0.35.0的官方文件 mReactInstanceManager 還是呼叫 onResume(), 然而 ReactInstanceManager 已經改為 onHostResume() 了,所以還呼叫 onResume() 會報紅。其他需要注意的都寫在註釋裡了。

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import com.facebook.react.BuildConfig;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
/**
 * Created by silencelin on 2016/10/24.
 */
public class MyReactActivity extends Activity 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();
          // startReactApplication 方法中的 moduleName 引數必須和你在index.android.js 檔案中                  // AppRegistry.registerComponent 註冊的專案名稱保持一致
        mReactRootView.startReactApplication(mReactInstanceManager, "AwesomeProject", null);
        setContentView(mReactRootView);
    }

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

      @Override
    protected void onResume() {
        super.onResume();
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostResume(this, this);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostPause();
        }
    }   

    @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);
    }
}複製程式碼

接下來別忘記在 AndroidManifest.xml 中註冊。

<activity
  android:name=".MyReactActivity"
  android:label="@string/app_name"
  android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />複製程式碼

其中 DevSettingsActivity 是下面這個設定頁面,也是開發過程中必須要使用到的頁面。

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
DevSettingActivity

還有網路許可權,一般專案都會有這個許可權了。

<uses-permission android:name="android.permission.INTERNET" />複製程式碼

還有個許可權,官方文件和其他很多文章都沒提到,老司機要發車了,大家坐穩了。就是懸浮窗許可權,沒有申請這個許可權你胳膊搖出麒麟臂都沒辦法調出上圖這個設定選單的。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>複製程式碼

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
真機搖一搖調出設定選單

Run your App !

終於到這個激動人心的時刻了,在你專案根目錄輸入如下命令

npm start複製程式碼

關於 npm start 命令

該命令會執行 package.json 檔案中 “ scripts ” 下的“ start ” 的值。

"scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  }複製程式碼

看了下這個 cli.js 檔案,最後執行的是 cliEntry.js 裡面的指令碼,一堆配置引數,簡單的說就是在本地配置一個 web 伺服器環境吧。(nodeJs 真的不懂,若有錯誤,煩請指正。)

成功的樣子如圖:

整合 React Native 到現有的 Android 專案( Mac, Windows 通用版 )
npm_start_success

Run your app 的注意事項

接下來就像往常一樣,點選 Android Run 按鈕,將專案部署到虛擬機器或者真機上了。注意 build 模式要選擇debug 。如果不小心選擇了 release 模式,你會遇到這個報錯:

Could not get BatchedBridge, make sure your bundle is packaged correctly

因為正式版需要你建立 React Native bundle 存放到本地 asserts 目錄。執行如下命令:

react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/複製程式碼

注意將 android/com/your-company-name/app-package-name/src/main 替換成你專案的實際路徑。最後看到asserts 目錄裡生成了 index.android.bundle 和 index.android.bundle.meta 兩個檔案就說明上面命令執行成功,可以愉快的去 run 你的專案了。

debug build 和 release build 方式 React Native 程式碼除錯的區別

debug build : 修改完 js 程式碼可以直接搖一搖 選擇 Reload Js 就可以看到更新後的效果。

release build : 修改完 js 程式碼需要重新生成 index.android.bundle 檔案,才能看到更新後的效果。因為正式版釋出後是無法依賴本地伺服器去更新index.android.bundle 。所以這個催生了一個 React Native 怎麼熱更新的問題。

接下來的安排

React Native Android 開發環境搭建 (已出)

React Native 整合到現有的 Android 專案(本篇)

React Native 專案熱更新(待更新)

React Native 優化(包大小優化,預載入解決首次載入白屏等)(待更新)

相關文章