原生 Android 項(xiàng)目集成 React Native

創(chuàng)建一個(gè) React Native 項(xiàng)目并寫一個(gè)純的 React Native 應(yīng)用可以參考官方指南。

iOS 項(xiàng)目集成 React Native 可以參考 原生 iOS 項(xiàng)目集成 React Native。

本文主要介紹原生 Android 項(xiàng)目集成 React Native 并用于部分頁面開發(fā)的流程。開發(fā)環(huán)境為 macOS 10.12、Android Studio 2.2.1、React Native 0.35.0。而官方給出的 植入原生 Android 應(yīng)用指南 只對應(yīng)到 0.28 版本。最新版(當(dāng)前為 0.35)的集成方案稍微有些變動。

1. 創(chuàng)建/修改 Android 項(xiàng)目

用 Android Studio 創(chuàng)建一個(gè) Android 項(xiàng)目,注意 Minimum SDK 要設(shè)置為 API 16 或以上,因?yàn)?React Native 要求 Android 4.1 及以上的環(huán)境。

如果現(xiàn)有 Android 項(xiàng)目且 Minimum API 小于16則修改 Minimum SDK 到16,注意部分 API 變化。

2. 添加 package.json

在 Android 項(xiàng)目根目錄新建文件 package.json,內(nèi)容如下(參考 react-native init 生成的 package.json 文件)

{
  "name": "react-native-sample",
  "version": "0.0.1",
  "description": "sample of react native embedding android",
  "main": "index.android.js",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "author": "danke77",
  "license": "ISC",
  "dependencies": {
    "react": "^15.3.2",
    "react-native": "^0.35.0"
  },
  "devDependencies": {
  }
}

執(zhí)行 npm install 就可以安裝 dependencies 下的 npm 組件了。

這個(gè)時(shí)候在 Android 項(xiàng)目根目錄就生成了 node_modules/ 文件夾,里面就是一些用到的組件。

.gitignore 中添加

# node.js
node_modules/
npm-debug.log

執(zhí)行 react-native upgrade 可以更新已有組件。

3. 添加 index.android.js

在 Android 項(xiàng)目根目錄創(chuàng)建目錄 js/,js 相關(guān)的代碼就放在這個(gè)文件夾下。

js/ 下添加 App.js,內(nèi)容如下

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

export default class extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>
          Hello React Native!
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ffffff'
  },
  text: {
    fontSize: 20,
    color: '#333333'
  }
})

在 Android 項(xiàng)目根目錄新建文件 index.android.js,內(nèi)容如下

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

AppRegistry.registerComponent('navigation', () => App)

這里的 navigation 一般會根據(jù)模塊功能命名,后面還會用到。

當(dāng)然也可以把 App.js 的內(nèi)容寫在 index.android.js 里,但這樣寫更清晰一些,尤其是項(xiàng)目大了文件多的情況。

4. Android 項(xiàng)目添加依賴

4.1 project 級別的 build.gralde

Android 默認(rèn)的依賴包的源 jcenter() 不包含最新版的 React Native,新版的 React Native 都只在 npm 里發(fā)布,因此要依賴 node_modules/ 下的東西。

在 Android 項(xiàng)目根目錄下的 build.gradle 文件添加如下內(nèi)容

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

4.2 module 級別的 build.gradle

在 Android 項(xiàng)目 app 目錄下的 build.gradle 文件添加如下內(nèi)容

// react native
compile 'com.facebook.react:react-native:0.35.0'  // From node_modules

這里版本號要和 package.json 里的一致。

5. React Native 相關(guān)的 Activity 和 Application

5.1 Activity

創(chuàng)建一個(gè)繼承自 com.facebook.react.ReactActivity 的 Activity

public class HelloReactActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "navigation";
    }
}

重寫 getMainComponentName() 方法,返回的字符串必須和前面的 AppRegistry.registerComponent('navigation', () => App) 里的 navigation 對應(yīng),表示該 Activity 會顯示對應(yīng)組件里的內(nèi)容。

5.2 Application

Application 需要實(shí)現(xiàn) com.facebook.react.ReactApplication 接口,并實(shí)現(xiàn)其方法

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

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

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

getUseDeveloperSupport() 表示是否啟動開發(fā)者模式。

getPackages() 是引用的模塊列表,默認(rèn)需要添加 MainReactPackage,如果需要在 js 里調(diào)用原生 Java 模塊,需要添加自定義的模塊(如 OtherReactPackage)。

新版這兩個(gè)方法是寫在 Application 里的,舊版都是些在 Activity 里的。

5.3 AndroidManifest.xml

AndroidManifest.xml 里需要添加自己創(chuàng)建的 Activity 和 React Native 提供的 DevSettingsActivity,還需要添加兩個(gè)權(quán)限。

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

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

HelloReactActivity 是自己創(chuàng)建的頁面,DevSettingsActivity 是開發(fā)調(diào)試需要用到的設(shè)置頁面。

android.permission.INTERNET 用于開發(fā)調(diào)試,android.permission.SYSTEM_ALERT_WINDOW 用于顯示懸浮窗。

6. 啟動服務(wù)

debug 模式下需要在 package.json 所在目錄下執(zhí)行 npm start,它等效于 package.json 里的 node node_modules/react-native/local-cli/cli.js start,相當(dāng)于啟動一個(gè)本地服務(wù)。

Terminal 顯示如下表示服務(wù)已正常啟動

> react-native-module@0.0.1 start /Users/danke77/Projects/react-native/HelloReactNative
> node node_modules/react-native/local-cli/cli.js start

Scanning 581 folders for symlinks in /Users/danke77/Projects/react-native/HelloReactNative/node_modules (17ms)
 ┌────────────────────────────────────────────────────────────────────────────┐
 │  Running packager on port 8081.                                            │
 │                                                                            │
 │  Keep this packager running while developing on any JS projects. Feel      │
 │  free to close this tab and run your own packager instance if you          │
 │  prefer.                                                                   │
 │                                                                            │
 │  https://github.com/facebook/react-native                                  │
 │                                                                            │
 └────────────────────────────────────────────────────────────────────────────┘
Looking for JS files in
   /Users/danke77/Projects/react-native/HelloReactNative

[2016-10-17 17:06:48] <START> Building Dependency Graph
[2016-10-17 17:06:48] <START> Crawling File System
[Hot Module Replacement] Server listening on /hot

React packager ready.

[2016-10-17 17:06:49] <END>   Crawling File System (966ms)
[2016-10-17 17:06:49] <START> Building in-memory fs for JavaScript
[2016-10-17 17:06:49] <END>   Building in-memory fs for JavaScript (260ms)
[2016-10-17 17:06:49] <START> Building in-memory fs for Assets
[2016-10-17 17:06:50] <END>   Building in-memory fs for Assets (138ms)
[2016-10-17 17:06:50] <START> Building Haste Map
[2016-10-17 17:06:50] <START> Building (deprecated) Asset Map
[2016-10-17 17:06:50] <END>   Building (deprecated) Asset Map (104ms)
[2016-10-17 17:06:50] <END>   Building Haste Map (428ms)
[2016-10-17 17:06:50] <END>   Building Dependency Graph (1825ms)

7. 開發(fā)調(diào)試

構(gòu)建 Android 項(xiàng)目,打開應(yīng)用,切換到 HelloReactActivity 頁面,通過搖一搖開啟調(diào)試菜單,選擇 Dev Settings 進(jìn)入 DevSettingsActivity

Dev Settings.png

設(shè)置 Debug server host & port for device 為本機(jī) IP 地址,添加端口號

Debug server host & port for device.png

返回到 HelloReactActivity 頁面,搖一搖選擇 Reload,接下來就可以開始調(diào)試了。

每次修改 js 代碼只需 Reload 即可,無需重新構(gòu)建整個(gè) Android 項(xiàng)目,修改 Java 代碼需要重新構(gòu)建。

8. 發(fā)布正式包

8.1 js bundle

React Native 的開發(fā)版需要有啟動一個(gè)本地服務(wù)隨時(shí)發(fā)送更新后的 js bundle 文件。如果要打正式包,需要把 js bundle 文件保存到 Android 項(xiàng)目的 assets/ 目錄下。這樣,正式包就不需要本地服務(wù)支持了。可參考官方文檔

app/src/main/ 下創(chuàng)建 assets/ 文件夾,執(zhí)行以下命令將 js bundle 保存到資源目錄下

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

app/src/main/assets/ 下就會生成 index.android.bundle 文件。

開發(fā)模式調(diào)試的時(shí)候 js 代碼會立即生效,無需執(zhí)行以上命令,但每次正式打包的時(shí)候如果改了 js 代碼都必須先執(zhí)行以上命令。

8.2 Proguard

集成 React Native 之后如果不加相關(guān)的混淆規(guī)則,打 release 包的時(shí)候就會報(bào)錯(cuò)。

參考 官方例子的混淆文件

# React Native

# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip

# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
    @com.facebook.proguard.annotations.DoNotStrip *;
    @com.facebook.common.internal.DoNotStrip *;
}

-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
  void set*(***);
  *** get*();
}

-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class *  { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }

-dontwarn com.facebook.react.**

# okhttp

-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**

# okio

-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

完成以上兩步后就可以通過 ./gradlew assembleRelease 打正式包了。

本文是 慌不要慌 原創(chuàng),發(fā)表于 https://danke77.github.io/,請閱讀原文支持原創(chuàng) https://danke77.github.io/2016/10/17/react-native-embedding-android/,版權(quán)歸作者所有,轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內(nèi)容