1. 程式人生 > >ReactNative基於CodePush實現熱更新整合詳解

ReactNative基於CodePush實現熱更新整合詳解

安裝工具介紹

根據最新的官方文件和實際整合經驗整理 http://www.jianshu.com/p/54cd13ed9e95

工具名稱 描述 備註
Chocolatey Windows上的包管理器(需翻牆 通常不安裝,使用 npm 即可
Python 2 注意目前不支援Python 3版本 不需安裝
NodeJs Javascript執行環境(runtime) 自帶 npm
Yarn Facebook提供的替代npm的工具,可以加速node模組的下載 通常不安裝使用,國內可通過設定設定npm映象達到同樣效果
NPM 包管理工具,主要用於解決NodeJS程式碼部署問題 NodeJS中已安裝
react-native-cli React Native的命令列工具用於執行建立、初始化、更新專案、執行打包服務(packager)等任務 直接使用npm即可安裝

Android 開發環境要求

  • Android Studio2.0或更高版本
  • [JDK] 1.8或更高版本
  • 必須安裝Android Support Repository

環境變數相關

ANDROID_HOME制定到本地 \sdk 對應目錄下
至少需配置:%Android_Home%\tools;%Android_Home%\platform-tools;


當前線上專案相關版本資訊


環境搭建如上圖,如有問題及時聯絡
下面是CodePush的Android客戶端整合部分(熱更新暫時使用微軟的遠端服務)

開啟workspace目錄初始化專案

$ react-native init RnApp


在對應的工作空間生成React Native工程目錄


根目錄下的 package.json 包管理配置檔案

注意:使用命令列初始化的RN工程預設使用官網最新的組建版本,可能存在和某些三方組建的相容性問題(如微軟的CodePush);
如果希望指定RN組建版本初始化,可使用:$ react-native init RnApp --version 0.39.2

{
    "name": "RnApp",//工程名稱
    "version": "0.0.1",//工程版本號
    "private"
: true,//讀寫許可權 "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start",//React Native啟動入口 "test": "jest" }, "dependencies": { "react": "15.4.2",//依賴的react版本 "react-native": "0.42.3"//依賴的react-native版本 }, "devDependencies": {//開發除錯依賴庫 "babel-jest": "19.0.0", "babel-preset-react-native": "1.9.1", "jest": "19.0.2", "react-test-renderer": "15.4.2" }, "jest": { "preset": "react-native" } }

在初始化的RN工程的根目錄下根據提示啟動並執行Android專案


會自動啟動一個本地開發伺服器:如圖代表開發伺服器執行正常


使用 React Native 命令列工具啟動專案異常處理

隨著 React Native 版本不斷升級,Android Studio 的版本變更,包括當前系統環境的差異性,導致在執行RN工程時會存在各種奇奇怪怪的問題;
此時需要將RN工程匯入到當前系統環境的 Android studio 中直接執行,可以避免由於各個工具直接版本的差異導致執行失敗的問題。


使用 Android Studio 開啟初始化的 RN 工程


注意需要在 AS 中選擇RN根目錄結構下的 Android 開啟


如圖表示 React Native 工程在 IDE 中編譯成功


RN初始化工程 App 下的 build.gradle 檔案
可以看到RN的最小SDK構建版本是 16 對應的Android系統版本是 V_4.1.2

apply plugin: "com.android.application"

import com.android.build.OutputFile

apply from: "../../node_modules/react-native/react.gradle"

def enableSeparateBuildPerCPUArchitecture = false

def enableProguardInReleaseBuilds = false

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.rnapp"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
        ndk {
            abiFilters "armeabi-v7a", "x86"
        }
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }
    // applicationVariants are e.g. debug, release
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
            def versionCodes = ["armeabi-v7a":1, "x86":2]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null) {  // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
            }
        }
    }
}

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    compile "com.facebook.react:react-native:+"  // From node_modules
}

// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
    from configurations.compile
    into 'libs'
}

如果將最小構建版本修改至低於 sdk16,則會導致工程構建失敗,如圖所示:
根據錯誤資訊:如果需要使用 React Native 元件,則需要最低的sdk構建版本不低於16,
如果需要向下相容,則需要使用工具重寫“com.facebook.react”原始碼。


在RN專案根目錄下開啟除錯伺服器

$ npm start


使用AS直接執行RnApp,安裝成功後部分手機需要使用者授權懸浮窗許可權


授權成功後重新開啟專案


如果出現不能獲取除錯橋的錯誤提示,請手動設定除錯埠號為:8081 後點擊 RELOAD(R,R)

設定除錯伺服器埠方式一:adb reverse tcp:8081 tcp:8081


等待 JsBundle 檔案解析完成即可渲染出 RN 頁面


設定除錯伺服器埠方式二:在開發模式下搖晃手機調用出開服者選項設定項


設定除錯伺服器主機名和埠號:


按照如下格式輸入主機名和埠號:

注意:所輸入的主機名一定是當前執行開發伺服器計算機的IP地址,並且除錯機和開發伺服器需要要在同一個區域網環境下;
方式二優點:如果設定成功,在除錯過程中就不需使用USB資料線和計算機連線了。


至此,一個RN工程的 hello world 工程就完成了...

備註:初始化後的RN工程啟動檔案和啟動頁原始碼

程式啟動入口: Application.java 檔案:

package com.rnapp;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

RN頁面入口: ReactActivity.java 檔案子類:

package com.rnapp;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "RnApp";
    }
}

下面是 CodePush 整合詳解

CodePush 簡介

CodePush 是微軟提供的一套用於熱更新 React Native 和 Cordova 應用的服務。
CodePush 是提供給 React Native 和 Cordova 開發者直接部署移動應用更新給使用者裝置的雲服務。CodePush 作為一箇中央倉庫,開發者可以推送更新 (JS, HTML, CSS and images),應用可以從客戶端 SDK 裡面查詢更新。CodePush 可以讓應用有更多的可確定性,也可以讓你直接接觸使用者群。在修復一些小問題和新增新特性的時候,不需要經過二進位制打包,可以直接推送程式碼進行實時更新。

安裝 CodePush CLI

引數 -g 表示安裝位置為全域性,請不要安裝在當前專案目錄下


建立CodePush賬號

涉及到公司開發者賬號,此處省略截圖

  • 在終端輸入$ code-push register,會開啟如下注冊頁面讓你選擇授權賬號。
  • 授權通過之後,CodePush會告訴你 access key ,複製此key到終端即可完成註冊。
  • 然後終端輸入 $ code-push login 進行登陸,登陸成功後,你的session檔案將會寫在 /Users/你的使用者名稱/.code-push.config。

在CodePush伺服器註冊app

終端輸入:$ code-push app add RnApp完成註冊並獲取部署鍵值

Production 表示生產環境 唯一識別的部署鍵值:K1pkiaHC2-w-0M3iRaABluolNtRL4JHzHx0MG
Staging 表示開發環境 唯一識別的部署鍵值:_lvQXBDZ0m_2rFdBlnZ5pkd0fa-n4JHzHx0MG
建議:Android和IOS使用單獨的專案部署獲取兩套 Deployment Key


安裝 react-native-code-push外掛

切換到RN工程根目錄 終端輸入命令:$ npm install react-native-code-push --save

注意:--save 引數表示是否將當前依賴寫入 package.json 檔案


將會在 package.json 檔案中寫入依賴配置


注意:切換到RN工程根目錄 終端輸入命令:$ rnpm link react-native-code-push 將會自動幫我們進行code-push外掛在原生中的gradle配置,
但是不建議使用,我們可以手動的進行 CodePush 的 gradle 配置以達到同樣的效果。

這是引入RN官網的文件:
安裝完node後建議設定npm映象以加速後面的過程(或使用科學上網工具)。注意:不要使用cnpm!cnpm安裝的模組路徑比較奇怪,packager不能正常識別!
即:不建議使用類似 cnpm 活著 rnpm 進行相關模組的操作。

在原生專案中手動整合 CodePush

1. 在 android/app/build.gradle檔案裡面添如下程式碼:

 apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"

2. 在/android/settings.gradle中新增如下程式碼:

 include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')

3. 在 android/app/build.gradle檔案裡面添如下程式碼將CodePush新增專案編譯:

 compile project(':react-native-code-push')

4. 執行 $ code-push deployment ls RnApp -k 命令獲取 部署祕鑰.


5. 在 MainApplication.java 中新增如下程式碼:

 //注意需要將部署鍵值替換成自己的專案的唯一鍵值
 new CodePush("deployment-key-here", MainApplication.this, BuildConfig.DEBUG)

6. 在index.Android.js 中呼叫更新服務


相應程式碼可直接複製使用

componentDidMount(){
    codePushUpdate();
  }

  //遠端服務檢測更新
  codePushUpdate(){
    codePush.sync({
      installMode: codePush.InstallMode.IMMEDIATE,
      updateDialog: true
     },
     (status) => {
      switch (status) {
        case codePush.SyncStatus.CHECKING_FOR_UPDATE:
            console.log('codePush.SyncStatus.CHECKING_FOR_UPDATE');
          break;
        case codePush.SyncStatus.AWAITING_USER_ACTION:
          console.log('codePush.SyncStatus.AWAITING_USER_ACTION');
          break;
        case codePush.SyncStatus.DOWNLOADING_PACKAGE:
          console.log('codePush.SyncStatus.DOWNLOADING_PACKAGE');
          break; 
        case codePush.SyncStatus.INSTALLING_UPDATE:
          console.log('codePush.SyncStatus.INSTALLING_UPDATE');
          break;
        case codePush.SyncStatus.UP_TO_DATE:
          console.log('codePush.SyncStatus.UP_TO_DATE');
          break;
        case codePush.SyncStatus.UPDATE_IGNORED:
          console.log('codePush.SyncStatus.UPDATE_IGNORED');
          break;
        case codePush.SyncStatus.UPDATE_INSTALLED:
          console.log('codePush.SyncStatus.UPDATE_INSTALLED');
          break;
        case codePush.SyncStatus.SYNC_IN_PROGRESS:
          console.log('codePush.SyncStatus.SYNC_IN_PROGRESS');
          break;
        case codePush.SyncStatus.UNKNOWN_ERROR:
          console.log('codePush.SyncStatus.UNKNOWN_ERROR');
          break;
      }
     },
     ({ receivedBytes, totalBytes, }) => {
      console.log('receivedBytes / totalBytes: ------------    ' + receivedBytes+'/'+totalBytes);
      }
    );
  }

8. 在RN更目錄下新建兩個資料夾

注意:將專案的VersionName修改為三位數!


9. CodePush命令列打JsBundle包(存於本地)

$ react-native bundle --platform android --entry-file index.android.js --bundle-output ./bundles/index.android.bundle  --assets-dest ./bundles --dev false

10. 執行如下命令部署到微軟伺服器

$ code-push release PatientMRB ./bundles 1.0.0 --deploymentName Staging --description “RN熱更新測試“ --mandatory true

11. 執行專案日誌輸出如下


至此:一個簡單的RN整合CodePush實現熱更新已經完成...
後續期待和大家一起爬坑...

附錄:CodePush 常用命令總結

  • code-push login
  • code-push logout
  • code-push access-key ls 列出登陸的token
  • code—push access-key rm <accessKey> 刪除某個access-key
  • code-push app ls
  • code-push app add <appName>
  • code-push app rm <appName>
  • code-push app rename <appName>
  • code-push app transfer 轉移app所有權到另一個賬號
  • code-push deployment add <appName> 部署
  • code-push deployment rename <appName> 重新命名
  • code-push deployment ls <appName>
  • code-push deployment ls <appName> -k
  • code-push deployment history <appName> <deploymentName>(Production||Staging)
  • code-push rollback <appName> <deploymentName> “appName”中的測試||生產環境的部署執行回滾
  • code-push rollback <appName> <deploymentName> —targetRelease v6 回滾到指定的部署標籤

簡書處女座,如有書寫不妥的地方請留言更正,同時歡迎拍磚吐槽...
最後有一個願望:希望藉此愛上技術寫作...