React Native植入原生Android應用的流程解析

引言

React Native是如今移動開發新的可選方案,也帶來了原屬於Web領域的React的優秀開發特性。另外一方面,React Native的技術棧一經掌握,能夠用於iOS、Android及Windows(見此)多個平臺,即所說的「learn once, write anywhere」。html

開始使用React Native的問題

如何使用React Native?參照官方指南,你會發現官方告訴你的是:請用react-native init命令來建立一個React Native項目。這個項目的根目錄結構是這樣:node

init命令生成的React Native項目

可是,以Android爲例,一個普通原生項目的根目錄結構倒是這樣(Android Studio 2.1.2):react

Android原生項目

能夠看到,Android原生項目(上圖的Drill根目錄)平級於生成的React Native項目的android目錄。那麼,若是一直以來都是Android原生開發,如今想要引入React Native,考慮部分頁面用React Native實現,應該如何作呢?android

這就是React Native植入原生應用的問題。顯然,react-native init命令生成的項目在結構上不太相符,它的出發點更像是「徹底用React Native作一個多平臺應用」,但咱們可能須要的是「一個原生應用但有部份內容是用React Native作的」。ios

在本文的時間點,React Native的最新版是0.27。官方對此已給出植入原生Android應用的指南,但它不夠準確,也缺乏一些細節。所以,本文將提供一個React Native植入原生Android應用的更詳細一點的流程。git

若是你想了解iOS版的,能夠閱讀這篇文章github

植入Android流程

基本環境

這篇流程是windows及Android Studio,若是你已是一個Android Studio原生應用開發者,以及Node.js用戶,那麼所需的環境你基本已經有了。詳情請參考windows環境搭建文字教程以及開始使用React Native,什麼都沒有也不要緊,正好從頭搭建。npm

此外,Android模擬器使用了Genymotion,註冊後就能夠供我的使用,會比官方模擬器性能要好一些。json

新建Android項目

讓咱們從一個全新的Android原生應用開始。windows

用Android Studio建立一個新項目,注意Minimum SDK應設置爲API 16及以上(React Native要求Android4.1以上的環境):

要求Android 4.1(API 16)以上

添加npm組件

到Android原生項目的根目錄(也能夠新建一個目錄,但根目錄比較經常使用)新建一個文件package.json,內容以下(這裏起名爲react-native-module):

{
  "name": "react-native-module",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "^15.1.0",
    "react-native": "^0.27.2"
  }
}

下面的dependencies的內容要如何得知呢?答案是參考react-native init生成的項目,畢竟版本號是會不斷更新的。若是你已經init生成過項目,能夠運行react-native upgrade更新後再參考。

而後,在這個package.json的所在位置,執行:

npm install

安裝好所需的npm組件。

添加index.android.js文件

一樣在根目錄,增長一個文件index.android.js,這是React Native開發的具體內容,是任意的,這裏給一個簡單的例子:

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

class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.note}>
          acgtofe.com with react native
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  note: {
    fontSize: 20
  }
});

AppRegistry.registerComponent('react-native-module', () => App);

注意上面代碼最後的react-native-module這個名字比較重要,能夠自定,但後面還會在其餘地方用到,須要保持一致。

在Android應用內添加依賴

回到Android Studio,到appbuild.gradle文件(module級別的gradle)裏添加依賴:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile "com.facebook.react:react-native:0.27.2"
}

最後一行的react-native就是咱們新增的,注意這裏的版本號要和package.json裏的一致。

運行一次Gradle Sync,你必然會獲得這個錯誤:

Gradle Sync錯誤

這是由於Android項目默認的依賴包的源jcenter()並不包含最新版的React Native(它只到0.20.1)。新版的React Native都只在npm裏發佈,所以你須要增長一下依賴包的源,到根目錄的build.gradle文件(project級別的gradle)內增長如下內容(從官方的這句註釋也能夠了解到這一點):

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

這裏的url路徑,將取決於你放置node_modules的位置(你能夠根據須要選擇放置在其餘地方)。以上是node_modules位於根目錄時的url路徑,把它改成"$rootDir/node_modules/react-native/android"也是能夠的,它們等效。如何知道這個路徑寫對了呢?反覆試就能夠了,若是路徑不對,Gradle Sync的時候必定會提示你前面的錯誤。

你可能在不少別的地方看到的都是這樣的寫法:

compile "com.facebook.react:react-native:+"

不太建議這樣作,由於沒有明確的版本號,你沒法讓系統幫你判斷前面的url路徑寫的是否正確。若是寫錯,Android將使用發佈在jcenter()的舊版React Native,而這將引起其餘錯誤(見後文)。

新建React Native的Activity

新建一個繼承自ReactActivity的activity(這裏起名爲LiveActivity),Android Studio會提醒你必須實現3個方法,通常寫成這樣:

public class LiveActivity extends ReactActivity {

    @Override
    protected String getMainComponentName() {
        return "react-native-module";
    }

    @Override
    protected boolean getUseDeveloperSupport() {
        return BuildConfig.DEBUG;
    }

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

getMainComponentName()的字符串返回值,必須和前面的index.android.js內的組件名一致。

getUseDeveloperSupport()是一個邏輯返回值,表示是否啓用開發者模式。這裏寫BuildConfig.DEBUG就能夠自動根據gradle構建的類型(debug或release)來決定。

getPackages()是模塊列表,通常像上面代碼這樣就能夠。若是你須要在JavaScript裏調用原生Java模塊,就要把它們添加到這裏,具體能夠參考這篇文章

清單文件添加聲明

到Android清單文件AndroidManifest.xml添加如下內容(省略了無關部分):

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.acgtofe.drill">

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

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

</manifest>

前面的權限都是React Native開發環境須要用到的。後面的LiveActivity是剛纔的React Native運行界面,DevSettingsActivity則是如下這個Dev Settings的界面:

Dev Settings界面

它也是開發版所必需的。

啓動packager server

package.json的位置,打開命令行,運行packager server:

react-native start

也能夠用npm start。啓動後的狀態看起來像這樣:

packager server運行中

運行起來

這是最後一步了。build這個Android項目,安裝到模擬器裏,而後打開應用,切換到LiveActivity界面(用按鈕跳轉,或者直接設置爲啓動界面均可以),這時候應該只看到一片空白。

ctrl + m(這是Genymotion的用法,事實上,這是Android的Menu鍵,如今的實體設備基本沒有這個鍵,但搖一搖能夠觸發)開啓調試菜單,選擇Dev Settings,打開前面貼過圖的DevSettingsActivity,設置Debug server host & port for device爲本機ip地址(命令行內ipconfig查看)。最後,回到LiveActivity,開啓調試菜單選擇Reload JS,等待一下子,若是你看到了像下圖這樣的界面:

示例效果

就說明完成了!對應的,packager server裏應該能夠看到每一次請求的記錄:

示例的packager server輸出

接下來,你就能夠開始React Native的開發了,改動保存後,從新Reload JS,就能夠看到新的效果。

建議及改進

建議使用Android 5.0+的設備(包括模擬器),它們支持直接USB傳輸packager server返回的那個bundle js文件。若是是Android 5.0+,能夠USB鏈接電腦後(若是是模擬器,那就等於已經鏈接)運行如下命令:

adb reverse tcp:8081 tcp:8081

而後就能夠省略掉前面流程裏設置本機ip地址那一步,直接Reload JS。注意設備須要開啓USB調試(模擬器不用),並且電腦同時只鏈接一個設備。

相對於前面設置本機ip地址的方式,這幫你免去了同一WiFi環境、代理等麻煩。

不順利的狀況

雖然流程看起來輕鬆愉快,但並不怎麼能一次成功。下面是我在流程中碰到過的一些問題及其記錄,能夠用做參考。

版本不匹配

錯誤提示以下:

Module 0 is not ...

參照github上的issue,這個錯誤的引起緣由是packager server的React Native版本和Android應用內的不一致。好比應用內的gradle依賴寫的是compile "com.facebook.react:react-native:+"url路徑寫得不對,結果用的就是jcenter()裏的0.20.1的舊版,就會有這個問題。

所以,建議用compile "com.facebook.react:react-native:0.27.2"這樣的寫法,並檢查版本號是否和package.json裏的一致。

404

404

這是說index.android.js文件不存在的錯誤。但我遇見的是文件就在那,也出這個錯誤。

這多是由不正確的緩存引發,個人解決方法:關閉server,刪除index.android.js,而後重啓server,刷新,獲得真正的404,而後還原index.android.js,再刷新即解決。

沒法鏈接到server

Could not connect to development server.

先按照Try the following to fix the issue: 下給出的解決方法依次檢查和嘗試。若是仍不能解決,刪除掉node_modules目錄,從新npm install,而後重開server。

windows下刪除node_modules目錄可能有路徑過長的問題,推薦用rimraf來刪除。

500

500

這個問題須要具體看server的輸出,我這裏的錯誤信息是:Error: Unable to find file with path: ......polyfills\prelude_dev.js。相似前面的沒法鏈接server,我也是刪除node_modules後從新安裝獲得解決。

有用的調試方法

流程中可能碰到的問題能夠分爲兩類,Android應用(client)和server。若是看到錯誤,打開瀏覽器訪問http://localhost:8081/index.android.bundle?platform=android,若是能看到輸出的JavaScript代碼,那說明server是比較正常的,更多是Android應用的問題。反過來,若是瀏覽器裏一樣看到錯誤信息,那更可能就是server的問題。

沒有Flow和Nuclide

你可能在開始用React Native的過程當中據說了FlowNuclide,它們分別是JavaScript類型檢查工具及React Native的推薦IDE。

但請注意,在本文的時間點,它們尚未windows版。我是用Atom來開發React Native的。

發佈正式版

React Native的開發版是須要有一個packager server來隨時發送更新後的bundle js文件的。但若是要獲得真正簽名的正式版(app-release),你須要把bundle js文件保存到Android應用的資源目錄內。這樣,正式版再也不須要server支持,能夠獨立運行。

參照官方的發行APK包指南,你只須要這樣幾步:

  • 建立目錄app/src/main/assets
  • 運行如下命令(對應本文流程的目錄結構),將bundle js文件保存到資源目錄。
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/
  • 在Android Studio裏選擇BuildGenerate Signed APK...,生成正式版的apk。

官方有提到使用react.gradle文件的方法,但我以爲像上面這樣不用它更簡單。

正式版的即時更新

看起來正式版把bundle js文件保存到了apk內,這好像就丟失了React Native的即時更新?對的,但仍然有辦法實現它,你能夠看看React-Native-Remote-Update,這個項目已通過時了,但裏面貼出的原理很值得參考。

如今,你能夠用react-native-auto-updater來幫助你實現React Native的即時更新。

參考資料集

我在寫本文的過程當中參考了下面三個資料集合,以爲很是棒,在此也貼出來:

結語

React Native的Android版本是去年9月(2015.9.15)才推出,此前只有iOS版。相對來講,Android的相關教程要比iOS少不少。因 此,我以爲有這樣一份windows + React Native for Android的組合流程會頗有幫助。

來嘗試新的移動開發方案吧!

相關文章
相關標籤/搜索