原生 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>