React-Native 之 CodePush 熱更新

React-Native CodePush 之 配置Android

使用RN開發(fā)項(xiàng)目雖然已經(jīng)開發(fā)了很久了,但親自操手配置 CodePush 還是第一次,話不多說(shuō)開始今天的 CodePush 配置之旅;


簡(jiǎn)介
CodePush 是由 Microsoft 提供的一個(gè)用于實(shí)現(xiàn) React Native 應(yīng)用程序熱更新的解決方案。它允許你在不通過(guò)應(yīng)用商店發(fā)布新版本的情況下,將更新推送到現(xiàn)有的安裝應(yīng)用程序。以下是 CodePush 的一些關(guān)鍵概念和用法:

  1. 部署環(huán)境: 在 CodePush 中,你可以創(chuàng)建不同的部署環(huán)境,例如 "Staging" 和 "Production"。每個(gè)環(huán)境都有自己的部署密鑰和更新。這有助于分離測(cè)試和生產(chǎn)環(huán)境的更新。
  2. 部署密鑰: 每個(gè)部署環(huán)境都有一個(gè)唯一的部署密鑰,用于標(biāo)識(shí)該環(huán)境的更新。你需要在應(yīng)用程序中配置正確的部署密鑰,以獲取相應(yīng)環(huán)境的更新。
  3. 發(fā)布更新: 使用 code-push release-react 命令發(fā)布新的更新。該命令可以將 React Native 項(xiàng)目的 JavaScript bundle 和資源文件上傳到 CodePush 服務(wù)器。

開始實(shí)操

開發(fā)文檔傳送門。
1. 需要在項(xiàng)目的根目錄使用npm 下載依賴包

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

2. 需要更改android的原生代碼

1)android/settings.gradle在 文件中,進(jìn)行以下添加

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

2)在文件中 android/app/build.gradle ,將 codepush.gradle 文件添加為其他生成任務(wù)定義

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

3)MainApplication.java 更新文件,則通過(guò)以下更改使用 CodePush:

// 1. 導(dǎo)入插件
import com.microsoft.codepush.react.CodePush;

public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...
        // 2. 重寫 getJSBundleFile 方法以讓 CodePush運(yùn)行時(shí)確定從那里獲取JS
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }
    };
}

添加部署密鑰

先去后臺(tái)創(chuàng)建app 微軟管理平臺(tái)(備注:創(chuàng)建完成app之后,會(huì)自動(dòng)生成Staging和Production環(huán)境)
第一步 - 使用 appcenter-cli,進(jìn)行創(chuàng)app環(huán)境密鑰
安裝 App Center CLI:

npm install -g appcenter-cli

第二步 - 登入

// 回車之后,按照提示輸入yes,然后會(huì)自動(dòng)打開瀏覽給出密碼,只需要復(fù)制粘貼回終端再回車就OK了
appcenter login

第三步 - 獲取 owner名稱 和 app名稱

appcenter apps list

第四步 - 選擇你要的配置的app項(xiàng)目,進(jìn)行創(chuàng)建SIT和PROD環(huán)境的密鑰(一定要核對(duì)好 owner名稱 和 app名稱的單詞大小寫)

// appcenter codepush release-react -a jackteng666-gmail.com/MyApp -d Staging SIT環(huán)境(測(cè)試環(huán)境)
范例:
appcenter codepush release-react -a 123456-@gamil.com/MyApp -d Staging

// appcenter codepush release-react -a 123456-@gamil.com/MyApp -d Production PROD環(huán)境(生產(chǎn)環(huán)境)
范例:
appcenter codepush release-react -a 123456-@gamil.com/MyApp -d Production

第五步 - 獲取密鑰

appcenter codepush deployment list --app 123456-@gamil.com/MyApp -k

不出意外的話,到這一步已經(jīng)創(chuàng)建好兩個(gè)環(huán)境的密鑰并且返回兩個(gè)環(huán)境的密鑰,接下來(lái)就是在app中配置密鑰


photo_2023-11-16_23-49-05.jpg

密鑰的配置分兩種形式,a. 固定環(huán)境 b.多種環(huán)境,這些根據(jù)自己的業(yè)務(wù)需求選擇
a. 固定環(huán)境:
在 android/app/src/main/res/values/strings.xml 進(jìn)行添加

 <resources>
     <string name="app_name">AppName</string>
     <string moduleConfig="true" name="CodePushDeploymentKey">替換自己需要的環(huán)境密鑰</string>
 </resources>

b.多種環(huán)境
在 android/app/build.gradle 進(jìn)行添加

android {
    ...
    buildTypes {
        debug {
            ...
            resValue "string", "CodePushDeploymentKey", '""'
            ...
        }
        releaseStaging {
            ...
            resValue "string", "CodePushDeploymentKey", '"<INSERT_STAGING_KEY>"'
            matchingFallbacks = ['release']
            ...
        }
        release {
            ...
            resValue "string", "CodePushDeploymentKey", '"<INSERT_PRODUCTION_KEY>"'
            ...
        }
    }
    ...
}

到這一步app密鑰已經(jīng)配置好了,接下來(lái)就是寫js代碼進(jìn)行發(fā)布和測(cè)試熱更新
詳細(xì)介紹可以看這里API Reference

import CodePush from 'react-native-code-push';
...
export default CodePush(App);

如果不想在啟動(dòng)app的時(shí)候,檢查的話,也可以單獨(dú)寫成組建,在需要的地方進(jìn)行加載;

import React, { useState, useEffect } from "react";
import codePush from "react-native-code-push";

const CheckUpdate = () => {
 
   /** 執(zhí)行CodePush更新 */
   const processCodepushUpdate = () => {
       codePush.notifyAppReady(); // 加這行避免自動(dòng)rollback, https://docs.microsoft.com/zh-tw/appcenter/distribution/codepush/rn-api-ref#codepushnotifyappready
       codePush
           .sync(
               {
                   installMode: codePush.InstallMode.ON_NEXT_RESUME, // 非強(qiáng)制:下次喚起時(shí)安裝新版本
                   mandatoryInstallMode: codePush.InstallMode.ON_NEXT_RESUME, // 強(qiáng)制:下次喚起時(shí)安裝新版本
                   minimumBackgroundDuration: 0, // 背景停留多久后觸發(fā)按照新版本(預(yù)設(shè)0)
                   rollbackRetryOptions: { delayInHours: 1, maxRetryAttempts: 50 }, // rollback重試設(shè)定
               },
               (status) => {
                   // codePush.SyncStatus.CHECKING_FOR_UPDATE 0 : 正在查詢 CodePush 服務(wù)器是否有更新。
                   // codePush.SyncStatus.AWAITING_USER_ACTION 1 : 有更新可用,并向最終用戶顯示確認(rèn)對(duì)話框。(僅在updateDialog使用時(shí)適用)
                   // odePush.SyncStatus.DOWNLOADING_PACKAGE 2 : 在從 CodePush 服務(wù)器下載可用更新。
                   // codePush.SyncStatus.INSTALLING_UPDATE 3 : 已下載可用更新并將安裝
                   // codePush.SyncStatus.UP_TO_DATE 4 :應(yīng)用程序已完全更新到配置的部署
                   // codePush.SyncStatus.UPDATE_IGNORED 5 : 應(yīng)用程序有一個(gè)可選更新,最終用戶選擇忽略該更新。(僅在updateDialog使用時(shí)適用)
                   // codePush.SyncStatus.UPDATE_INSTALLED 6 : 已安裝可用更新,并將在syncStatusChangedCallback函數(shù)返回后立即運(yùn)行或在下次應(yīng)用程序恢復(fù)/重新啟動(dòng)時(shí)運(yùn)
                   // codePush.SyncStatus.SYNC_IN_PROGRESS 7: 正在進(jìn)行的sync操作阻止當(dāng)前調(diào)用的執(zhí)行。
                   // codePush.SyncStatus.UNKNOWN_ERROR -1 : 同步操作發(fā)現(xiàn)未知錯(cuò)誤。
                   switch (status) {
                       case codePush.SyncStatus.DOWNLOADING_PACKAGE:
                           console.log("在從 CodePush 服務(wù)器下載可用更新");
                           break;
                       case codePush.SyncStatus.INSTALLING_UPDATE:
                           console.log("有更新可用,已下載可用更新并將安裝");
                           break;
                       case codePush.SyncStatus.UPDATE_INSTALLED:
                           console.log("已安裝可用更新,并將在syncStatusChangedCallback函數(shù)返回后立即運(yùn)行或在下次應(yīng)用程序恢復(fù)/重新啟動(dòng)時(shí)運(yùn)");
                           codePush.restartApp();
                           break;
                   }
               },
               ({ receivedBytes, totalBytes }) => {
                   // 計(jì)算出下載百分比
                   const percent = Math.floor((receivedBytes / totalBytes) * 100);
               }
           )
           .catch((error) => {
                console.log(JSON.stringify(error))
           });
   };

   useEffect(() => {
        processCodepushUpdate();
   }, []);

   return ...
};

export default CheckUpdate;

到這一步熱更新框架基本上完成了,接下來(lái)就是測(cè)試熱更新

發(fā)布版本指令:

當(dāng)前發(fā)布的是SIT環(huán)境,PROD環(huán)境同理

appcenter codepush release-react -a 123456-@gamil.com/MyApp -t "*" -d Staging --description "v1.0.0 測(cè)試更新" --sourcemap-output --output-dir ./build/android

備注:
-t 是指目標(biāo)版本,意思是只有是指定的目標(biāo)版本才會(huì)觸發(fā)更新,比如說(shuō),現(xiàn)在你的用戶用的版本是1.1.1 和 1.0.0這兩種版本,那么 -t "1.1.1" 就是發(fā)布之后,只有版本為1.1.1的用戶才會(huì)觸發(fā)熱更新
如果不需要指定目標(biāo)版本就輸入 -t "*" 通配符為全部版本 或者不輸入 -t

當(dāng)終端返回以下內(nèi)容,則表示已經(jīng)發(fā)布成功了(SIT環(huán)境)

Successfully released an update containing the "build/android/CodePush" directory to the "Staging" deployment of the "MyApp" app.

查看發(fā)布?xì)v史

appcenter codepush deployment history -a 123456-@gamil.com/MyApp Staging
photo_2023-11-17_00-24-27.jpg

到這里最新版本的代碼已經(jīng)發(fā)布到codPush服務(wù)器上了,接下來(lái)就可以用已經(jīng)有熱更新代碼版本的app來(lái)測(cè)試了;

最簡(jiǎn)單的方式就是
1、把當(dāng)前版本打包給Android手機(jī)安裝,然后再改代碼(能夠明顯看到不一樣的界面,比較好分辨是否更新成功),然后再重新發(fā)布一次;
2、重新打開APP檢查是否有更新提示(上面的組建,可以根據(jù)自己的想法寫出彈窗和顯示進(jìn)度條)

我當(dāng)前遇到一個(gè)錯(cuò)誤狀態(tài)碼是 codePush.SyncStatus.UNKNOWN_ERROR -1 : 同步操作發(fā)現(xiàn)未知錯(cuò)誤。,這個(gè)錯(cuò)誤會(huì)有很多種情況,比較難查。我的app上導(dǎo)致這個(gè)狀態(tài)的原因是:

{
    "nativeStackAndroid":[{"lineNumber":49,"file":"CodePush.java","methodName":"h","class":"com.microsoft.codepush.react.a"},{"lineNumber":25,"file":"CodePushNativeModule.java","methodName":"a","class":"com.microsoft.codepush.react.CodePushNativeModule$c"},{"lineNumber":3,"file":"CodePushNativeModule.java","methodName":"doInBackground","class":"com.microsoft.codepush.react.CodePushNativeModule$c"},{"lineNumber":389,"file":"AsyncTask.java","methodName":"call","class":"android.os.AsyncTask$3"},{"lineNumber":266,"file":"FutureTask.java","methodName":"run","class":"java.util.concurrent.FutureTask"},{"lineNumber":1167,"file":"ThreadPoolExecutor.java","methodName":"runWorker","class":"java.util.concurrent.ThreadPoolExecutor"},{"lineNumber":641,"file":"ThreadPoolExecutor.java","methodName":"run","class":"java.util.concurrent.ThreadPoolExecutor$Worker"},{"lineNumber":929,"file":"Thread.java","methodName":"run","class":"java.lang.Thread"}],
    "userInfo":null,
    "message":"Error in getting binary resources modified time",
    "code":"EUNSPECIFIED"
}

解決方式是在 android/app/build.gradle 的 defaultConfig 中增加:
原文

resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())

然后重新編譯打包給Android機(jī)安裝,然后再重新改代碼,進(jìn)行發(fā)布測(cè)試熱更新

總結(jié):

1、使用 npm 安裝 react-native-code-push 依賴
2、更改android的原生代碼進(jìn)行codePush連線
3、使用 微軟管理平臺(tái) 創(chuàng)建app
4、使用 appcenter-cli 進(jìn)行指令管控
5、使用 react-native-code-push 進(jìn)行編碼更新
6、發(fā)布更新測(cè)試
7、解決更新失敗的問(wèn)題

以上是codePush一個(gè)簡(jiǎn)單實(shí)用,如想更詳細(xì)更多功能,建議查看官網(wǎng)文檔;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容