React Native—集成到原生Android項目

目前的react-native版本是0.57.8,參考了一些文章之後,終於集成好了。React Native環境的搭建我就不説了,詳細的能夠參考React Native中文網的搭建文檔javascript

創建新的Android工程


環境配置好之後(sdk下載可能比較慢),用Android Studio創建一個Empty Activity的項目。html

集成React Native


  1. 安裝相關依賴java

    • 在項目根目錄下執行 npm init 命令,生成 package.json 文件。此時要注意一下package name和入口文件entry point(新版都寫index.js)。然後package.json 文件中scripts標簽下添加start配置,便可通過npm start啓動。node

      "start": "node node_modules/react-native/local-cli/cli.js start"
    • 然後執行命令npm install --save react react-native,若是出現RN須要哪一個版本react支持就install @xx.xx版本的。react

      "dependencies": {
          "react": "^16.6.3",
          "react-native": "^0.57.8"
        }

      這是我目前的版本。android

  2. 在項目的根目錄下創建.flowconfig文件,該文件的內容能夠從Facebook 的GitHub上下載, 查看這裏, 將其內容copy進去便可ios

    也可直接運行命令創建.flowconfig文件git

    curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig //下載.flowconfig文件

    Flow allows us to easily add static type checking to our JavaScript. Flow will help you prevent bugs and allow for better code documentation among other things. A lot of the React Native documentation and source code already also uses flow, so there has never been a better time to start using it!github

  3. 配置mavenshell

    在app/ 目錄下的 build.gradle 文件中的 dependencies 裏添加 React Native 依賴:

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

    若是想要指定特定的 React Native 版本,能夠用具體的版本號替換 +,固然前提是你從 npm 裏下載的是這個版本。

    在項目的 build.gradle 文件中爲 React Native 添加一個 maven 依賴的入口,必須寫在 "allprojects" 代碼塊中:

    allprojects {
        repositories {
            maven {
                // All of React Native (JS, Android binaries) is installed from npm
                url "$rootDir/../node_modules/react-native/android"
                // 若node_modules在根目錄下,直接寫成url "$rootDir/node_modules/react-native/android"
            }
            ...
        }
        ...
    }
  4. 添加NDK支持

    若是不添加這句話的話,可能會形成關於32位和64位SO庫混合引入Crash:

    • 在App 的build.gradle中的defaultConfig節點下添加以下語句:
    ndk {
       abiFilters "armeabi-v7a", "x86"
    }
    即:
    defaultConfig {
            applicationId "com.example.lance.myapplication"
            minSdkVersion 23
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            ndk {
                abiFilters "armeabi-v7a", "x86"
            }
        }
    • 而後在咱們project下的gradle.properties文件中的末尾添加以下語句:
      android.useDeprecatedNdk=true

    • 在App的build.gradle文件的android節點下添加:
    configurations.all {
         resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
     }
  5. 代碼集成

    建立一個index.js文件

    建立咱們的 react-native 組件,在根目錄下建立 index.js,(在 react-native 0.49.0版本之前,是 index.android.js 和 index.ios.js 做爲入口文件,如今合成一個 index.js 文件了)

    import React from 'react';
    import {AppRegistry, StyleSheet, Text, View} from 'react-native';
    
    class App extends React.Component {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.hello}>Hello, React Native!</Text>
          </View>
        );
      }
    }
    var styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
      },
      hello: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
        color: 'red',
      },
    });
    
    AppRegistry.registerComponent('MyReactNativeApp', () => App);

    完成以上步驟後,React Native就算集成到咱們現有的Android項目中,接下來咱們用Activity來展現咱們的ReactNative.

    創建一個Activity

    新版本中的ReactNative只須要自定義一個Activity,並將其集成ReactActivity,實現getMainComponentName()方法,在該方法中返回一開始咱們註冊的ReactNative的名稱便可(即跟咱們的index.android.js中的AppRegistry中的第一個參數相同),在ReactActivity中,已經幫咱們實現了ReactRoot與ReactInstanceManager的配置,在以前的版本中,ReactRoot與ReactInstanceManager須要咱們本身去寫.

    建立 MyReactActivity, 在上面建立安卓項目的時候,已經建立了一個 MainActivity,在它的同級目錄下,在 Android Studio右鍵新建一個 Activity,命名爲 MyReactActivity,而後把內容改成以下:

    package com.xl.hellorn;
    
    import javax.annotation.Nullable;
    
    import com.facebook.react.ReactActivity;
    
    public class MyReactActivity extends ReactActivity {
        @Nullable
        @Override
        protected String getMainComponentName() {
            return "MyReactNativeApp";   //MyReactNativeApp即註冊ReactNative時的名稱;
        }
    }

    建立 MyApplication

    初始化一個ReactNativeHost,該MyApplication繼承Application並實現ReactApplication,在源碼loadApp方法時,會將當前的Activity的Application強制轉換成ReactApplication:

    package com.xl.hellorn;
    
    import android.app.Application;
    
    import com.facebook.react.ReactApplication;
    import com.facebook.react.ReactNativeHost;
    import com.facebook.react.ReactPackage;
    import com.facebook.react.shell.MainReactPackage;
    import com.facebook.soloader.SoLoader;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MyApplication extends Application implements ReactApplication {
    
        private final ReactNativeHost reactNativeHost = 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 reactNativeHost;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            SoLoader.init(this,false);
        }
    }

    最後在 app/src/main/AndroidManifest.xml 文件中,添加一些權限,以及聲明MyApplication 跟 MyReactActivity

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xl.hellorn">
    
        <uses-permission android:name="android.permission.INTERNET"/>   <!-- 網絡權限 -->
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <!-- 彈框權限 -->
        <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/> <!-- 窗體覆蓋權限 -->
    <!-- 聲明MainApplication -->
        <application
            android:name=".MyApplication"
            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">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <!-- 聲明MyReactActivity -->
            <activity
                android:name=".MyReactActivity"
                android:label="@string/app_name"
                android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            </activity>
            <!-- 聲明能夠經過晃動手機或者點擊Menu菜單打開相關的調試頁面 -->
            <activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
        </application>
    
    </manifest>

原生頁面跳轉到react-native頁面


在原生中加一個按鈕,在 MainActivity 添加一個按鈕跳轉到 MyReactActivity,首先在 app/src/main/res/layout 下的 activity_main.xml 添加一個按鈕元素

<Button
    android:id="@+id/btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="點擊跳轉到RN界面"/>

在 MainActivity 裏添加點擊跳轉事件

package com.xl.hellorn;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 點擊按鈕跳轉到 react-native 頁面
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this,MyReactActivity.class));
            }
        });
    }
}

完成以上步驟後,切換到咱們的Terminal窗口,執行npm start命令來啓動咱們的React Native Server, 若是在這出現錯誤, 則能夠認爲咱們的項目中沒有index.android.bundle這個文件 , 接下來, 咱們手動的在app/src/main/目錄中添加一個assets目錄, 並在該目錄下添加一個index.android.bundle文件, 而後手動在該bundle文件中添加內容, 執行如下命令:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest app/src/main/res/

聲明:

  1. --platform : 平臺(android/ios)
  2. --dev : 開發模式
  3. --entry-file : 條目文件
  4. --bundle-output : bundle文件生成的目錄
  5. --assets-dest : 資源文件生成的目錄

再次執行 npm start 啓動本地服務器,點擊按鈕,出現了紅屏,也就是錯誤頁面。

從錯誤信息 error: bundling failed: NotFoundError: Cannot find entry file index.android.js in any of the roots 咱們能夠看出找不到入口文件index.android.js,而咱們的入口文件是 index.js,所以咱們須要另外加一些配置讓它知道咱們的入口文件實際上是 index.js
解決方法參考 react-native/issues/16517。在 app/ 目錄下的 build.gradle 文件中最上面添加

apply plugin: 'com.android.application' // 這時原來存在的
apply from: "../node_modules/react-native/react.gradle"
project.ext.react = [
    entryFile: "index.js"
]

而後在 MainApplication 的 ReactNativeHost 類中添加:

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    ...
    // 這是須要添加的
    @Override
    protected String getJSMainModuleName() {
        return "index";
    }
}

再次運行,就能夠正常跳轉到 react-native 的頁面了。

參考連接:
http://www.apkbus.com/blog-847095-77211.html
https://github.com/lin-xin/blog/issues/26

相關文章
相關標籤/搜索