React Native是如今移动开发新的可选方案,也带来了原属于Web领域的React的优秀开发特性。另外一方面,React Native的技术栈一经掌握,能够用于iOS、Android及Windows(见此)多个平台,即所说的“learn once, write anywhere”。javascript
如何使用React Native?参照官方指南,你会发现官方告诉你的是:请用react-native init
命令来建立一个React Native项目。这个项目的根目录结构是这样:html
可是,以Android为例,一个普通原生项目的根目录结构倒是这样(Android Studio 2.1.2):java
能够看到,Android原生项目(上图的Drill
根目录)平级于生成的React Native项目的android
目录。那么,若是一直以来都是Android原生开发,如今想要引入React Native,考虑部分页面用React Native实现,应该如何作呢?node
这就是React Native植入原生应用的问题。显然,react-native init
命令生成的项目在结构上不太相符,它的出发点更像是“彻底用React Native作一个多平台应用”,但咱们可能须要的是“一个原生应用但有部份内容是用React Native作的”。react
在本文的时间点,React Native的最新版是0.27。官方对此已给出植入原生Android应用的指南,但它不够准确,也缺乏一些细节。所以,本文将提供一个React Native植入原生Android应用的更详细一点的流程。android
若是你想了解iOS版的,能够阅读这篇文章。git
这篇流程是windows及Android Studio,若是你已是一个Android Studio原生应用开发者,以及Node.js用户,那么所需的环境你基本已经有了。详情请参考windows环境搭建文字教程以及开始使用React Native,什么都没有也不要紧,正好从头搭建。github
此外,Android模拟器使用了Genymotion,注册后就能够供我的使用,会比官方模拟器性能要好一些。shell
让咱们从一个全新的Android原生应用开始。npm
用Android Studio建立一个新项目,注意Minimum SDK应设置为API 16及以上(React Native要求Android4.1以上的环境):
到Android原生项目的根目录(也能够新建一个目录,但根目录比较经常使用)新建一个文件package.json
,内容以下(这里起名为react-native-module
):
{ "name": "react-native-module", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { "react": "^15.1.0", "react-native": "^0.27.2" } }
下面的dependencies
的内容要如何得知呢?答案是参考react-native init
生成的项目,毕竟版本号是会不断更新的。若是你已经init
生成过项目,能够运行react-native upgrade
更新后再参考。
而后,在这个package.json
的所在位置,执行:
npm install
安装好所需的npm组件。
一样在根目录,增长一个文件index.android.js
,这是React Native开发的具体内容,是任意的,这里给一个简单的例子:
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; class App extends Component { render() { return ( <View style={styles.container}> <Text style={styles.note}> acgtofe.com with react native </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, note: { fontSize: 20 } }); AppRegistry.registerComponent('react-native-module', () => App);
注意上面代码最后的react-native-module
这个名字比较重要,能够自定,但后面还会在其余地方用到,须要保持一致。
回到Android Studio,到app
的build.gradle
文件(module级别的gradle)里添加依赖:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile "com.facebook.react:react-native:0.27.2" }
最后一行的react-native
就是咱们新增的,注意这里的版本号要和package.json
里的一致。
运行一次Gradle Sync,你必然会获得这个错误:
这是由于Android项目默认的依赖包的源jcenter()
并不包含最新版的React Native(它只到0.20.1
)。新版的React Native都只在npm里发布,所以你须要增长一下依赖包的源,到根目录的build.gradle
文件(project级别的gradle)内增长如下内容(从官方的这句注释也能够了解到这一点):
allprojects { repositories { jcenter() maven { // All of React Native (JS, Android binaries) is installed from npm url "$projectDir/../node_modules/react-native/android" } } }
这里的url
路径,将取决于你放置node_modules
的位置(你能够根据须要选择放置在其余地方)。以上是node_modules
位于根目录时的url
路径,把它改成"$rootDir/node_modules/react-native/android"
也是能够的,它们等效。如何知道这个路径写对了呢?反复试就能够了,若是路径不对,Gradle Sync的时候必定会提示你前面的错误。
你可能在不少别的地方看到的都是这样的写法:
compile "com.facebook.react:react-native:+"
不太建议这样作,由于没有明确的版本号,你没法让系统帮你判断前面的url
路径写的是否正确。若是写错,Android将使用发布在jcenter()
的旧版React Native,而这将引起其余错误(见后文)。
新建一个继承自ReactActivity
的activity(这里起名为LiveActivity
),Android Studio会提醒你必须实现3个方法,通常写成这样:
public class LiveActivity extends ReactActivity { @Override protected String getMainComponentName() { return "react-native-module"; } @Override protected boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } }
getMainComponentName()
的字符串返回值,必须和前面的index.android.js
内的组件名一致。
getUseDeveloperSupport()
是一个逻辑返回值,表示是否启用开发者模式。这里写BuildConfig.DEBUG
就能够自动根据gradle构建的类型(debug或release)来决定。
getPackages
是模块列表,通常像上面代码这样就能够。若是你须要在JavaScript里调用原生Java模块,就要把它们添加到这里,具体能够参考这篇文章。
到Android清单文件AndroidManifest.xml
添加如下内容(省略了无关部分):
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.acgtofe.drill"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <application ... > ... <activity android:name=".LiveActivity" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> </application> </manifest>
前面的权限都是React Native开发环境须要用到的。后面的LiveActivity
是刚才的React Native运行界面,DevSettingsActivity
则是如下这个Dev Settings
的界面:
它也是开发版所必需的。
到package.json
的位置,打开命令行,运行packager server:
react-native start
也能够用npm start
。启动后的状态看起来像这样:
这是最后一步了。build这个Android项目,安装到模拟器里,而后打开应用,切换到LiveActivity
界面(用按钮跳转,或者直接设置为启动界面均可以),这时候应该只看到一片空白。
按ctrl
+ m
(这是Genymotion的用法,事实上,这是Android的Menu键,如今的实体设备基本没有这个键,但摇一摇能够触发)开启调试菜单,选择Dev Settings
,打开前面贴过图的DevSettingsActivity
,设置Debug server host & port for device
为本机ip地址(命令行内ipconfig
查看)。最后,回到LiveActivity
,开启调试菜单选择Reload JS
,等待一下子,若是你看到了像下图这样的界面:
就说明完成了!对应的,packager server里应该能够看到每一次请求的记录:
接下来,你就能够开始React Native的开发了,改动保存后,从新Reload JS
,就能够看到新的效果。
建议使用Android 5.0+的设备(包括模拟器),它们支持直接USB传输packager server返回的那个bundle js文件。若是是Android 5.0+,能够USB链接电脑后(若是是模拟器,那就等于已经链接)运行如下命令:
adb reverse tcp:8081 tcp:8081
而后就能够省略掉前面流程里设置本机ip地址那一步,直接Reload JS
。注意设备须要开启USB调试(模拟器不用),并且电脑同时只链接一个设备。
相对于前面设置本机ip地址的方式,这帮你免去了同一WiFi环境、代理等麻烦。
虽然流程看起来轻松愉快,但并不怎么能一次成功。下面是我在流程中碰到过的一些问题及其记录,能够用做参考。
错误提示以下:
参照github上的issue,这个错误的引起缘由是packager server的React Native版本和Android应用内的不一致。好比应用内的gradle依赖写的是compile "com.facebook.react:react-native:+"
但url
路径写得不对,结果用的就是jcenter()
里的0.20.1
的旧版,就会有这个问题。
所以,建议用compile "com.facebook.react:react-native:0.27.2"
这样的写法,并检查版本号是否和package.json
里的一致。
这是说index.android.js
文件不存在的错误。但我遇见的是文件就在那,也出这个错误。
这多是由不正确的缓存引发,个人解决方法:关闭server,删除index.android.js
,而后重启server,刷新,获得真正的404,而后还原index.android.js
,再刷新即解决。
先按照Try the following to fix the issue:
下给出的解决方法依次检查和尝试。若是仍不能解决,删除掉node_modules
目录,从新npm install
,而后重开server。
windows下删除node_modules
目录可能有路径过长的问题,推荐用rimraf来删除。
这个问题须要具体看server的输出,我这里的错误信息是:Error: Unable to find file with path: ......polyfills\prelude_dev.js
。相似前面的没法链接server,我也是删除node_modules
后从新安装获得解决。
流程中可能碰到的问题能够分为两类,Android应用(client)和server。若是看到错误,打开浏览器访问http://localhost:8081/index.android.bundle?platform=android
,若是能看到输出的JavaScript代码,那说明server是比较正常的,更多是Android应用的问题。反过来,若是浏览器里一样看到错误信息,那更可能就是server的问题。
你可能在开始用React Native的过程当中据说了Flow和Nuclide,它们分别是JavaScript类型检查工具及React Native的推荐IDE。
但请注意,在本文的时间点,它们尚未windows版。我是用Atom来开发React Native的。
React Native的开发版是须要有一个packager server来随时发送更新后的bundle js文件的。但若是要获得真正签名的正式版(app-release
),你须要把bundle js文件保存到Android应用的资源目录内。这样,正式版再也不须要server支持,能够独立运行。
参照官方的发行APK包指南,你只须要这样几步:
建立目录app/src/main/assets
。
运行如下命令(对应本文流程的目录结构),将bundle 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/
在Android Studio里选择Build
→Generate Signed APK...
,生成正式版的apk。
官方有提到使用react.gradle
文件的方法,但我以为像上面这样不用它更简单。
看起来正式版把bundle js文件保存到了apk内,这好像就丢失了React Native的即时更新?对的,但仍然有办法实现它,你能够看看React-Native-Remote-Update,这个项目已通过时了,但里面贴出的原理很值得参考。
如今,你能够用react-native-auto-updater来帮助你实现React Native的即时更新。
我在写本文的过程当中参考了下面三个资料集合,以为很是棒,在此也贴出来:
React Native的Android版本是去年9月(2015.9.15)才推出,此前只有iOS版。相对来讲,Android的相关教程要比iOS少不少。所以,我以为有这样一份windows + React Native for Android的组合流程会颇有帮助。
来尝试新的移动开发方案吧!
(从新编辑自个人博客,原文地址:http://acgtofe.com/posts/2016/06/react-native-embedding-android)