天天看點

React Native For Android初體驗

​​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      

不過可能由于網絡或者服務不穩定,實際上使用這個指令安裝可能會失敗,就算成功也會花費較長的時間,是以建議到​​Node.js官網​​去直接下載下傳pkg包:

React Native For Android初體驗

​​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 For Android初體驗

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 For Android初體驗

至此,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的截圖如下:

React Native For Android初體驗

最終生成的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>      

而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​​的相關經驗。

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