React Native 專案整合 CodePush 全然指南
作者 | 錢凱
杏仁移動開發project師,前嵌入式project師。關注大前端技術新潮流。
本文使用的環境:
React [email protected]
Android [email protected]
Android Build [email protected]
Android Gradle [email protected]
Why CodePush?
CodePush 是微軟提供的一個熱更新前後臺方案。它對 React Native 專案有非常好的支援。
眼下針對 React Native 的 hot update 方案有很多。可是 CodePush 是最成熟穩定的方案,它最大的特點是提供了完整的後臺工具。它基本的長處是:
微軟出品,大廠保證
良好的多環境支援(Testing。Staging, Production)
灰度公佈、自己主動回滾等等特性
良好的資料統計支援:下載、安裝、出錯一目瞭然
強大的 CLI 工具,一個終端搞定所有流程
因為 React Native 執行的是指令碼 js 檔案,對熱更新有天然的親和,有餘力的團隊能夠嘗試實現自己的框架,一個簡單的實現思路是:
改動載入
jsBundle
的程式碼,轉而從指定的本地儲存位置去載入。假設沒有。下載 bundle, 而且本次開啟使用 app 包中的 bundle。假設找到
jsBundle
檔案。呼叫 api 比較版本號號,假設不一致,則從指定server下載最新的 bundle 進行替換。通過反射呼叫私有方法。在下載完畢的回撥中更新執行時資源。從而能馬上看到更新的效果。
使用相似
google-diff-match-patch
的 diff 工具。生成差異化補丁。不必下載完整 bundle,從而大大減小補丁包體積。
網上有非常多資料和原始碼。這裡就不細述了。
後臺配置
為了使用 Code Push 公佈熱更新,我們須要向微軟服務註冊我們的應用。
這部分工作微軟提供了強大的命令列工具:CodePush CLI
。
安裝 cli 工具
npm 全域性安裝:
npm install -g code-push-cli
關聯賬號
使用命令
code-push register
註冊一個賬號,能夠直接使用 GitHub 賬號授權。完畢後將 token 複製回命令列中。
使用 whoami
檢視登入狀態:
code-push whoami
註冊應用
登入成功後,我們註冊一個app:
code-push app add 你的App名稱 android react-native
注意一定要為 Android 和 iOS 分別註冊,兩者的更新包內容會有差異。
查詢狀態
每一個 App 有不同的執行時環境。比方 Production
,Staging
等,我們也能夠配置自己的環境。檢視 App 的不同環境和部署狀況:
code-push deployment ls 註冊的app名稱
眼下我們還沒有公佈不論什麼更新。所以表中的狀態是空的。
到這裡就完畢了後端的基本配置。
App端配置
版本號相容
安裝 Code Push 環境前首先要 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
來檢視包相關資訊。
我們建議始終將RN、React以及一些相關庫升級到最新版本號。在根資料夾下使用命令:
npm install --save react-native-code-push
來安裝最新版本號的 CodePush。
也能夠參照上面的相容性表格。安裝指定版本號:
npm install --save [email protected]
project配置(Android)
假設project建立的時候比較早。可能是使用命令create-react-native-app
來建立的,則須要在根資料夾執行:
npm run eject
來改變project結構。防止後面的相容性問題。
配置安卓project,官方提供了兩種途徑:
使用命令列工具
rnpm
(如今已經被整合到React Native CLI工具中了)。執行
react-native link react-native-code-push
手動配置
假設你是新手,或者對 gradle、安卓project結構不瞭解,我們強烈建議執行一次手動配置,幫助理解究竟發生了什麼。
手動配置
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 應該被加入到編譯過程。對於單個 module 的專案能夠不用須要這個檔案,可是對於 multiModule 的專案我們就須要這個檔案。否則 gradle 不知道要載入哪些專案。
這個檔案的程式碼在初始化階段就會被執行。
我們加入的內容告訴 gradle:去 node_modules
資料夾下的 react-native-code-push
載入 CodePush 子專案。
step 2
在 android/app/build.gradle
中的 dependencies
方法中加入依賴:
...
dependencies {
...
compile project(':react-native-code-push')
}
這樣就能在主project中引用到 CodePush 模組了。
step 3
繼續在 android/app/build.gradle
中,加入在編譯打包階段 CodePush 須要執行的 task 引用:
...
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
...
這段程式碼事實上就是呼叫了 project 物件的 apply 方法,傳入了一個以 from 為 key 的 map。完整寫出來就是這種:
project.apply([from: '../../node_modules/react-native-code-push/android/codepush.gradle'])
apply from
和 apply plugin
的差別在於,前者是從指定 url 去載入指令碼檔案。後者則用是從倉庫拉取 plugin id 相應的二進位制執行包。
step 4
CodePush 公佈有各種環境(deployment)。預設有 Staging 和 Production,我們須要在 buildType
中配置相應的環境。而且設定 PushKey
,從而讓 App 端的 CodePush RunTime 依據不同的健值來下載正確的更新包。
查詢各個環境 Key 的方法是使用上文安裝的 CLI 工具:
code-push deployment ls App名稱 -k
上表中的 Deployment Key
就是相應環境的 Key 值了。
在 android/app/build.gradle
中。配置 buildTypes:
buildTypes {
// 相應Production環境
release {
...
buildConfigField "String", "CODEPUSH_KEY", '"從上述結果中複製的production值"'
...
}
// 相應Staging環境
releaseStaging {
// 從 release 拷貝配置,僅僅改動了 pushKey
initWith release
buildConfigField "String", "CODEPUSH_KEY", '"從上述結果中複製的stagingkey值"'
}
debug {
buildConfigField "String", "CODEPUSH_KEY", '""'
}
}
注意這裡不同 buildType 的命名,Staging 環境相應的 buildType 就叫releaseStaging
,要符合這種命名規範。
Debug 環境儘管用不到 CodePush, 可是也要配置空的 Key 值,否則會報錯。
step 5
處理完引用關係後,我們改動 MainApplication.java
,在 App 執行時啟動 CodePush 服務:
// 宣告包
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(),
// 建立一個CodePush執行時例項
new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG)
...
);
}
};
}
js端引入 Code Push
配置完專案project後,我們將 CodePush 引入到 js 端。
首先將 App 的根元件包裹在 CodePush 中:
import codePush from "react-native-code-push";
AppRegistry.registerComponent('BDCRM', () => codePush(App));
CodePush 會在 App 啟動後自己主動去 check 和更新最新的版本號。我們能夠加入一些配置,讓它在進入後臺的時候也執行檢查:
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 公佈更新
完畢前後端的配置,打包公佈應用後,興許的改動我們就能通過 CLI 工具來公佈啦!
升級前首先要 check:
應用的版本號號要有更新(app/build.gradle: defaultConfig/versionName)
js bundle 要有改動,Code Push 會 diff 前後版本號。假設程式碼一致會覺得是無效的更新包
開啟終端,進入到project資料夾,完整公佈命令是:
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>]
命令引數非常多,但用途都一目瞭然。嫌每次打麻煩的話,做成指令碼也能夠。
一般來說,我們公佈應用首先會在測試環境進行穩定性測試。通過後再公佈到生產環境中:
打包公佈 Staging 環境
code-push release-react 應用名 --platform android --deploymentName Staging --description "修復一些bug"
這樣。我們 Staging 環境就能夠收到更新推送啦。具體載入新 bundle 的實際。和我們在應用中配置的策略有關。上文已經介紹過了。
測試 ok 後,提升(Promoting)到 Production 環境,而且進行灰度20%公佈
code-push promote 應用名 Staging Production --rollout 20%
在生產環境驗證 ok,使用 patch 將灰度改動為100%,進行全網公佈:
code-push patch 應用名 Production -rollout 100%
以上就是依照 測試 - 灰度 - 所有公佈 步驟的一個典型 CodePush 公佈工作流。
整體來說,CodePush 能滿足我們灰度公佈 React Native 應用的大部分需求了,由微軟提供的server端支援能夠節省非常多工作。是一個成熟可靠的方案。假設要說缺點,可能有幾個須要考慮一下:
server速度。國內網路狀況可能會影響下發的成功率和效率。
汙染程式碼,在 js 端必須將根節點包裹到 CodePush 模組中去,汙染了程式碼。
冗餘,假設僅僅是想要簡單的下發小體積的 js bundle。CodePush 顯得太“重”,過於冗餘了。這時候用輕量化的方案更好。
總之,我們依據自己專案的須要去進行選型就好了!
很多其它細節,能夠參考文件
全文完
下面文章您可能也會感興趣:
我們正在招聘 Java project師,歡迎有興趣的同學投遞簡歷到 [email protected] 。
杏仁技術站
長按左側二維碼關注我們。這裡有一群熱血青年期待著與您相會。