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