I. Android client environment construction

precondition

  • React-native version: 0.61+
  • Node version: 12.10.0+

React Native 0.60 and later automatically link third-party libraries if we implement YARN Add React-native code-push.

The Android environment automatically configures the code-push configuration file,

However, we set up the hot update of the private server, and need to manually configure the address to request hot update. Therefore, we need to disable the automatic configuration of react-native code-push in the Android environment

1. Initialize an RN project

Create a hands-on project for RN and then configure the Deployment Key configuration obtained above

$react-native init HotUpdateDemo $ cd ./HotUpdateDemo $ npm install --save react-native-code-push # installation react -- native code - a pushCopy the code

2. Disable autoLink for React-native code-push in android.

Create a react-native.config.js file in the project root directory and add the following content

module.exports = {
  dependencies: {
    "react-native-code-push": {
      platforms: {
        android: null}}}};Copy the code

3. Configure the Android environment

The official documentation

3.1 the android/app/build. Gradle modification

// Ellipsis............
// If react.gradle already exists, there is no need to introduce it again
apply from: ".. /.. /node_modules/react-native/react.gradle"
apply from: ".. /.. /node_modules/react-native-code-push/android/codepush.gradle"
    
// Ellipsis............. dependencies { ......Implementation "com.facebook.react:react-native:0.61.5" // From node_modules
    implementation project(path: ':react-native-code-push')  // From node_modules. }...Copy the code

3.2 the android/Settings. Gradle modification

. rootProject.name ='Name of current app'
// What matters is the following two lines of code
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '.. /node_modules/react-native-code-push/android/app')... . applyfrom: file(".. /node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'.Copy the code

3.3 Configuring deploymentKey in a development/production environment

Go to Android \app\ SRC \main\res\values\strings.xml and add the following code

Obtain DeploymentKey for different environments by using the code-push Deployment ls

-k command

<resources> ............ . <string moduleConfig="true" name="CodePushDeploymentKeyTest"> Test your environment's DeploymentKey</string> <string moduleConfig="true" name="CodePushDeploymentKeyPro"> DeploymentKey in production </string>............ . </resources>Copy the code

3.4 MainApplication. Java modification

packagecom.beesrv.www; .// Import codePush package
importcom.microsoft.codepush.react.CodePush; .public class MainApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {... .// Add this code
        @Override
        protected String getJSMainModuleName(a) {
          return "index";
        }
       	// Add this code
        @Override
        protected String getJSBundleFile(a) {
            return CodePush.getJSBundleFile();
        }  
        @Override
        protected List<ReactPackage> getPackages(a) {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
            
          // String deploymentKey, Context context, boolean isDebugMode, String serverUrl
          / * deploymentKey test environment/production environment for the current environment of hot update key, serverUrl for hot update private server address Such as: http://45.40.193.123:3000 * /              
		  // Add this code
          packages.add(new CodePush("deploymentKey",MainApplication.this,BuildConfig.DEBUG,"serverUrl"));
          returnpackages; }... . }; }Copy the code

3. Add a hot update code to RN

Open the React Native entry file index.js and make the following changes to the index.js file

import React, {Component} from 'react';
import {AppRegistry, Platform, StyleSheet, Text, View} from 'react-native';
import {name as appName} from './app.json';
import codePush from 'react-native-code-push'

type Props = {};
export default class App extends Component<Props> {

    constructor(props) {
        super(props);
        this.state = {
            message: ' '
        };
    }

    componentDidMount() {
        codePush.checkForUpdate().then((update) = > {
            if (update) {
                this.setState({message: 'There's a new update! '})}else {
                this.setState({message: 'Already up to date, no need to update! '})}})}render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>Version 1.0</Text>
                <Text style={styles.instructions}>{this.state.message}</Text>
            </View>); }}// omit the style file

AppRegistry.registerComponent(appName, () = > codePush(App));

Copy the code

The componentDidMount lifecycle function checks if the CodePush application needs to be updated, and if so, downloads the CodePush application’s updates. Recompile and run the application

Upgrade the version number displayed in the index.js file to 1.1

render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>Version 1.1</Text>
                <Text style={styles.instructions}>{this.state.message}</Text>
            </View>
        );
    }

Copy the code

4. Generate bundle

Before publishing an update, you need to bundle js as follows:

  • Step 1: Add bundles in the project directory:mkdir bundles
  • Step 2: Run the command packageReact-native bundle --platform platform --entry-file Startup file --bundle-output PACKAGE JS output file -- Assets-dest resource output directory --dev Whether to debug.

eg: react-native bundle --platform android --entry-file index.android.js --bundle-output ./bundles/index.android.bundle --dev false

/ / offline package $ cd./ Project catalog
 $ mkdir bundles
 $React-native bundle --platform platform --entry-file Startup file --bundle-output PACKAGE JS output file -- Assets-dest resource output directory --dev Whether to debug.
 eg.
 $react-native bundle --platform ios --entry-file index.js --bundle-output ./ios/bundles/main.jsbundle --dev false
Copy the code
React-native bundle --platform --entry-file Startup file --bundle-output JS output file --assets-dest resource output directory --dev Whether to debug eg:$ react-native bundle --entry-file index.ios.js --bundle-output ./bundle/ios/main.jsbundle --platform ios --assets-dest ./bundle/ios --dev false
Copy the code

5. Release updates

Once the bundle is packaged, updates can be published through CodePush. Input at terminal

Code-push release < application name > < bundle directory > < application version > --deploymentName: updates the environment --description: updates the description -- Mandatory: specifies whether updates are mandatory

Eg: code – push release GitHubPopular. / bundles/index. The android. Bundle 1.0.6 – deploymentName Production – the description “1. Support for article caching. –mandatory true


 $Code-push release < application name > < bundle directory > < application version > --deploymentName: updates the environment --description: updates the description -- Mandatory: specifies whether updates are mandatoryCode - push release HotUpdateDemo - ios. / ios/bundles/main jsbundle 1.0.0 - deploymentName Production - the description "I am a new package, Very new "-- Mandatory true
 $Code-push release-react 
       
       
         --t --des
       
      Note: CodePush updates the Staging environment by default. If you are releasing updates to the Production environment, you need to specify --d Production, or --m true if you are releasing forced updates $Code-push release-react iOSRNHybrid ios --t 1.0.0 --devfalse --d Production --des "This is the first update package." --m true
Copy the code

6. The easiest way (can be ignored)

Fusion simplifies step 4 and step 5 operations (auto build& Auto publish)

code-push release-react< appName > < plateForm > Test environment execution:$ code-push release-reactThe project name-androidAndroid production environment:$ code-push release-reactThe project name-android android -d Production

code-push release-react MyApp-iOS ios  --t 1.0.0 --dev false --d Production --des "1. Optimize the operation process" --mTrue The parameter --tIs the version of the binary (.ipa and APK) installation package; --devIs whether to enable developer mode (default: false); --dThere are Production Staging and Staging(default: Staging) environments where updates are to be released. --desTo update notes; --mThe update is mandatory. The parameter corresponding to -- t in the command to issue update package is consistent with the version number in our project, which should not be misunderstood as the version number of update package, for example, the version number in the project is1.0.0So if we need to do this1.0.0The first hot update for the version of the project, then -- t in the command is also equal to1.0.0, the second heat is new1.0.0The version number of the project needs to be changed to three digits. The default is two digits, but CodePush requires a three digit version number to publish the updated app. The app name must be the same as the previously registered app name regarding code-push release-reactMore optional parameters can be entered in terminal code-push release-reactTake a look. In addition, we can use code-push deployment ls<appName> to see the release details and the installation of this update. eg:code-push release-react codepushAPP ios
Copy the code

Wait for the system to package and publish the bundle for hot updates, close and reopen the application after successful publishing, and you will see updates when the application starts

After detecting an update, the system downloads the latest resource and updates it. When the application is closed and opened again, you can see the effect of the successful update. Also, you can view the updates using the code-push Deployment command provided by CodePush,

2. Build an IOS client

no

React-native code demo

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} from 'react-native';

import CodePush from "react-native-code-push"; / / introduce code - a push

let codePushOptions = {
  // Set the frequency of checking for updates
  // when the ON_APP_RESUME APP returns to the foreground
  //ON_APP_START when the APP is started
  //MANUAL MANUAL check
  checkFrequency : CodePush.CheckFrequency.ON_APP_RESUME
};

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu'.android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu'}); type Props = {};class App extends Component<Props> {

  // If there is an update prompt
  syncImmediate() {
    CodePush.sync( {
          // Install mode
          //ON_NEXT_RESUME the next time it returns to the foreground
          //ON_NEXT_RESTART The next restart
          //IMMEDIATE updates immediately
          installMode : CodePush.InstallMode.IMMEDIATE ,
          / / dialog
          updateDialog : {
            // Whether to display the update description
            appendReleaseDescription : true ,
            // Update the description prefix. The default is "Description"
            descriptionPrefix : "Update:" ,
            // Forces the button text to be updated. The default is continue
            mandatoryContinueButtonLabel : "Update now" ,
            // Mandatory update information. The default is "An update is available that must be installed."
            mandatoryUpdateMessage : "Must be updated before use." ,
            // The text of the button is "ignore" by default.
            optionalIgnoreButtonLabel : 'later' ,
            // Confirm button text when not forced to update. The default is "Install"
            optionalInstallButtonLabel : 'Background Update' ,
            // The updated message text is checked when the update is not mandatory
            optionalUpdateMessage : 'There is a new version, is it up to date? ' ,
            // Title of the Alert window
            title : 'Update Prompt'},},); }componentWillMount() {
    CodePush.disallowRestart();// Disable restart
    this.syncImmediate(); // Start checking for updates
  }

  componentDidMount() {
    CodePush.allowRestart();// Restart is allowed after loading
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit App.js
        </Text>
        <Text style={styles.instructions}>
          {instructions}
        </Text>

        <Text style={styles.instructions}>This is the newer version</Text>
      </View>); }}// This line must be written
App = CodePush(codePushOptions)(App)

export default App

const styles = StyleSheet.create({
  container: {
    flex: 1.justifyContent: 'center'.alignItems: 'center'.backgroundColor: '#F5FCFF',},welcome: {
    fontSize: 20.textAlign: 'center'.margin: 10,},instructions: {
    textAlign: 'center'.color: '# 333333'.marginBottom: 5,}})Copy the code

Before using it, you need to check when to update, whether the update is mandatory, whether the update is required to be real-time, etc

Update time

There are two common in-app update opportunities. One is to check the update upon opening the App, and the other is to put the App on the setting interface for users to check the update and install it

  • Check for updates when you open your APP

The easiest way to use it is in the React Natvie root component’s componentDidMount method

Codepush.sync () (import codePush from ‘react-native code-push’) checks and installs updates, which will take effect after reboot if there are updates available for download. However, both downloads and installations are silent, meaning they are not visible to the user. Additional configuration is required if user visibility is required. For details, please refer to the official codePush API documentation. For partial codes, please refer to the above documentation for complete codes

codePush.sync({
  updateDialog: {
    appendReleaseDescription: true.descriptionPrefix:'\n\n Update: \n'.title:'update'.mandatoryUpdateMessage:' '.mandatoryContinueButtonLabel:'update',},mandatoryInstallMode:codePush.InstallMode.IMMEDIATE,
  deploymentKey: CODE_PUSH_PRODUCTION_KEY,
});
Copy the code
  • In the above configuration, a prompt dialog box is displayed when checking for updates, mandatoryInstallMode means forcing updates, and appendReleaseDescription means that the description of the update when published is displayed in the update dialog for the user to see
  • The user clicks the check for updates button

After user click the update button to check, if there are update pop-up prompt dialog box allows users to choose whether or not to update, if the user clicks the update button immediately, will be for installation package download (in fact at that time should show the download progress, omitted here) restart immediately after the download is complete and effective (also can be configured later restart), part of the code is as follows

codePush.checkForUpdate(deploymentKey).then((update) = > {
    if(! update) { Alert.alert("Tip"."This is the latest version --"[{text: "Ok".onPress: () = > {
                console.log("O 'clock OK"); }}]); }else {
        codePush.sync({
                deploymentKey: deploymentKey,
                updateDialog: {
                    optionalIgnoreButtonLabel: 'later'.optionalInstallButtonLabel: 'Update now'.optionalUpdateMessage: 'There is a new version, is it up to date? '.title: 'Update Prompt'
                },
                installMode: codePush.InstallMode.IMMEDIATE,
            },
            (status) = > {
                switch (status) {
                    case codePush.SyncStatus.DOWNLOADING_PACKAGE:
                        console.log("DOWNLOADING_PACKAGE");
                        break;
                    case codePush.SyncStatus.INSTALLING_UPDATE:
                        console.log(" INSTALLING_UPDATE");
                        break; }},(progress) = > {
                console.log(progress.receivedBytes + " of " + progress.totalBytes + " received."); }); }}Copy the code

Whether the update is mandatory

If mandatory updates need to be specified at issue time, configure –m true in the issue command

Whether updates are required immediately

Specify installMode in update configuration to determine the restart time after installation, that is, when the update takes effect

  • CodePush. InstallMode. IMMEDIATE: the installation is complete restart the update immediately
  • Codepush.installmode. ON_NEXT_RESTART: The installation will be updated after the next reboot
  • Codepush.installmode. ON_NEXT_RESUME: The application will be restarted and updated after installation

Example 2

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow* /

import React, { Component } from 'react';
import {
  AppRegistry,
  Dimensions,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  Platform,
  View,
} from 'react-native';

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

//var Dimensions = require('Dimensions');

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu'.android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu'});export default class App extends Component<{} > {constructor() {
    super(a);this.state = { restartAllowed: true ,
                   syncMessage: "I'm the little Update." ,
                   progress: false};
  }

  // Listen for status updates
  codePushStatusDidChange(syncStatus) {
    switch(syncStatus) {
      case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
        this.setState({ syncMessage: "Checking for update." });
        break;
      case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
        this.setState({ syncMessage: "Downloading package." });
        break;
      case CodePush.SyncStatus.AWAITING_USER_ACTION:
        this.setState({ syncMessage: "Awaiting user action." });
        break;
      case CodePush.SyncStatus.INSTALLING_UPDATE:
        this.setState({ syncMessage: "Installing update." });
        break;
      case CodePush.SyncStatus.UP_TO_DATE:
        this.setState({ syncMessage: "App up to date.".progress: false });
        break;
      case CodePush.SyncStatus.UPDATE_IGNORED:
        this.setState({ syncMessage: "Update cancelled by user.".progress: false });
        break;
      case CodePush.SyncStatus.UPDATE_INSTALLED:
        this.setState({ syncMessage: "Update installed and will be applied on restart.".progress: false });
        break;
      case CodePush.SyncStatus.UNKNOWN_ERROR:
        this.setState({ syncMessage: "An unknown error occurred.".progress: false });
        break; }}codePushDownloadDidProgress(progress) {
    this.setState({ progress });
  }

  // Allow updates after restart
  toggleAllowRestart() {
    this.state.restartAllowed
      ? CodePush.disallowRestart()
      : CodePush.allowRestart();

    this.setState({ restartAllowed:!this.state.restartAllowed });
  }

  // Get updated data
  getUpdateMetadata() {
    CodePush.getUpdateMetadata(CodePush.UpdateState.RUNNING)
      .then((metadata: LocalPackage) = > {
        this.setState({ syncMessage: metadata ? JSON.stringify(metadata) : "Running binary version".progress: false });
      }, (error: any) = > {
        this.setState({ syncMessage: "Error: " + error, progress: false });
      });
  }

  /** Update is downloaded silently, and applied on restart (recommended) Automatically Update, one-click operation */
  sync() {
    CodePush.sync(
      {},
      this.codePushStatusDidChange.bind(this),
      this.codePushDownloadDidProgress.bind(this)); }** Update pops a confirmation dialog, and then immediately reboots the app */
  syncImmediate() {
    CodePush.sync(
      { installMode: CodePush.InstallMode.IMMEDIATE, updateDialog: true },
      this.codePushStatusDidChange.bind(this),
      this.codePushDownloadDidProgress.bind(this)); }render() {

   let progressView;

    if (this.state.progress) {
      progressView = (
        <Text style={styles.messages}>{this.state.progress.receivedBytes} of {this.state.progress.totalBytes} bytes received</Text>
      );
    }

    return (
      <View style={styles.container}>

        <Text style={styles.welcome}>You can modify the text here to see if the update is successful!</Text>

        <TouchableOpacity onPress={this.sync.bind(this)}>
          <Text style={styles.syncButton}>Press for background sync</Text>
        </TouchableOpacity>

        <TouchableOpacity onPress={this.syncImmediate.bind(this)}>
          <Text style={styles.syncButton}>Press for dialog-driven sync</Text>
        </TouchableOpacity>

        {progressView}
        
        <TouchableOpacity onPress={this.toggleAllowRestart.bind(this)}>
          <Text style={styles.restartToggleButton}>Restart { this.state.restartAllowed ? "allowed" : "forbidden"}</Text>
        </TouchableOpacity>

        <TouchableOpacity onPress={this.getUpdateMetadata.bind(this)}>
          <Text style={styles.syncButton}>Press for Update Metadata</Text>
        </TouchableOpacity>

        <Text style={styles.messages}>{this.state.syncMessage || ""}</Text>

      </View>); }}const styles = StyleSheet.create({
  container: {
    flex: 1.alignItems: "center".backgroundColor: "#F5FCFF".paddingTop: 50
  },
  image: {
    margin: 30.width: Dimensions.get("window").width - 100.height: 365 * (Dimensions.get("window").width - 100) / 651,},messages: {
    marginTop: 30.textAlign: "center",},restartToggleButton: {
    color: "blue".fontSize: 17
  },
  syncButton: {
    color: "green".fontSize: 17
  },
  welcome: {
    fontSize: 20.textAlign: "center".margin: 20}});Copy the code

Reference documentation

  • www.jianshu.com/p/8e08c7661…
  • Juejin. Cn/post / 684490…
  • github
  • The Denver nuggets
  • Juejin. Cn/post / 684490…
  • Codepush – API documentation
  • React-native code-push
  • Code-push Microsoft official documentation
  • how-can-i-disable-autolinking-for-unsupported-library
  • www.jianshu.com/p/6a5e00d22…
  • Segmentfault.com/a/119000001…