React Native 熱更新實踐

xiangzhihong發表於2023-04-21

以下是基於CodePush的熱更新方案的實踐,有需要的可以參考一下:

一、配置appcenter

1.1 安裝appcenter

安裝appcenter的命令如下:

npm install -g appcenter-cli 
/** 安裝完成後 */
appcenter help
/** 如果出現幫助指令說明安裝成功 */

安裝成功之後,登入appcenter,涉及的命令如下:

appcenter login

執行後在開啟的瀏覽器選擇一種登陸方式登陸(開啟的網頁需要掛代理)。
image.png

登陸成功後會獲得token,將token 填入控制檯完成登陸。
image.png

登陸成功後,執行如下命令可以檢視登陸資訊。

appcenter profile list

1.2 建立不同平臺的App

建立的命令格式如下:

appcenter apps create -d <appDisplayName> -o <operatingSystem> -p <platform>

比如,下面是隻考慮iOS 和Android平臺。

appcenter apps create -d RNDemoAndroid -o Android -p React-Native
appcenter apps create -d RNDemoiOS -o iOS -p React-Native

接著,建立App在Staging和Production環境的部署key,命令如下。

//ios 
appcenter codepush deployment add -a <ownerName>/RNDemoiOS Staging 
appcenter codepush deployment add -a <ownerName>/RNDemoiOS Production
//android
appcenter codepush deployment add -a <ownerName>/RNDemoAndroid Staging
appcenter codepush deployment add -a <ownerName>/RNDemoAndroid Production

以上shell命令都會返回成功的提示。接下來,再執行一下語句。

//自行替換
ownername appName appcenter codepush deployment list -a <ownerName>/<appName> -k

image.png

到此,appcenter配置過程就完成了。關於Appcenter的使用和配置,可以參考下面的內容。

https://github.com/microsoft/appcenter-cli

https://learn.microsoft.com/zh-cn/appcenter/

 

二、react-native-code-push安裝及配置

2.1 安裝

安裝的命令如下:

yarn add react-native-code-push cd ios && pod install

2.2 iOS配置

此處只針對react-native 0.60 版本及以上配置,配置前請確保已安裝必須的CocoaPods依賴項。開啟AppDelegate.m檔案,併為 CodePush 標頭新增匯入語句:

#import <CodePush/CodePush.h>

AppDelegate.m的程式碼如下 (注意:#import 必須在FB_SONARKIT_ENABLED 前面,否則ios archive 會報錯)。

#import "AppDelegate.h" 
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h> 
//** 引入熱更新 
#import <CodePush/CodePush.h>
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h> 
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h> 
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

然後替換下面的程式碼:

return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
//替換為
return [CodePush bundleURL];

增加部署的key值到 info.plist中。

<key>CodePushDeploymentKey</key>
<string>xxx</string>

更多的內容以及配置react-native 版本0.6以下的詳見內容:https://github.com/microsoft/react-native-code-push/blob/master/docs/setup-ios.md

 

2.3 Android配置

開啟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')

然後,在android/app/build.gradle中新增如下配置程式碼:

... 
apply from: "../../node_modules/react-native/react.gradle" 
//新增這一行 apply
from: "../../node_modules/react-native-code-push/android/codepush.gradle" 
...

開啟MainApplication.java檔案,然後引入以 CodePush程式碼。

...
// 1. Import the plugin class.
import com.microsoft.codepush.react.CodePush;


public class MainApplication extends Application implements ReactApplication {


    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...


        // 2. Override the getJSBundleFile method in order to let
        // the CodePush runtime determine where to get the JS
        // bundle location from on each app start
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }
    };
}

將部署的key加入到strings.xml。

<resources>
    <string name="app_name">AppName</string> 
    //xxx 是部署的key 
    <string moduleConfig="true" name="CodePushDeploymentKey">xxx</string>
</resources>

更多的配置,請參考:https://github.com/microsoft/react-native-code-push/blob/master/docs/setup-android.md

 

2.4 遇到的問題

1,Xcode執行專案到模擬器的時候報錯:Could not build module 'Foundation'。

解決辦法:更新mac系統到最新,更新xcode版本到最新!!!

 

2,Xcode archive 專案打包時提示:use of undeclared identifier 'CodePush'

解決辦法:#import 必須在FB_SONARKIT_ENABLED 前面,否則ios archive 會報錯

 

三、React Native 專案程式碼

3.1 修改程式碼

為了實現RN的更新,我們還需要對RN專案的程式碼進行修改。首先,需要在專案的入口檔案中引入CodePush,並新增如下邏輯:

//for class
import codePush from "react-native-code-push";
class MyApp extends Component { }
MyApp = codePush(MyApp); 
//for hook
import codePush from "react-native-code-push"; 
const MyAp = () => { } 
// 如果需要更快更新,可以將 MyApp = codePush(MyApp) 新增;
// let codePushOptions = { checkFrequency: codePush.CheckFrequency.ON_APP_RESUME };
// MyApp = codePush(codePushOptions)(MyApp); 
MyApp = codePush(MyApp);

如果需要手動檢查更新,也可以使用下面的程式碼:

let codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL };


class MyApp extends Component {
    onButtonPress() {
        codePush.sync({
            updateDialog: true,
            installMode: codePush.InstallMode.IMMEDIATE
        });
    }


    render() {
        return (
            <View>
                <TouchableOpacity onPress={this.onButtonPress}>
                    <Text>Check for updates</Text>
                </TouchableOpacity>
            </View>
        )
    }
}


MyApp = codePush(codePushOptions)(MyApp);

3.2 快速驗證程式碼

下面,我提供了快速驗證效果的示例程式碼。替換app.tsx 為以下程式碼可以快速驗證熱更新效果。

/* eslint-disable no-lone-blocks */
/* eslint-disable react-native/no-inline-styles */


import React from 'react';
import type {Node} from 'react';
import {
  SafeAreaView,
  Text,
  Alert,
  useColorScheme,
  TouchableOpacity,
  View,
  Image,
} from 'react-native';
import testImg from './assets/test.png';
import Video from 'react-native-video';
import CodePush from 'react-native-code-push';
import consultant from './assets/video/consultant.mp4';


const App: () => Node = () => {
  const backgroundStyle = {
    backgroundColor: '#fff',
    flex: 1,
  };
  const check = () => {
    CodePush.sync(
      {
        installMode: CodePush.InstallMode.IMMEDIATE,
      },
      (status: CodePush.SyncStatus) => {
        console.log(status, CodePush.SyncStatus);
        switch (status) {
          case CodePush.SyncStatus.UP_TO_DATE:
            {
              Alert.alert('已經是最新版本');
            }
            break;
          case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
            {
              Alert.alert('正在下載更新包');
            }
            break;
          case CodePush.SyncStatus.UPDATE_INSTALLED:
            {
              Alert.alert('最新版本已安裝');
            }
            break;
          case CodePush.SyncStatus.UPDATE_IGNORED:
            {
              Alert.alert('更新已忽略');
            }
            break;
          case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
            {
              Alert.alert('正在檢查更新');
            }
            break;
          default:
            break;
        }


        console.log(status);
      },
      () => {},
    );
  };
  const clear = () => {
    CodePush.clearUpdates();
  };
  return (
    <SafeAreaView style={backgroundStyle}>
      <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        <TouchableOpacity onPress={check}>
          <Text>Check for updates</Text>
        </TouchableOpacity>
        <Text>當前版本1.1.1</Text>
        <TouchableOpacity onPress={clear} style={{marginTop: 20}}>
          <Text>clear updates</Text>
        </TouchableOpacity>
        {/** 校驗圖片 */}
        {/* <Image source={testImg} style={{width: 100, height: 100}} /> */}
        {/** 驗證影片 */}
        {/* <Video
          source={consultant}
          resizeMode="cover"
          paused={false}
          repeat
          style={{width: 200, height: 200}}
        /> */}
      </View>
    </SafeAreaView>
  );
};


export default CodePush(App);

四、推送更新

首先,上傳jsbudle包到codepush,執行釋出更新命令:

appcenter codepush release-react -a <ownerName>/<appName> 


//example 
appcenter codepush release-react -a <ownerName>/MyApp-iOS
appcenter codepush release-react -a <ownerName>/MyApp-Android

釋出成功會收到成功的提示。
image.png
在操作的過程中,遇到了一個推送jsbudle 錯誤。

appcenter codepush release-react -a 1256003290-qq.com/RNDemoIOS --plist-file-prefix  "ios/orange"
/**error message  */
/**Command 'codepush release-react -a 1256003290-qq.com/RNDemoIOS' failed with exception "Unable to find either of the following plist files in order to infer your app's binary version: "ios/reactnativecli/Info.plist", "ios/Info.plist". If your plist has a different name, or is located in a different directory, consider using either the "--plist-file" or "--plist-file-prefix" parameters to help inform the CLI how to find it." */
appcenter codepush release-react -a 1256003290-qq.com/RNDemoIOS 
/** Command 'codepush release-react -a 1256003290-qq.com/RNDemoIOS' failed with exception "Unable to find either of the following plist files in order to infer your app's binary version: "ios/reactnativecli/Info.plist", "ios/Info.plist". If your plist has a different name, or is located in a different directory, consider using either the "--plist-file" or "--plist-file-prefix" parameters to help inform the CLI how to find it."*/
appcenter codepush release-react -a 1256003290-qq.com/RNDemoIOS -p"ios/orange/info.plist"
 /** Command 'codepush release-react -a 1256003290-qq.com/RNDemoIOS -p ios/orange/info.plist' failed with exception "Unable to find either of the following pbxproj files in order to infer your app's binary version: "ios/reactnativecli.xcodeproj/project.pbxproj", "ios/project.pbxproj"."*/

發現怎麼執行,codepush 都表示路徑不對,找不到info.plist。最後發現codepush 去的包名是從package.json的name,他會按照這個去找路徑下的配置檔案,我的包名恰好和專案名不一致,導致了問題。然後,修改package包名和專案名一致後就可以了。

相關文章