React Native項目整合Microsoft CodePush完整指南(Android)

本文使用的環(huán)境:

  • React@16.3.1
  • React Native@0.55.4
  • react-native-code-push@5.3.4
  • Android SDK@23
  • Android Build Tool@23.0.3
  • Gradle@2.14.1
  • Android Gradle Plugin@2.2.3

Why CodePush

目前針對 React Native 的 hot update 方案有許多狱掂,但是 CodePush 是最成熟穩(wěn)定的方案芥颈,它最大的特點是提供了完整的后臺工具。它主要的優(yōu)點是:

  • 微軟出品,大廠保證
  • 良好的多環(huán)境支持(Testing匿值,Staging, Production)
  • 灰度發(fā)布新蟆、自動回滾等等特性
  • 良好的數(shù)據(jù)統(tǒng)計支持:下載训柴、安裝缸濒、出錯一目了然
  • 強大的 CLI 工具,一個終端搞定全部流程

由于 React Native 執(zhí)行的是腳本js文件植榕,對熱更新有天然的親和再沧,有余力的團隊可以嘗試實現(xiàn)自己的框架,一個簡單的實現(xiàn)思路是:

  • 修改加載 jsBundle 的代碼尊残,轉(zhuǎn)而從指定的本地存儲位置去加載炒瘸。如果沒有,下載 bundle, 并且本次打開使用 app 包中的 bundle寝衫。
  • 如果找到 jsBundle 文件顷扩,調(diào)用 api 比較版本號,如果不一致慰毅,則從指定服務(wù)器下載最新的 bundle 進行替換隘截。
  • 通過反射調(diào)用私有方法,在下載完成的回調(diào)中更新運行時資源汹胃,從而能立即看到更新的效果婶芭。
  • 使用類似 google-diff-match-patch 的 diff 工具,生成差異化補丁着饥,不必下載完整 bundle犀农,從而大大減小補丁包體積。

網(wǎng)上有很多資料和源碼宰掉,這里就不細述了井赌。

后臺配置

為了使用 Code Push 發(fā)布熱更新谤逼,我們需要向微軟服務(wù)注冊我們的應(yīng)用。這部分工作微軟提供了強大的命令行工具:CodePush CLI仇穗。

CodePush CLI

安裝 cli 工具

npm 全局安裝:

npm install -g code-push-cli

關(guān)聯(lián)賬號

使用命令

code-push register

注冊一個賬號,可以直接使用 GitHub 賬號授權(quán)戚绕,完成后將 token 復(fù)制回命令行中纹坐。

授權(quán)返回的token

使用 whoami 查看登錄狀態(tài):

code-push whoami

注冊應(yīng)用

登錄成功后,我們注冊一個app:

code-push app add 你的App名稱 android react-native

注意一定要為 Android 和 iOS 分別注冊舞丛,兩者的更新包內(nèi)容會有差異耘子。

注冊成功

查詢狀態(tài)

每個 App 有不同的運行時環(huán)境,比如 Production,Staging等球切,我們也可以配置自己的環(huán)境谷誓。查看 App 的不同環(huán)境和部署狀況:

code-push deployment ls 注冊的app名稱
查詢狀態(tài)

目前我們還沒有發(fā)布任何更新,所以表中的狀態(tài)是空的吨凑。

到這里就完成了后端的基本配置捍歪。

App端配置

版本兼容

安裝 Code Push 環(huán)境前首先要 check 版本的兼容性問題,不同的RN版本需要使用不同的 Code Push鸵钝,原則上我們建議將 RN 和 CodePush 都升級到最新版本糙臼。

下表是官方文檔中的兼容性說明:

React Native version(s) Supporting CodePush version(s)
<0.14 Unsupported
v0.14 v1.3 (introduced Android support)
v0.15-v0.18 v1.4-v1.6 (introduced iOS asset support)
v0.19-v0.28 v1.7-v1.17 (introduced Android asset support)
v0.29-v0.30 v1.13-v1.17 (RN refactored native hosting code)
v0.31-v0.33 v1.14.6-v1.17 (RN refactored native hosting code)
v0.34-v0.35 v1.15-v1.17 (RN refactored native hosting code)
v0.36-v0.39 v1.16-v1.17 (RN refactored resume handler)
v0.40-v0.42 v1.17 (RN refactored iOS header files)
v0.43-v0.44 v2.0+ (RN refactored uimanager dependencies)
v0.45 v3.0+ (RN refactored instance manager code)
v0.46 v4.0+ (RN refactored js bundle loader code)
v0.46-v0.53 v5.1+ (RN removed unused registration of JS modules)
v0.54-v0.55 v5.3+ (Android Gradle Plugin 3.x integration)

安裝包

使用命令:

npm info react-native-code-push

來查看包相關(guān)信息。

作為一家有追求的公司恩商,建議始終將RN变逃、React以及一些相關(guān)庫升級到最新版本。在根目錄下使用命令:

npm install --save react-native-code-push

來安裝最新版本的 CodePush怠堪。

也可以參照上面的兼容性表格揽乱,安裝指定版本:

npm install --save react-native-code-push@5.1.4

工程配置(Android)

如果工程創(chuàng)建的時候比較早,可能是使用命令create-react-native-app來創(chuàng)建的粟矿,則需要在根目錄執(zhí)行:

npm run eject

來改變工程結(jié)構(gòu)凰棉,防止后面的兼容性問題。

配置安卓工程嚷炉,官方提供了兩種途徑:

  • 使用命令行工具rnpm(現(xiàn)在已經(jīng)被整合到React Native CLI工具中了)渊啰。執(zhí)行
react-native link react-native-code-push
  • 手動配置

如果你是新手,或者對gradle申屹、安卓工程結(jié)構(gòu)不了解绘证,我們強烈建議執(zhí)行一次手動配置,幫助理解到底發(fā)生了什么哗讥。

手動配置

step 1

android/settings.gradle文件中添加:

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

這個文件定義了哪些 module 應(yīng)該被加入到編譯過程嚷那,對于單個 module 的項目可以不用需要這個文件,但是對于 multiModule 的項目我們就需要這個文件杆煞,否則 gradle 不知道要加載哪些項目魏宽。這個文件的代碼在初始化階段就會被執(zhí)行腐泻。

我們添加的內(nèi)容告訴 gradle:去 node_modules 目錄下的 react-native-code-push 加載 CodePush 子項目。

step 2

android/app/build.gradle 中的 dependencies 方法中添加依賴:

...
dependencies {
    ...
    compile project(':react-native-code-push')
}

這樣就能在主工程中引用到 CodePush 模塊了队询。

step 3

繼續(xù)在 android/app/build.gradle 中派桩,添加在編譯打包階段 CodePush 需要執(zhí)行的 task 引用:

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

這段代碼其實就是調(diào)用了 project 對象的 apply 方法,傳入了一個以 from 為 key 的 map蚌斩。完整寫出來就是這樣的:

project.apply([from: '../../node_modules/react-native-code-push/android/codepush.gradle'])

apply fromapply plugin的區(qū)別在于铆惑,前者是從指定 url 去加載腳本文件,后者則用是從倉庫拉取 plugin id 對應(yīng)的二進制執(zhí)行包送膳。

step 4

CodePush 發(fā)布有各種環(huán)境(deployment)员魏,默認有 Staging 和 Production,我們需要在 buildType 中配置對應(yīng)的環(huán)境叠聋,并且設(shè)置 PushKey撕阎,從而讓 App 端的 CodePush RunTime 根據(jù)不同的健值來下載正確的更新包。

查詢各個環(huán)境 Key 的方法是使用上文安裝的 CLI 工具:

code-push deployment ls App名稱 -k
查詢CodePushKey

上表中的 Deployment Key 就是對應(yīng)環(huán)境的 Key 值了碌补。

android/app/build.gradle 中虏束,配置 buildTypes:

buildTypes {

    // 對應(yīng)Production環(huán)境
    release {
        ...
        buildConfigField "String", "CODEPUSH_KEY", '"從上述結(jié)果中復(fù)制的production值"'
        ...
    }

    // 對應(yīng)Staging環(huán)境
    releaseStaging {
        // 從 release 拷貝配置,只修改了 pushKey
        initWith release
        buildConfigField "String", "CODEPUSH_KEY", '"從上述結(jié)果中復(fù)制的stagingkey值"'
    }

    debug {
        buildConfigField "String", "CODEPUSH_KEY", '""'
    }
}

注意這里不同 buildType 的命名脑慧,Staging 環(huán)境對應(yīng)的 buildType 就叫 releaseStaging魄眉,要符合這樣的命名規(guī)范。

Debug 環(huán)境雖然用不到 CodePush闷袒, 但是也要配置空的 Key 值坑律,否則會報錯。

step 5

處理完引用關(guān)系后囊骤,我們修改 MainApplication.java晃择,在 App 執(zhí)行時啟動 CodePush 服務(wù):

// 聲明包
import com.microsoft.codepush.react.CodePush;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...
        // 重寫 getJSBundleFile() 方法,讓 CodePush 去獲取正確的 jsBundle
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                // 創(chuàng)建一個CodePush運行時實例
                new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
                ...
            );
        }
    };
}

js端引入 Code Push

配置完項目工程后也物,我們將 CodePush 引入到 js 端宫屠。

首先將 App 的根組件包裹在 CodePush 中:

import codePush from "react-native-code-push";

AppRegistry.registerComponent('BDCRM', () => codePush(App));

CodePush 會在 App 啟動后自動去 check 和更新最新的版本,我們可以添加一些配置滑蚯,讓它在進入后臺的時候也執(zhí)行檢查:

let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL };
AppRegistry.registerComponent('BDCRM', () => codePush(codePushOptions)(App));

CodePush js端的 api 不多浪蹂,我們可以用這些 api 控制更新的一系列流程,常用的有:

// 檢測是否有更新包可用
codePush.checkForUpdate(deploymentKey: String = null, handleBinaryVersionMismatchCallback: (update: RemotePackage) => void): Promise<RemotePackage>;

// 獲取本地最新更新包的屬性
codePush.getCurrentPackage(): Promise<LocalPackage>;

// 重啟app(即使不用在 Hot Updating告材,也挺有用的)
codePush.restartApp(onlyIfUpdateIsPending: Boolean = false): void;

// 手動進一次更新
codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress), handleBinaryVersionMismatchCallback: function(update: RemotePackage)): Promise<Number>;

更多詳細信息見文檔坤次。

使用 CodePush CLI 發(fā)布更新

完成前后端的配置,打包發(fā)布應(yīng)用后斥赋,后續(xù)的改動我們就能通過 CLI 工具來發(fā)布啦缰猴!

升級前首先要check:

  • 應(yīng)用的版本號要有更新(app/build.gradle: defaultConfig/versionName)
  • js bundle要有改動,Code Push 會 diff 前后版本疤剑,如果代碼一致會認為是無效的更新包

打開終端滑绒,進入到工程目錄闷堡,完整發(fā)布命令是:

code-push release-react <appName> <platform>
[--bundleName <bundleName>]
[--deploymentName <deploymentName>]
[--description <description>]
[--development <development>]
[--disabled <disabled>]
[--entryFile <entryFile>]
[--gradleFile <gradleFile>]
[--mandatory]
[--noDuplicateReleaseError]
[--outputDir <outputDir>]
[--plistFile <plistFile>]
[--plistFilePrefix <plistFilePrefix>]
[--sourcemapOutput <sourcemapOutput>]
[--targetBinaryVersion <targetBinaryVersion>]
[--rollout <rolloutPercentage>]
[--privateKeyPath <pathToPrivateKey>]
[--config <config>]

一搬來說,我們發(fā)布應(yīng)用首先會在測試環(huán)境進行穩(wěn)定性測試疑故,通過后再發(fā)布到生產(chǎn)環(huán)境中:

  • 打包發(fā)布 Staging 環(huán)境
code-push release-react 應(yīng)用名 --platform android --deploymentName Staging --description "修復(fù)一些bug"
  • 測試 ok 后杠览,提升(Promoting)到 Production 環(huán)境,并且進行灰度20%發(fā)布
code-push promote 應(yīng)用名 Staging Production --rollout 20%
  • 在生產(chǎn)環(huán)境驗證 ok焰扳,使用 patch 將灰度修改為100%倦零,進行全網(wǎng)發(fā)布:
code-push patch 應(yīng)用名 Production -rollout 100%

以上就是一個典型的 CodePush 發(fā)布工作流。

更多細節(jié)吨悍,可以參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蹋嵌,隨后出現(xiàn)的幾起案子育瓜,更是在濱河造成了極大的恐慌,老刑警劉巖栽烂,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躏仇,死亡現(xiàn)場離奇詭異,居然都是意外死亡腺办,警方通過查閱死者的電腦和手機焰手,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怀喉,“玉大人书妻,你說我怎么就攤上這事」#” “怎么了躲履?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長聊闯。 經(jīng)常有香客問我工猜,道長,這世上最難降的妖魔是什么菱蔬? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任篷帅,我火速辦了婚禮,結(jié)果婚禮上拴泌,老公的妹妹穿的比我還像新娘魏身。我一直安慰自己,他們只是感情好弛针,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布叠骑。 她就那樣靜靜地躺著,像睡著了一般削茁。 火紅的嫁衣襯著肌膚如雪宙枷。 梳的紋絲不亂的頭發(fā)上掉房,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音慰丛,去河邊找鬼卓囚。 笑死,一個胖子當著我的面吹牛诅病,可吹牛的內(nèi)容都是我干的哪亿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼贤笆,長吁一口氣:“原來是場噩夢啊……” “哼蝇棉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芥永,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤篡殷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后埋涧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體板辽,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年棘催,在試婚紗的時候發(fā)現(xiàn)自己被綠了劲弦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡醇坝,死狀恐怖邑跪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纲仍,我是刑警寧澤呀袱,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站郑叠,受9級特大地震影響夜赵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乡革,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一寇僧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沸版,春花似錦嘁傀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春笑撞,著一層夾襖步出監(jiān)牢的瞬間岛啸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工茴肥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坚踩,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓瓤狐,卻偏偏與公主長得像瞬铸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子础锐,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容