原生 Android 项目集成 React Native

Oui4366 8年前
   <p>本文主要介绍原生 Android 项目集成 React Native 并用于部分页面开发的流程。开发环境为 macOS 10.12、Android Studio 2.2.1、 React Native 0.35.0 。而官方给出的 植入原生 Android 应用指南 只对应到 0.28 版本。最新版(当前为 0.35)的集成方案稍微有些变动。</p>    <h2><strong>1. 创建/修改 Android 项目</strong></h2>    <p>用 Android Studio 创建一个 Android 项目,注意 <strong>Minimum SDK 要设置为 API 16 或以上</strong> ,因为 React Native 要求 Android 4.1 及以上的环境。</p>    <p>如果现有 Android 项目且 Minimum API 小于16则修改 Minimum SDK 到16,注意部分 API 变化。</p>    <h2><strong>2. 添加 package.json</strong></h2>    <p>在 Android 项目根目录新建文件 package.json ,内容如下</p>    <pre>  <code class="language-javascript">{    "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": {    }  }</code></pre>    <p>执行 npm install 就可以安装 dependencies 下的 npm 组件了。</p>    <p>这个时候在 Android 项目根目录就生成了 node_modules/ 文件夹,里面就是一些用到的组件。</p>    <p>在 .gitignore 中添加</p>    <pre>  <code class="language-javascript"># node.js  node_modules/  npm-debug.log</code></pre>    <p>执行 react-native upgrade 可以更新已有组件。</p>    <h2><strong>3. 添加 index.android.js</strong></h2>    <p>在 Android 项目根目录创建目录 js/ ,js 相关的代码就放在这个文件夹下。</p>    <p>在 js/ 下添加 App.js ,内容如下</p>    <pre>  <code class="language-javascript">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'    }  })</code></pre>    <p>在 Android 项目根目录新建文件 index.android.js ,内容如下</p>    <pre>  <code class="language-javascript">import { AppRegistry } from 'react-native'  import App from './js/App'    AppRegistry.registerComponent('navigation', () => App)</code></pre>    <p>这里的 navigation 一般会根据模块功能命名,后面还会用到。</p>    <p>当然也可以把 App.js 的内容写在 index.android.js 里,但这样写更清晰一些,尤其是项目大了文件多的情况。</p>    <h2><strong>4. Android 项目添加依赖</strong></h2>    <h3><strong>4.1 project 级别的 build.gralde</strong></h3>    <p>Android 默认的依赖包的源 jcenter() 不包含最新版的 React Native,新版的 React Native 都只在 npm 里发布,因此要依赖 node_modules/ 下的东西。</p>    <p>在 Android 项目根目录下的 build.gradle 文件添加如下内容</p>    <pre>  <code class="language-javascript">allprojects {      repositories {          maven {              // All of React Native (JS, Android binaries) is installed from npm              url "$rootDir/node_modules/react-native/android"          }      }  }</code></pre>    <h3><strong>4.2 module 级别的 build.gradle</strong></h3>    <p>在 Android 项目 app 目录下的 build.gradle 文件添加如下内容</p>    <pre>  <code class="language-javascript">// react native  compile 'com.非死book.react:react-native:0.35.0'  // From node_modules</code></pre>    <p>这里版本号要和 package.json 里的一致。</p>    <h2><strong>5. React Native 相关的 Activity 和 Application</strong></h2>    <h3><strong>5.1 Activity</strong></h3>    <p>创建一个继承自 com.非死book.react.ReactActivity 的 Activity</p>    <pre>  <code class="language-javascript">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";      }  }</code></pre>    <p>重写 getMainComponentName() 方法,返回的字符串必须和前面的 AppRegistry.registerComponent('navigation', () => App) 里的 navigation 对应,表示该 Activity 会显示对应组件里的内容。</p>    <h3><strong>5.2 Application</strong></h3>    <p>Application 需要实现 com.非死book.react.ReactApplication 接口,并实现其方法</p>    <pre>  <code class="language-javascript">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;  }</code></pre>    <p>getUseDeveloperSupport() 表示是否启动开发者模式。</p>    <p>getPackages() 是引用的模块列表,默认需要添加 MainReactPackage ,如果需要在 js 里调用原生 Java 模块,需要添加自定义的模块(如 OtherReactPackage )。</p>    <p>新版这两个方法是写在 Application 里的,旧版都是些在 Activity 里的。</p>    <h3><strong>5.3 AndroidManifest.xml</strong></h3>    <p>在 AndroidManifest.xml 里需要添加自己创建的 Activity 和 React Native 提供的 DevSettingsActivity ,还需要添加两个权限。</p>    <pre>  <code class="language-javascript"><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.非死book.react.devsupport.DevSettingsActivity" />  </application></code></pre>    <p>HelloReactActivity 是自己创建的页面, DevSettingsActivity 是开发调试需要用到的设置页面。</p>    <p>android.permission.INTERNET 用于开发调试, android.permission.SYSTEM_ALERT_WINDOW 用于显示悬浮窗。</p>    <h2><strong>6. 启动服务</strong></h2>    <p>debug 模式下需要在 package.json 所在目录下执行 npm start ,它等效于 package.json 里的 node node_modules/react-native/local-cli/cli.js start ,相当于启动一个本地服务。</p>    <p>Terminal 显示如下表示服务已正常启动</p>    <pre>  <code class="language-javascript">> 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/非死book/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)</code></pre>    <h2><strong>7. 开发调试</strong></h2>    <p>构建 Android 项目,打开应用,切换到 HelloReactActivity 页面,通过摇一摇开启调试菜单,选择 Dev Settings 进入 DevSettingsActivity</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/629100d1e9b4577152918e7b920be3ff.png"></p>    <p style="text-align:center">Dev Settings.png</p>    <p>设置 Debug server host & port for device 为本机 IP 地址,添加端口号</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/baa2700d7b405afcc223ca41ebddf54c.png"></p>    <p style="text-align:center">Debug server host & port for device.png</p>    <p>返回到 HelloReactActivity 页面,摇一摇选择 Reload ,接下来就可以开始调试了。</p>    <p>每次修改 js 代码只需 Reload 即可,无需重新构建整个 Android 项目,修改 Java 代码需要重新构建。</p>    <h2><strong>8. 发布正式包</strong></h2>    <h3><strong>8.1 js bundle</strong></h3>    <p>React Native 的开发版需要有启动一个本地服务随时发送更新后的 js bundle 文件。 如果要打正式包,需要把 js bundle 文件保存到 Android 项目的 assets/ 目录下。 这样,正式包就不需要本地服务支持了。</p>    <p>在 app/src/main/ 下创建 assets/ 文件夹,执行以下命令将 js bundle 保存到资源目录下</p>    <pre>  <code class="language-javascript">$ 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/</code></pre>    <p>在 app/src/main/assets/ 下就会生成 index.android.bundle 文件。</p>    <p>开发模式调试的时候 js 代码会立即生效,无需执行以上命令,但每次正式打包的时候如果改了 js 代码都必须先执行以上命令。</p>    <h3><strong>8.2 Proguard</strong></h3>    <p>集成 React Native 之后如果不加相关的混淆规则,打 release 包的时候就会报错。</p>    <p> </p>    <pre>  <code class="language-javascript"># 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.非死book.proguard.annotations.DoNotStrip  -keep,allowobfuscation @interface com.非死book.proguard.annotations.KeepGettersAndSetters  -keep,allowobfuscation @interface com.非死book.common.internal.DoNotStrip    # Do not strip any method/class that is annotated with @DoNotStrip  -keep @com.非死book.proguard.annotations.DoNotStrip class *  -keep @com.非死book.common.internal.DoNotStrip class *  -keepclassmembers class * {      @com.非死book.proguard.annotations.DoNotStrip *;      @com.非死book.common.internal.DoNotStrip *;  }    -keepclassmembers @com.非死book.proguard.annotations.KeepGettersAndSetters class * {    void set*(***);    *** get*();  }    -keep class * extends com.非死book.react.bridge.JavaScriptModule { *; }  -keep class * extends com.非死book.react.bridge.NativeModule { *; }  -keepclassmembers,includedescriptorclasses class * { native <methods>; }  -keepclassmembers class *  { @com.非死book.react.uimanager.UIProp <fields>; }  -keepclassmembers class *  { @com.非死book.react.uimanager.annotations.ReactProp <methods>; }  -keepclassmembers class *  { @com.非死book.react.uimanager.annotations.ReactPropGroup <methods>; }    -dontwarn com.非死book.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.**</code></pre>    <p>完成以上两步后就可以通过 ./gradlew assembleRelease 打正式包了。</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/fc29c86fc2b8</p>    <p> </p>