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 (
      <View style={styles.container}> <Text style={styles.welcome}> Welcome to React Native! </Text> <Text style={styles.instructions}> To get started,edit index.android.js </Text> <Text style={styles.instructions}> Shake or press menu button for dev menu </Text> </View> ); } }); 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,最好是具有JavascriptReact的相关经验。

更新:很多人反馈说执行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

> bufferutil@1.2.1 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

> utf-8-validate@1.2.1 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

> spawn-sync@1.0.13 postinstall /Users/guhaoxin/Desktop/reactnative/asce1885/node_modules/react-native/node_modules/yeoman-generator/node_modules/cross-spawn/node_modules/spawn-sync
> node postinstall


> fsevents@1.0.0 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
react-native@0.11.0 node_modules/react-native
├── absolute-path@0.0.0
├── progress@1.1.8
├── graceful-fs@4.1.2
├── stacktrace-parser@0.1.3
├── react-timer-mixin@0.13.3
├── wordwrap@1.0.0
├── underscore@1.7.0
├── image-size@0.3.5
├── semver@4.3.6
├── yargs@1.3.2
├── debug@2.1.0 (ms@0.6.2)
├── immutable@3.7.5
├── bser@1.0.0 (node-int64@0.4.0)
├── optimist@0.6.1 (wordwrap@0.0.3,minimist@0.0.10)
├── source-map@0.1.31 (amdefine@1.0.0)
├── promise@7.0.4 (asap@2.0.3)
├── chalk@1.0.0 (escape-string-regexp@1.0.3,ansi-styles@2.1.0,supports-color@1.3.1,strip-ansi@2.0.1,has-ansi@1.0.3)
├── worker-farm@1.3.1 (xtend@4.0.0,errno@0.1.4)
├── rebound@0.0.12
├── sane@1.2.0 (watch@0.10.0,minimist@1.2.0,exec-sh@0.2.0,minimatch@0.2.14,walker@1.0.7,fb-watchman@1.6.0)
├── connect@2.8.3 (methods@0.0.1,uid2@0.0.2,fresh@0.1.0,cookie-signature@1.0.1,pause@0.0.1,bytes@0.2.0,buffer-crc32@0.2.1,qs@0.6.5,cookie@0.1.0,send@0.1.2,formidable@1.0.14)
├── uglify-js@2.4.16 (uglify-to-browserify@1.0.2,async@0.2.10,optimist@0.3.7,source-map@0.1.34)
├── ws@0.8.0 (options@0.0.6,ultron@1.0.2,bufferutil@1.2.1,utf-8-validate@1.2.1)
├── regenerator@0.8.36 (private@0.1.6,through@2.3.8,commoner@0.10.3,recast@0.10.25,esprima-fb@15001.1.0-dev-harmony-fb,defs@1.1.0)
├── jstransform@11.0.1 (object-assign@2.1.1,base62@1.1.0,source-map@0.4.4,commoner@0.10.3)
├── module-deps@3.5.6 (inherits@2.0.1,shallow-copy@0.0.1,duplexer2@0.0.2,minimist@0.2.0,concat-stream@1.4.10,parents@1.0.1,subarg@0.0.1,readable-stream@1.1.13,through2@0.4.2,resolve@0.7.4,stream-combiner2@1.0.2,browser-resolve@1.9.1,JSONStream@0.7.4,detective@3.1.0)
├── joi@5.1.0 (topo@1.0.3,isemail@1.2.0,hoek@2.16.3,moment@2.10.6)
├── yeoman-environment@1.2.7 (escape-string-regexp@1.0.3,log-symbols@1.0.2,diff@1.4.0,text-table@0.2.0,untildify@2.1.0,mem-fs@1.1.0,globby@2.1.0,grouped-queue@0.3.0,lodash@3.10.1,inquirer@0.8.5)
├── yeoman-generator@0.20.3 (read-chunk@1.0.1,detect-conflict@1.0.0,yeoman-welcome@1.0.1,path-is-absolute@1.0.0,path-exists@1.0.0,yeoman-assert@2.1.0,rimraf@2.4.3,async@1.4.2,xdg-basedir@2.0.0,user-home@2.0.0,mime@1.3.4,dargs@4.0.1,run-async@0.1.0,nopt@3.0.4,mkdirp@0.5.1,shelljs@0.5.3,istextorbinary@1.0.2,through2@2.0.0,diff@2.1.2,glob@5.0.14,underscore.string@3.2.2,findup-sync@0.2.1,pretty-bytes@2.0.1,cli-table@0.3.1,dateformat@1.0.11,mem-fs-editor@2.0.4,github-username@2.0.0,cross-spawn@2.0.0,class-extend@0.1.1,download@4.2.0,html-wiring@1.2.0,gruntfile-editor@1.0.0,sinon@1.17.0,inquirer@0.8.5)
├── react-tools@0.14.0-beta1 (commoner@0.10.3)
├── babel-core@5.8.21 (slash@1.0.0,try-resolve@1.0.1,babel-plugin-remove-debugger@1.0.1,babel-plugin-remove-console@1.0.1,babel-plugin-property-literals@1.0.1,babel-plugin-eval@1.0.1,babel-plugin-jscript@1.0.4,babel-plugin-inline-environment-variables@1.0.1,babel-plugin-undefined-to-void@1.1.6,babel-plugin-member-expression-literals@1.0.1,shebang-regex@1.0.0,to-fast-properties@1.0.1,trim-right@1.0.1,babel-plugin-react-constant-elements@1.0.3,babel-plugin-react-display-name@1.0.3,fs-readdir-recursive@0.1.2,babel-plugin-constant-folding@1.0.1,babel-plugin-proto-to-assign@1.0.4,babel-plugin-dead-code-elimination@1.0.2,babel-plugin-runtime@1.0.7,private@0.1.6,globals@6.4.1,esutils@2.0.2,convert-source-map@1.1.1,js-tokens@1.0.1,babylon@5.8.23,resolve@1.1.6,bluebird@2.10.1,json5@0.4.0,babel-plugin-undeclared-variables-check@1.0.2,output-file-sync@1.1.1,source-map-support@0.2.10,home-or-tmp@1.0.0,debug@2.2.0,is-integer@1.0.6,repeating@1.1.3,line-numbers@0.2.0,detect-indent@3.0.1,minimatch@2.0.10,core-js@1.1.4,regexpu@1.3.0,regenerator@0.8.35)
└── babel@5.8.21 (slash@1.0.0,commander@2.8.1,chokidar@1.1.0)
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$

文末摄影鉴赏

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom