React Native For Android初体验

作者: asce1885 | 来源:发表于2015-09-21 21:02 被阅读21482次

    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, esprima-fb@15001.1.0-dev-harmony-fb, 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, text-table@0.2.0, 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, lodash@3.10.1, 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, path-exists@1.0.0, path-is-absolute@1.0.0, 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, source-map@0.4.4, 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, lodash@3.10.1, 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, path-is-absolute@1.0.0, path-exists@1.0.0, fs-readdir-recursive@0.1.2, convert-source-map@1.1.1, commander@2.8.1, source-map@0.4.4, output-file-sync@1.1.1, glob@5.0.14, lodash@3.10.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$ 
    

    欢迎关注我的微信公众号

    相关文章

      网友评论

      • 黑白咖:跳过
      • 程小曦:你好,我生成的IOS文件里面 有两个文件名包含 - tvOS,我快奔溃了,请问是什么原因?
        程小曦:而且直接编译ios项目无法编译
      • wangxxi:编译出来的库,RN本身的库有多大?
        asce1885:@wangxxi demo apk7M多
      • weishu:解决 初始化工程时间过长或者失败的问题:http://www.tianweishu.com/2015/10/08/problems-when-config-react-native-env/
        asce1885:@维术 赞
      • c87c81859066:npm 2.14.4
        nvm 0.26.1
        博主也是这个吗?
        asce1885:@huige npm 2.14.2
        nvm 0.26.1
      • c87c81859066:sudo react-native init AwesomeProject 执行了很久都没结束,我已经使用了翻墙工具还是很慢!博主遇到过这样的问题吗?
        asce1885:@维术 那有可能是你前面的环境配置有问题,我刚刚又使用这条命令创建一个新工程,几分钟的事儿
        asce1885:@huige 那有可能是你前面的环境配置有问题,我刚刚又使用这条命令创建一个新工程,几分钟的事儿
        weishu:@huige +1
      • ac8d5fa8c065:其他依赖的函数包对于开发者来说是`通明`的
        asce1885:@leel 哈哈,已改正,谢谢!
      • 太热了:问一下"目前React Native只支持在OS X系统上面进行开发",这句话是哪里能看到的
        太热了:@asce1885 谢谢,我再试试吧。我还以为这句话说得是ios环境。
        asce1885:@太热了 https://facebook.github.io/react-native/docs/getting-started.html:Only OS X is currently supported
      • 猴面包树WZJ:需不需要android开发经验?
        罗永慧:@614f3cb1a588 我也遇到了这种情况,不知仁兄最终有没有解决,是什么原因导致的?
        614f3cb1a588:我的用as打开以后,点击运行以后,在手机上怎么什么都不显示,就是个白屏?那位可以解答一下,谢谢!
        asce1885:@猴面包树WZJ 需要,但要求不高,更多的是要求有JS和React的经验

      本文标题:React Native For Android初体验

      本文链接:https://www.haomeiwen.com/subject/dzybcttx.html