React Native for Android 入门老虎

原文出处:http://www.race604.com/react-native-for-android-start/ 

昨天期待已久的 React Native for Android 发布了,赶紧来尝试一下,我跟着这个 Getting Started 开开始入门。发现被一些 “老虎” 堵在门口, Hello World 都跑不起来,入不了 React Native 的门,让我很懊恼,最后终于解决。可能大家也会遇到类似的问题,这篇文章希望能帮到你。

安装环境

根据官方的入门文档,需要安装 Homebrew, nvm, Node.js 4.0, watchman, flow。

首先 Homebrew 大家应该都安装好了,这里就不说了。接下来实用 brew 安装 nvm:

$ brew install nvm

然后会提示你把 nvm 加入环境变量中,例如,我就在 .bashrc 或者 .zshrc 中加入如下两行:

export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh

然后使用 nvm 安装最新的 Node.js 4.0(Node.js 怎么突然到了 4.0 了,之前版本都是 0.xx?还是很惊讶的)。

$ nvm install node && nvm alias default node

我在这里遇到了第一只 “老虎”。这个命令执行不下去,我来打开 NodeJS 官网,发现竟然打不开,原来是 Node.js 的访问有问题。如果也有同学和我一样,可以点击这里下载这个安装包直接安装也可以。

另外多说一句:nvm 确实是一个好东西,可以在你的机器上装几个版本的 Node.js,然后根据需要切换版本。例如装完 4.0 以后,发现 Hexo 挂掉了,可以方便的实用 nvm 切换到 0.12。

后面的安装没有啥问题,按照官方文档来就可以:

$ brew install watchman
$ brew install flow

最后,最好执行一下如下命令,更新相关软件:

$ brew update && brew upgrade

开始 Hello World

环境安装好了,迫不及待的来 Hello world。运行如下几条命令,直接可以生成一个样例项目:

$ npm install -g react-native-cli
$ react-native init AwesomeProject
$ cd AwesomeProject/

接下来就是激动人心的时候,插上手机,开始运行:

$ react-native run-android

这里会使用 Gradle 进行编辑。编译过程中有可能会出现 Android sdk 找不到,Build tools 版本找不到的错误。这是因为 RN for Android 需要环境变量 ANDROID_HOME:

export ANDROID_HOME=/usr/local/opt/android-sdk

并且需要 Build-tools version 23.0.1,API 23 等,安装好就可以正常编译了。

正常情况下会自动安装安装 APK 包并运行,与此同时会打开一个终端运行 dev server。但是,我期待的 Hello World 页面并没有出现,只是出现了一个空白的 Activity。发生了什么?我一步一步检查我的配置过程,是不是我漏掉了什么,并没有发现我做错什么。

我发现运行运行 dev server 的终端中打出了一些错误信息:

dyld: Library not loaded: /usr/local/lib/libpcre.1.dylib
  Referenced from: /usr/local/bin/watchman
  Reason: image not found
dyld: Library not loaded: /usr/local/lib/libpcre.1.dylib
  Referenced from: /usr/local/bin/watchman
  Reason: image not found
Watchman:  watchman--no-pretty get-sockname returned with exit code null dyld: Library not loaded: /usr/local/lib/libpcre.1.dylib
  Referenced from: /usr/local/bin/watchman
  Reason: image not found
 ERROR  watchman--no-pretty get-sockname returned with exit code null dyld: Library not loaded: /usr/local/lib/libpcre.1.dylib
  Referenced from: /usr/local/bin/watchman
  Reason: image not found
Error: watchman--no-pretty get-sockname returned with exit code null dyld: Library not loaded: /usr/local/lib/libpcre.1.dylib
  Referenced from: /usr/local/bin/watchman
  Reason: image not found
    at ChildProcess.<anonymous> (/Users/Jing/Projects/ReactNative/AwesomeProject/node_modules/react-native/node_modules/sane/node_modules/fb-watchman/index.js:194:18)
    at emitTwo (events.js:87:13)
    at ChildProcess.emit (events.js:172:7)
    at maybeClose (internal/child_process.js:817:16)
    at Socket.<anonymous> (internal/child_process.js:319:11)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at Pipe._onclose (net.js:469:12)

经过一番搜索,发现是 pcre 找不到,通过如下命令即可修复:

$ brew uninstall pcre && brew install pcre

有时候你运行 react-native run-android,发现并不能自动运行 dev server,你可以在当前项目目录中运行如下命令来手动启动 server:

$ react-native start

再次启动,发现还是空白的页面,终端中也没有任何的错误提示,按 menu 键、摇手机也没有出现官网中所说的开发者菜单。

是不是我手机的问题?我决定使用模拟器试一下,果然,终于出现能够正常显示了内容了。也知道正常的 React Native App 开发运行时怎么样的了:首先会出现一个 Loading JS 的窗口,并且在 dev server 端也会打出客户端下载 JS Bundle 的 log。

我再用手机试一下,发现 APP 直接显示一个空白的页面,dev server 终端也没有打印任何 log。难道是 dev server 没有连接上 server 端?看一下 Logcat 的 log,有如下错误:

E/unknown:React: Unable to download JS bundle
E/unknown:React: java.net.ConnectException: failed to connect to localhost/127.0.0.1 (port 8081) after 5000ms: isConnected failed: ECONNREFUSED (Connection refused)
E/unknown:React:     at libcore.io.IoBridge.isConnected(IoBridge.java:223)
E/unknown:React:     at libcore.io.IoBridge.connectErrno(IoBridge.java:161)
E/unknown:React:     at libcore.io.IoBridge.connect(IoBridge.java:112)
...
E/unknown:React:  Caused by: libcore.io.ErrnoException: isConnected failed: ECONNREFUSED (Connection refused)
E/unknown:React:     at libcore.io.IoBridge.isConnected(IoBridge.java:208)
E/unknown:React:     at libcore.io.IoBridge.connectErrno(IoBridge.java:161) 
E/unknown:React:     at libcore.io.IoBridge.connect(IoBridge.java:112) 
E/unknown:React:     at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192) 
E/unknown:React:     at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:460) 
E/unknown:React:     at java.net.Socket.connect(Socket.java:833) 
...

发现,果然是 Server 访问错误。参考官方文档。对于 Android 5.0 及以上的设备,直接运行:

$ adb reverse tcp:8081 tcp:8081

我找来一个 Android 5.0 的机器,果然可以了。对于 5.0 以下的机器,上面的命令会出错:

$ adb reverse tcp:8081 tcp:8081
error: closed
error: closed

我的主力开发机器是 Android 4.4 的。按照官方文档,需要按 menu 键或者摇晃手机,在开发菜单中设置一下 dev server 的 IP 地址。前面我就试过了,我怎么操作都不能打开这个菜单。突然想到,是不是系统屏蔽了?我去找系统权限设置,发现如下:

blob.png

果然,系统默认禁用掉了 APP 的显示悬浮窗的权限。因为那个菜单是实用悬浮窗来显示的,所以一直看不到。开启这个权限,然后就可以正常唤起菜单了。然后菜单中选择 Dev Settings ->Debug server host for device,填入你开发电脑的 IP 地址。如果手机和电脑不在同一个网段,或者手机不能访问到,就会出现如下页面:

之前显示空白页面,其实应该是弹出这个错误页面的,因为这个页面也是一个悬浮窗,默认也被禁止了。

到现在,终于真相大白了,可以对 React Native “Say Hello” 了。为了运行一个 Hello World 花费了我不少时间,接下来就可以开始 React Native 之旅了。

总结一下

  1. 确保相关工具和 Android SDK 都是最新的;

  2. 因为 React Native for Android 提示错误和开发菜单都是通过悬浮窗显示的,要注意的 ROM 有没有自作聪明的帮你默认禁用掉了显示悬浮窗的权限;

  3. RN 需要启动一个 Dev server 来辅助开发,Android 5.0 可以直接通过 USB 的访问,5.0 以下只能通过 WiFi 来访问。为了便于开发,还是建议实用 5.0 的机器。