React Native For Android初体验
@author ASCE1885的 Github 微博 CSDN
本文由于潜在的商业目的,不开放全文转载许可,谢谢!
React Native For Android提前发布了,代码托管在Github上面,本文是一个尝鲜体验,主要介绍环境配置的过程。
环境配置
目前React Native只支持在OS X系统上面进行开发,其他系统的筒靴们请掩泪飘过,同时,使用React Native开发的app只能运行在>= Android 4.1 (API 16) 和>= iOS 7.0的手机操作系统上面。
搭建React Native的开发环境涉及到几个工具,这里我们简单介绍一下:
Homebrew
Homebrew是一个方便开发者在MAC OS X系统上面安装Linux工具包的ruby脚本,而MAC OS X已经内置了ruby的解释环境,因此安装Homebrew只需执行以下脚本:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
更多关于Homebrew的用法请参见官方文档。关于Homebrew的作者,还有一个趣闻:白板编程没写出反转二叉树,Homebrew 作者被谷歌拒掉了。
nvm
Node版本管理器,是一个简单的bash脚本,用来管理同一台电脑上的多个node.js版本,并可实现方便的版本间切换。我们可以使用Homebrew来安装nvm:
brew install nvm
然后打开.bashrc文件
vim $HOME/.bashrc
添加如下配置:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
当然也可以选择官方的安装方法,就不用自己手动写.bashrc文件了:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.26.1/install.sh | bash
或者
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.26.1/install.sh | bash
这样配置之后,在Terminal输入nvm命令还是提示command not found,需要再次输入:
. ~/.nvm/nvm.sh
激活nvm,这一点比较奇怪,因为我们在.bashrc已经设置了才对。
Node.js
基于Chrome V8 JavaScript引擎实现的一个JavaScript运行时,可用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行的数据密集型的实时应用。通过nvm安装Node.js的命令如下:
nvm install node && nvm alias default node
不过可能由于网络或者服务不稳定,实际上使用这个命令安装可能会失败,就算成功也会花费较长的时间,因此建议到Node.js官网去直接下载pkg包:
watchman
Facebook 开源的一个文件监控服务,用来监视文件并且记录文件的改动情况,当文件变更它可以触发一些操作,例如执行一些命令等等。安装watchman,是为了规避node文件监控的一个bug,安装很简单,脚本如下:
brew install watchman
flow
Facebook 出品的一个用于 JavaScript 代码静态类型检查的工具,用于找出 JavaScript 代码中的类型错误。Flow 采用 OCaml 语言开发。安装脚本如下:
brew install flow
安装完成之后,可以执行如下命令更新 Homebrew 的信息,并升级所有可以升级的软件:
brew update && brew upgrade
Android开发环境要求
打开Android SDK Manager,确保如下工具和开发包已经安装:
SDK:
- Android SDK Build-tools version 23.0.1
- Android 6.0 (API 23)
- Android Support Repository
模拟器:
- Intel x86 Atom System Image (for Android 5.1.1 - API 22)
- Intel x86 Emulator Accelerator (HAXM installer)
React Native工程配置
安装react-native
npm install -g react-native-cli
在Terminal中运行以上脚本,成功后,就可以在Terminal中使用react-native这个命令了,这个脚本只需执行一次。
生成工程
react-native init AwesomeProject
在Terminal中执行以上脚本,它会下载React Native工程源码和依赖,并在AwesomeProject/iOS/AwesomeProject.xcodeproj
目录中创建XCode工程,在AwesomeProject/android/app
创建Android Studio工程。生成的示例工程目录如下所示:
至此,React Native配置完成。
Android Studio工程概览
使用Android Studio打开AwesomeProject/android/app
,Gradle会去下载一系列依赖的函数包,这个过程视网速而定,可能会比较长时间。通过阅读源码我们可以发现,这些依赖的函数包主要有:
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.fresco:fresco:0.6.1'
compile 'com.facebook.fresco:imagepipeline-okhttp:0.6.1'
compile 'com.fasterxml.jackson.core:jackson-core:2.2.3'
compile 'com.google.code.findbugs:jsr305:3.0.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.squareup.okhttp:okhttp-ws:2.4.0'
compile 'com.squareup.okio:okio:1.5.0'
compile 'org.webkit:android-jsc:r174650'
不过如果我们反编译AwesomeProject生成的apk,或者在Android Studio中查看AwesomeProject的External Libraries,会发现事实上最终打进apk包的函数包不止上面这些,可以看到External Libraries的截图如下:
最终生成的debug版本apk大小为7.2M,体积还是比较大的。
当然,打开app/build.gradle文件,可以看到该module只依赖react-native的一个jar包,其他依赖的函数包对于开发者来说是透明的:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.awesomeproject"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.facebook.react:react-native:0.11.+'
}
打开示例工程唯一的类MainActivity,可以发现已经针对React Native做了一层封装调用,默认帮我们维护了React Native的生命周期。
package com.awesomeproject;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceManager;
private ReactRootView mReactRootView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "AwesomeProject", null);
setContentView(mReactRootView);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this);
}
}
}
工程目录下的index.android.js是基于React写的js主模块代码:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
} = React;
var AwesomeProject = React.createClass({
render: function() {
return (
Welcome to React Native!
To get started, edit index.android.js
Shake or press menu button for dev menu
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
而package.json是工程的依赖和元数据配置文件:
{
"name": "AwesomeProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node_modules/react-native/packager/packager.sh"
},
"dependencies": {
"react-native": "^0.11.0"
}
}
最后说一句,想要学习React Native For Android,最好是具有Javascript和React的相关经验。
更新:很多人反馈说执行
react-native init
命令创建工程会卡死在这里,猜测可能是前面的环境配置出现问题导致,我使用该命令创建一个新的工程只需几分钟,执行后的日志记录如下,供参考:
Last login: Thu Sep 24 11:05:42 on ttys002
N/A: version "node" is not yet installed
guhaoxindeMacBook-Pro:~ guhaoxin$ cd ~/Desktop/
guhaoxindeMacBook-Pro:Desktop guhaoxin$ mkdir reactnative
guhaoxindeMacBook-Pro:Desktop guhaoxin$ cd reactnative/
guhaoxindeMacBook-Pro:reactnative guhaoxin$ react-native init asce1885
This will walk you through creating a new React Native project in /Users/guhaoxin/Desktop/reactnative/asce1885
> [email protected] install /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/ws/node_modules/bufferutil
> node-gyp rebuild
CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
SOLINK_MODULE(target) Release/bufferutil.node
> [email protected] install /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/ws/node_modules/utf-8-validate
> node-gyp rebuild
CXX(target) Release/obj.target/validation/src/validation.o
SOLINK_MODULE(target) Release/validation.node
> [email protected] postinstall /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/yeoman-generator/node_modules/cross-spawn/node_modules/spawn-sync
> node postinstall
> [email protected] install /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/babel/node_modules/chokidar/node_modules/fsevents
> node-pre-gyp install --fallback-to-build
SOLINK_MODULE(target) Release/.node
CXX(target) Release/obj.target/fse/fsevents.o
SOLINK_MODULE(target) Release/fse.node
COPY /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/babel/node_modules/chokidar/node_modules/fsevents/lib/binding/Release/node-v46-darwin-x64/fse.node
TOUCH Release/obj.target/action_after_build.stamp
[email protected] node_modules/react-native
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] ([email protected])
├── [email protected]
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected]
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
└── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
Setting up new React Native app in /Users/guhaoxin/Desktop/reactnative/asce1885
create .flowconfig
create .gitignore
create .watchmanconfig
create index.ios.js
create index.android.js
create ios/main.jsbundle
create ios/asce1885/AppDelegate.h
create ios/asce1885/AppDelegate.m
create ios/asce1885/Base.lproj/LaunchScreen.xib
create ios/asce1885/Images.xcassets/AppIcon.appiconset/Contents.json
create ios/asce1885/Info.plist
create ios/asce1885/main.m
create ios/asce1885Tests/asce1885Tests.m
create ios/asce1885Tests/Info.plist
create ios/asce1885.xcodeproj/project.pbxproj
create ios/asce1885.xcodeproj/xcshareddata/xcschemes/asce1885.xcscheme
create android/app/build.gradle
create android/app/proguard-rules.pro
create android/app/src/main/AndroidManifest.xml
create android/app/src/main/res/values/strings.xml
create android/app/src/main/res/values/styles.xml
create android/build.gradle
create android/gradle.properties
create android/settings.gradle
create android/app/src/main/res/mipmap-hdpi/ic_launcher.png
create android/app/src/main/res/mipmap-mdpi/ic_launcher.png
create android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
create android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
create android/gradle/wrapper/gradle-wrapper.jar
create android/gradle/wrapper/gradle-wrapper.properties
create android/gradlew
create android/gradlew.bat
create android/app/src/main/java/com/asce1885/MainActivity.java
**To run your app on iOS:**
Open /Users/guhaoxin/Desktop/reactnative/asce1885/ios/asce1885.xcodeproj in Xcode
Hit Run button
**To run your app on Android:**
Have an Android emulator running, or a device connected
cd /Users/guhaoxin/Desktop/reactnative/asce1885
react-native run-android
guhaoxindeMacBook-Pro:reactnative guhaoxin$ cd asce1885/
guhaoxindeMacBook-Pro:asce1885 guhaoxin$ ls
android index.android.js index.ios.js ios node_modules package.json
guhaoxindeMacBook-Pro:asce1885 guhaoxin$
欢迎关注我的微信公众号