React Native學(xué)習(xí)<四> 把ReactNative 嵌入到原生代碼中

核心概念

如果你正準(zhǔn)備從頭開始制作一個(gè)新的應(yīng)用棍厂,那么React Native會(huì)是個(gè)非常好的選擇颗味。但如果你只想給現(xiàn)有的原生應(yīng)用中添加一兩個(gè)視圖或是業(yè)務(wù)流程,React Native也同樣不在話下牺弹。只需簡(jiǎn)單幾步浦马,你就可以給原有應(yīng)用加上新的基于React Native的特性、畫面和視圖等张漂。

把React Native組件植入到Android應(yīng)用中有如下幾個(gè)主要步驟:

  1. 首先當(dāng)然要了解你要植入的React Native組件晶默。
  2. 在Android項(xiàng)目根目錄中使用npm來(lái)安裝react-native ,這樣同時(shí)會(huì)創(chuàng)建一個(gè)node_modules/的目錄航攒。
  3. 創(chuàng)建js文件磺陡,編寫React Native組件的js代碼。
  4. build.gradle文件中添加com.facebook.react:react-native:+漠畜,以及一個(gè)指向node_nodules/目錄中的react-native預(yù)編譯庫(kù)的maven路徑币他。
  5. 創(chuàng)建一個(gè)React Native專屬的Activity,在其中再創(chuàng)建ReactRootView盆驹。
  6. 啟動(dòng)React Native的Packager服務(wù)圆丹,運(yùn)行應(yīng)用。
  7. 根據(jù)需要添加更多React Native的組件躯喇。
  8. 在真機(jī)上運(yùn)行辫封、調(diào)試
  9. 打包廉丽。
  10. 發(fā)布應(yīng)用倦微,升職加薪,走向人生巔峰正压!??

開發(fā)環(huán)境準(zhǔn)備

首先按照開發(fā)環(huán)境搭建教程來(lái)安裝React Native在安卓平臺(tái)上所需的一切依賴軟件(比如npm)欣福。

在應(yīng)用中添加JS代碼

在項(xiàng)目的根目錄中運(yùn)行:

npm init
npm install --save react react-native
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
  • 1.npm init`創(chuàng)建了一個(gè)空的node模塊(過(guò)程需要填寫其實(shí)就是創(chuàng)建了一個(gè)package.json描述文件)。
流程
  • 2.npm install`則創(chuàng)建了node_modules目錄并把react和react-native下載到了其中焦履。
新增的部分
  • 3.這一步非必需拓劝,可跳過(guò)雏逾,curl命令,其實(shí)質(zhì)是下載.flowconfig配置文件郑临,這個(gè)文件用于約束js代碼的寫法栖博。

下面我們打開新創(chuàng)建的package.json文件,去除:

    "test": "echo \"Error: no test specified\" && exit 1"

然后在其scripts字段中加入:

"start": "node node_modules/react-native/local-cli/cli.js start"

現(xiàn)在你的package.json內(nèi)容應(yīng)該類似這樣:

{
  "name": "myreact",
  "version": "1.0.0",
  "description": "test",
  "main": "index.js",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"

  },
  "author": "AFinalStone",
  "license": "ISC",
  "dependencies": {
    "react": "^15.4.2",
    "react-native": "^0.41.2",
    "react-native-storage": "^0.2.1"
  }
}

示例中的version字段沒(méi)有太大意義(除非你要把你的項(xiàng)目發(fā)布到npm倉(cāng)庫(kù))厢洞。scripts中是用于啟動(dòng)packager服務(wù)的命令仇让。dependencies中的react和react-native的版本取決于你的具體需求。一般來(lái)說(shuō)我們推薦使用最新版本躺翻。你可以使用npm info reactnpm info react-native來(lái)查看當(dāng)前的最新版本丧叽。另外,react-native對(duì)react的版本有嚴(yán)格要求公你,高于或低于某個(gè)范圍都不可以踊淳。本文無(wú)法在這里列出所有react native和對(duì)應(yīng)的react版本要求,只能提醒讀者先嘗試執(zhí)行npm install省店,然后注意觀察安裝過(guò)程中的報(bào)錯(cuò)信息嚣崭,例如require react@某.某.某版本, but none was installed,然后根據(jù)這樣的提示懦傍,執(zhí)行npm i -S react@某.某.某版本雹舀。

接下來(lái)在項(xiàng)目根目錄中創(chuàng)建index.android.js文件,然后將下面的代碼復(fù)制粘貼進(jìn)來(lái):

'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}>Congratulations on your success</Text>
      </View>
    )
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

AppRegistry.registerComponent('myreact', () => HelloWorld);
  • 注意粗俱,AppRegistry.registerComponent('myreact', () => HelloWorld)的第一個(gè)參數(shù)要和package.json中的name一致

準(zhǔn)備工作

在你的app中 build.gradle 文件中添加 React Native 依賴:

 dependencies {
     compile "com.facebook.react:react-native:+" // From node_modules.
 }

如果想要指定特定的React Native版本说榆,可以用具體的版本號(hào)替換 +,當(dāng)然前提是你從npm里下載的是這個(gè)版本 寸认。

在項(xiàng)目的 build.gradle 文件中為 React Native 添加一個(gè) maven 依賴的入口签财,必須寫在 "allprojects" 代碼塊中:

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

確保依賴路徑的正確!以免在 Android Studio 運(yùn)行Gradle同步構(gòu)建時(shí)拋出 “Failed to resolve: com.facebook.react:react-native:0.x.x" 異常偏塞。

接著唱蒸,在 AndroidManifest.xml 清單文件中聲明網(wǎng)絡(luò)權(quán)限:

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

如果需要訪問(wèn) DevSettingsActivity 界面,也需要在 AndroidManifest.xml 中聲明:

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

This is only really used in dev mode when reloading JavaScript from the development server, so you can strip this in release builds if you need to.

添加原生代碼

想要通過(guò)原生代碼調(diào)用 React Native 灸叼,就像這樣神汹,我們需要在一個(gè) Activity 中創(chuàng)建一個(gè) ReactRootView 對(duì)象,將它關(guān)聯(lián)一個(gè) React application 并設(shè)為界面的主視圖古今。

如果你想在安卓5.0以下的系統(tǒng)上運(yùn)行屁魏,請(qǐng)用 com.android.support:appcompat 包中的 AppCompatActivity 代替 Activity

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;

public class MyReactActivity 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")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        // 注意這里的myreact必須對(duì)應(yīng)“index.android.js”中的
        // “AppRegistry.registerComponent()”的第一個(gè)參數(shù)
        mReactRootView.startReactApplication(mReactInstanceManager, "myreact", null);

        setContentView(mReactRootView);
    }

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

如果你的項(xiàng)目名字不是叫“myreact”捉腥,則需要將“index.android.js”中的“AppRegistry.registerComponent()”方法中的第一個(gè)參數(shù)替換為對(duì)應(yīng)的名字氓拼。

如果你使用的是 Android Studio , 可以使用Alt + Enter快捷鍵來(lái)自動(dòng)為MyReactActivity類補(bǔ)上缺失的import語(yǔ)句。注意引入的BuildConfig應(yīng)該是在你自己的包中,而不是在...facebook...的包中桃漾。

我們需要把 MyReactActivity 的主題設(shè)定為 Theme.AppCompat.Light.NoActionBar 坏匪,因?yàn)槔锩嬗性S多組件都使用了這一主題。

<activity
  android:name=".MyReactActivity"
  android:label="@string/app_name"
  android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>

現(xiàn)在activity已就緒呈队,可以運(yùn)行一些JavaScript代碼了剥槐。

配置權(quán)限以便開發(fā)中的紅屏錯(cuò)誤能正確顯示,或者你直接把targetSdkVersion設(shè)置為22也行

如果你的應(yīng)用會(huì)運(yùn)行在Android 6.0(API level 23)或更高版本,請(qǐng)確保你在開發(fā)版本中有打開懸浮窗(overlay)權(quán)限宪摧。If your app is targeting the Android API level 23 or greater, make sure you have the overlay permission enabled for the development build. You can check it with Settings.canDrawOverlays(this);. This is required in dev builds because react native development errors must be displayed above all the other windows. Due to the new permissions system introduced in the API level 23, the user needs to approve it. This can be acheived by adding the following code to the Activity file in the onCreate() method. OVERLAY_PERMISSION_REQ_CODE is a field of the class which would be responsible for passing the result back to the Activity.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (!Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                                   Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
    }
}

Finally, the onActivityResult() method (as shown in the code below) has to be overridden to handle the permission Accepted or Denied cases for consistent UX.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                // SYSTEM_ALERT_WINDOW permission not granted...
            }
        }
    }
}

運(yùn)行你的應(yīng)用

運(yùn)行應(yīng)用首先需要啟動(dòng)開發(fā)服務(wù)器(Packager)。你只需在項(xiàng)目根目錄中執(zhí)行以下命令即可:

npm install
npm start

保持packager的窗口運(yùn)行不要關(guān)閉颅崩,然后像往常一樣編譯運(yùn)行你的Android應(yīng)用(在命令行中執(zhí)行./gradlew installDebug或是在Android Studio中編譯運(yùn)行)几于。

如果你是使用Android Studio來(lái)編譯運(yùn)行,有可能會(huì)導(dǎo)致packger報(bào)錯(cuò)退出沿后。先把packager服務(wù)窗口關(guān)掉沿彭,然后再使用AndroidStudio編譯運(yùn)行Android應(yīng)用,
這個(gè)時(shí)候只保持在MainActivity頁(yè)面尖滚,然后重新在項(xiàng)目根目錄執(zhí)行npm start命令喉刘,然后操作APP進(jìn)入MyReactActivity的頁(yè)面

編譯執(zhí)行一切順利進(jìn)行之后,在進(jìn)入到MyReactActivity時(shí)應(yīng)該就能立刻從packager中讀取JavaScript代碼并執(zhí)行和顯示:

結(jié)果

在Android Studio中打包

你也可以使用Android Studio來(lái)打release包漆弄!其步驟基本和原生應(yīng)用一樣睦裳,只是在每次編譯打包之前需要先執(zhí)行js文件的打包(即生成離線的jsbundle文件,這樣APP進(jìn)入RN編寫的頁(yè)面會(huì)直接調(diào)用本身的JS代碼,不需要去服務(wù)器請(qǐng)求)撼唾。
具體的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

其中--bundle-output后面的兩個(gè)參數(shù)指出assets和res文件夾的具體路徑廉邑,注意把上述命令中的路徑替換為你實(shí)際項(xiàng)目的路徑。如果assets目錄不存在倒谷,需要提前自己創(chuàng)建一個(gè)蛛蒙。

然后在Android Studio中正常生成release版本即可!

打包好的release版本app運(yùn)行在許多X64CPU的手機(jī)上渤愁,打開RN頁(yè)面直接閃退

錯(cuò)誤:

java.lang.UnsatisfiedLinkError: dlopen failed: "xxx/libgnustl_shared.so" is 32-bit instead of 64-bit

解決方案:

1牵祟、在項(xiàng)目的根目錄的 gradle.properties里面添加一行代碼

android.useDeprecatedNdk=true.

2、在project的root目錄下的build.gradle中添加如下代碼抖格。

defaultConfig {
    ···
    ndk{
        abiFilters "armeabi-v7a","x86"
    }
    packagingOptions {
        exclude "lib/arm64-v8a/librealm-jni.so"
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诺苹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子他挎,更是在濱河造成了極大的恐慌筝尾,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件办桨,死亡現(xiàn)場(chǎng)離奇詭異筹淫,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門损姜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饰剥,“玉大人,你說(shuō)我怎么就攤上這事摧阅√兀” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵棒卷,是天一觀的道長(zhǎng)顾孽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)比规,這世上最難降的妖魔是什么若厚? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蜒什,結(jié)果婚禮上测秸,老公的妹妹穿的比我還像新娘。我一直安慰自己灾常,他們只是感情好霎冯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钞瀑,像睡著了一般沈撞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仔戈,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天关串,我揣著相機(jī)與錄音,去河邊找鬼监徘。 笑死晋修,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凰盔。 我是一名探鬼主播墓卦,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼户敬!你這毒婦竟也來(lái)了落剪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤尿庐,失蹤者是張志新(化名)和其女友劉穎忠怖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抄瑟,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凡泣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鞋拟。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骂维,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贺纲,到底是詐尸還是另有隱情航闺,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布猴誊,位于F島的核電站潦刃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稠肘。R本人自食惡果不足惜福铅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望项阴。 院中可真熱鬧,春花似錦笆包、人聲如沸环揽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)歉胶。三九已至,卻和暖如春巴粪,著一層夾襖步出監(jiān)牢的瞬間通今,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工肛根, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辫塌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓派哲,卻偏偏與公主長(zhǎng)得像臼氨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芭届,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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