React Native realizes hot update and automatic signature packaging function

  • 2021-12-04 11:09:13
  • OfStack

Project Background: Manual link Android App

1. Download react-native-code-push

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

2. Add under 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')

3. In android\ app\ src\ main\ java\ com\ app\ MainApplication.java Modification in file


...
// 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();
    }
  // Manual link What needs to be modified , Automatic link It should not need to be changed 
      @Override
  protected List<ReactPackage> getPackages() {
   @SuppressWarnings("UnnecessaryLocalVariable")
   List<ReactPackage> packages = new PackageList(this).getPackages();
   // Packages that cannot be autolinked yet can be added manually here, for
   // example:
   // packages.add(new MyReactNativePackage());
   packages.add(new CodePush(getResources().getString(R.string.CodePushDeploymentKey),
     getApplicationContext(), BuildConfig.DEBUG,
     getResources().getString(R.string.reactNativeCodePush_androidServerURL)));
   return packages;
  }
  };
}
 
//CodePushDeploymentKey Correspondence string.xml Inside  Deployment key Adj. name
//reactNativeCodePush_androidServerURL Correspondence string.xml Inside the hot update service address name

4. Modification of string. xml: First, add your app to the push center and get the key of the environment branch you need

4.1. Log in to the hot update server

4.2. Push Center Create Project: (For 1st Deployment)

code-push app add Project Name android react-native

4.3 Add Environment Branch dev: code-push deployment add Project Name dev (for 1st deployment)

To package the project into the corresponding environment branch, you need to configure the key and hot update address corresponding to the environment branch into the project file: (strings. xml)

4.4 The preparation is done, now let's modify the string. xml file


...
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
...
 
dependencies {
  implementation project(':react-native-code-push') // It's best to add it manually, otherwise there may be pits 
  implementation fileTree(dir: "libs", include: ["*.jar"])
  ....
}
....

5. Modification of android/app/build. gradle file


 STACK TRACE AND/OR SCREENSHOTS
 
:app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:6: error: package com.microsoft.codepush.react does not exist
import com.microsoft.codepush.react.CodePush;
                  ^
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:23: error: cannot find symbol
    return CodePush.getJSBundleFile();
        ^
 symbol: variable CodePush
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:35: error: cannot find symbol
      new CodePush(null, getApplicationContext(), BuildConfig.DEBUG),
        ^
 symbol: class CodePush
3 errors
:app:compileDebugJavaWithJavac FAILED
 
FAILURE: Build failed with an exception.
 
* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

Mining pit: react-native-code-push need to add dependencies manually, otherwise an error will be reported:

STACK TRACE AND/OR SCREENSHOTS

:app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:6: error: package com.microsoft.codepush.react does not exist
import com.microsoft.codepush.react.CodePush;
^
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:23: error: cannot find symbol
return CodePush.getJSBundleFile();
^
symbol: variable CodePush
C:\Stock Api\stock_app\android\app\src\main\java\com\stock_app\MainApplication.java:35: error: cannot find symbol
new CodePush(null, getApplicationContext(), BuildConfig.DEBUG),
^
symbol: class CodePush
3 errors
:app:compileDebugJavaWithJavac FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

6. Mining pit: app of manual link react-native-code-push, autolink needs to be prohibited, otherwise an error will be reported:

java.lang.IllegalStateException: Native module CodePush tried to override CodePushNativeModule. Check the getPackages() method in MainApplication.java, it might be that module is being created twice. If this was your intention, set canOverrideExistingModule=true

Therefore, it is necessary to add a configuration file to prohibit automatic link

Create the react-native. config. js file in the project root directory


module.exports = {
  dependencies: {
    'react-native-code-push': {
      platforms: {
        android: null, // disable Android platform, other platforms will still autolink
      },
    },
  },
};

7. The hot update configuration is complete. Now we need to detect the hot update when the project starts and prompt

In the project entry file App. js:


import React,{ Component } from 'react';
import Root from './src/inner';
import configureStore from './src/inner/store';
import UpdateDialog from './src/common/components/updateDialog'
import CodePush from "react-native-code-push";
 
const { persistor, store } = configureStore();
 
 
class App extends Component {
 
 state = {
 
  visitDialog: false,
  current: 0,
  total: 100
 }
 
 
 
 componentDidMount() {
  CodePush.sync({
   // Installation mode 
   //ON_NEXT_RESUME  The next time you return to the front desk, 
   //ON_NEXT_RESTART  Under 1 On the second restart 
   //IMMEDIATE  Update now 
   installMode: CodePush.InstallMode.IMMEDIATE,
   // Dialog box 
   updateDialog: {
    // Display Update Description 
    appendReleaseDescription: true,
    // Updates the prefix of the description.   Default to "Description"
    descriptionPrefix: " Update content: ",
    // Force update button text, default to continue
    mandatoryContinueButtonLabel: " Update now ",
    // Information when forcing updates .  Default to "An update is available that must be installed."
    mandatoryUpdateMessage: " Must be updated before it can be used ",
    // Button text when not forced to update , Default to "ignore"
    optionalIgnoreButtonLabel: ' Later ',
    // Confirm button text when not forced to update .  Default to "Install"
    optionalInstallButtonLabel: ' Background update ',
    // When an update is not forced, the updated message text is checked 
    optionalUpdateMessage: ' There is a new version, is it updated? ',
    //Alert The title of the window 
    title: ' Update prompt '
   },
  },
   (status) => {
    console.log(status, 'status')
    if (status == 7) {
     this.setState({ visitDialog: true })
    }
   },
   (progress) => {
    let receivedBytes = progress.receivedBytes / 1024 / 1024;
    let totalBytes = progress.totalBytes / 1024 / 1024;
    this.setState({
     current: receivedBytes,
     total: totalBytes
    })
    if (receivedBytes === totalBytes) {
     setTimeout(() => {
      this.setState({ visitDialog: false })
     }, 1000)
    }
    console.log(progress, 'progress')
   }
  );
 }
 
 render() {
  console.log(this.state.visitDialog, 'visitDialog');
  return (
   <>
    <Root store={store} persistor={persistor} />
    {this.state.visitDialog && <UpdateDialog
     title={this.state.current === this.state.total ? ' Completed ' : ' Downloading update file '}
     describe={this.state.current === this.state.total ? ' Welcome to ' : ' Please be patient '}
     current={this.state.current} total={this.state.total}></UpdateDialog>}
   </>
  )
 }
};
 
let codePushOptions = {
 // Set the frequency of checking for updates 
 //ON_APP_RESUME APP When I returned to the front desk, 
 //ON_APP_START APP When it is opened, 
 //MANUAL  Manual inspection 
 checkFrequency: CodePush.CheckFrequency.ON_APP_START
};
 
export default CodePush(codePushOptions)(App);

UpdateDialog: Is my own encapsulated hot update download progress bar components, download tips, according to their own mood to write casually, here I will not post their own code! (It's not good, I'm sorry.)

Now that we have the hot update configuration, pack the official apk!

1. Generate a signature file: Run the command in the root directory of the project:

keytool-genkey-v-keystore My Signature-key. jks-keyalg RSA-keysize 2048-validity 10000-alias My Signature

2. Copy the generated signature file to the directory: android/app

3. Configure gradle. properties


android.useAndroidX=true
android.enableJetifier=true
MYAPP_RELEASE_STORE_FILE=wms-app-key.jks // Generated signing key 
MYAPP_RELEASE_KEY_ALIAS=ydh // Alias 
MYAPP_RELEASE_STORE_PASSWORD= Password set when signing 
MYAPP_RELEASE_KEY_PASSWORD= Password set when signing 

4. Amendment of android/app/build.gradle


  signingConfigs {
    debug {
      ...
    }
 
    release {
      if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
        storeFile file(MYAPP_RELEASE_STORE_FILE)
        storePassword MYAPP_RELEASE_STORE_PASSWORD
        keyAlias MYAPP_RELEASE_KEY_ALIAS
        keyPassword MYAPP_RELEASE_KEY_PASSWORD
      }
    }
  }

5. Packaging (under android directory):.\ gradlew.bat assembleRelease

If app is packaged successfully, copy apk to the mobile phone for installation

6. Push code: (When you need to update, push the code to the environment branch you want to update)

Push to dev Environment: code-push release-react Project Name android-d dev

Push to production environment:-m true for forced update, not added for no forced update

code-push release-react Project Name android-d Production-m true

Then restart app, and you can see the update prompt

Summarize


Related articles: