1. 程式人生 > >React Native和Android整合詳解

React Native和Android整合詳解

前言

按照React Native的迭代速度,使用官網的文件,已經不能很順利的實現React Native和Android的有效整合。React Native最新版本 已經是0.39。為了更好的講解React Native和Android的整合我這裡列出我本地的環境:

  • Android Stuidio 2.2穩定版
  • 64位win7作業系統
  • 紅米note3雙網通普配版
  • React Native 0.39

具體實踐

建立專案

這一步按照AS新建專案嚮導一步步完成即可,完成後。

  • 在app module下的build.gradle檔案的dependencies中新增React Native 依賴:compile
    “com.facebook.react:react-native:+”
  • 修改Manifest檔案:
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
<activity    android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

注:compile SDK 和target SDK都是24(網上有文章講,使用的appcompat-v7支援包版本必須是23.0.1,compile SDK和target SDK也必須是23 。不過最新的也支援的)

compile 'com.android.support:appcompat-v7:24.2.1'

如果你出現下面的錯誤,可以降低版本到23.

Caused by: java.lang.IllegalAccessError: Method 'void 
android.support.v4.net.ConnectivityManagerCompat.<init>()' 
is inaccessible to class 
'com.facebook.react.modules.netinfo.NetInfoModule' 
(declaration of 'com.facebook.react.modules.netinfo.NetInfoModule'
appears in /data/app/com.milter.www.awesomeproject2-2/base.apk)

將Android專案變成React Native專案

其實整合的過程就是將一個原生的Android專案,轉換為滿足React Native結構格式的專案React Native專案結構

  • 建立並修改package.json檔案
    進入Android專案的根目錄,使用命令:
npm init

這個命令會引導你在ReactNativeWithNativeApp目錄下建立一個package.json檔案。如圖所示:
這裡寫圖片描述

接下來我們對package.json檔案進行修改,修改部分如下:

 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }

修改為:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
 ,"start": "node node_modules/react-native/local-cli/cli.js start" 
}

修改後,我們在專案根目錄的命令列視窗中輸入命令:

npm start

就相當於執行如下命令:

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

隨著package.json檔案的建立,我們的專案也變成了一個Node專案。

引入React Native 模組

在專案根目錄下輸入如下的命令:

npm install --save react react-native

執行完後我們發現專案多了一個node_modules檔案,react native依賴的庫都會在這裡看到。

  • 建立.flowconfig檔案
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig

這一命令的作用是將命令中url指向的.flowconfig檔案下載到專案的根目錄。在上面的圖packagejson中可以看到這個下載後的檔案。關於curl的講解請看curl詳解
注:如果你不想使用curl命令,你可以可以https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig拷貝里的內容存為.flowconfig檔案。

建立RN程式

在根目錄下建立index.android.js檔案,如果你是直接用react-native init demo(專案名),也可以拷貝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);

將React Native程式整合進Android專案

在專案根目錄的build.gradle中(注意:不是app模組中的build.gradle檔案)新增依賴。

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

修改MainActivity內容,完整程式碼如下:

public class MainActivity extends AppCompatActivity
      implements DefaultHardwareBackBtnHandler {

  private ReactRootView mReactRootView;
  private ReactInstanceManager mReactInstanceManager;
  private LifecycleState mLifecycleState
          = LifecycleState.BEFORE_RESUME;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    /* 下面的版本判斷程式碼官方文件中沒有,
      如果不新增,在6.0以上的Android版本中會報錯 */
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          if (!Settings.canDrawOverlays(this)) {
              Intent serviceIntent = new Intent(
                      Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
              startActivity(serviceIntent);
          }
      }
      mReactRootView = new ReactRootView(this);
      mReactInstanceManager = ReactInstanceManager.builder()
              .setApplication(getApplication())
              .setBundleAssetName("index.android.bundle")
              .setJSMainModuleName("index.android")
              .addPackage(new MainReactPackage())
              .setUseDeveloperSupport(BuildConfig.DEBUG)
              .setInitialLifecycleState(mLifecycleState)
              .build();
//下面程式碼中的"HelloWorld"來自index.android.js檔案中最後一行程式碼
      mReactRootView.startReactApplication(mReactInstanceManager,
              "HelloWorld", null);

      setContentView(mReactRootView);
  }

  @Override
  protected void onPause() {
      super.onPause();

      mLifecycleState = LifecycleState.BEFORE_RESUME;

      if (mReactInstanceManager != null) {
          mReactInstanceManager.onHostPause();
      }
  }

  @Override
  protected void onResume() {
      super.onResume();

      mLifecycleState = LifecycleState.RESUMED;

      if (mReactInstanceManager != null) {
          mReactInstanceManager.onHostResume(this, this);
      }
  }

  @Override
  protected void onDestroy() {
      super.onDestroy();

      mReactRootView.unmountReactApplication();
      mReactRootView = null;

      if (mReactInstanceManager != null) {
          mReactInstanceManager.destroy();
      }
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode,
                               Intent data) {
      if (mReactInstanceManager != null) {
          mReactInstanceManager.onActivityResult(this,requestCode,
                  resultCode, data);
      }
  }

  @Override
  public void onBackPressed() {
      if (mReactInstanceManager != null) {
          mReactInstanceManager.onBackPressed();
      }
      else {
          super.onBackPressed();
      }
  }

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

執行配置

使用npm start命令執行專案,然後使用

react-native run-android

如果報錯,請往下看。如果出現如下錯誤:

java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so

這個錯誤的原因是React Native提供的libreactnativejni.so檔案是32位,而我們的專案中用了一些不相容的64位so檔案,二者混在一起產生的。解決的辦法就是禁止使用那些64位的so檔案。
第一,在專案根目錄下的gradle.properties檔案最後加上這樣一句:

android.useDeprecatedNdk=true

第二、在app module下的build.gradle檔案中新增如下內容:

android {
    ...
    defaultConfig {
        ...
        ndk{
            abiFilters "armeabi-v7a", "x86"
        }
        ...
    }
...
}

第三、找出不相容的64位so檔案並禁止它們
在目錄…\ReactNativeWithNativeApp\app\build\outputs\apk下找到app-debug.apk,並把它解壓,檢視一下,解壓後的檔案的lib目錄下有沒有這個目錄:
arm64-v8a
如果有這個目錄,看看裡面的so檔案,都是我們要禁止的,禁止的方法如下:假設裡面有一個 1.so檔案,我們要在app module下的build.gradle檔案中做如下修改:

android {
    ...
    defaultConfig {
        ...
        ndk{
            abiFilters "armeabi-v7a", "x86"
        }
        packagingOptions {
            exclude "lib/arm64-v8a/1.so"            
        }
        ...
    }
...
}

好了,整合就說完了,請大家持續關注哦,現在出專案實戰了。