uniapp hot update, farewell to cloud packaging

preface

The project has always used uniapp to package apps, but every time it is released, tested or launched, it must use the official cloud packaging... There are size restrictions. Needless to say, the waiting time on Friday is longer than the packaging time, so I wonder if it can be hot updated? Just do it

Research stage

First, I looked in the documents on the official website and found that the official had a hot update platform. Unfortunately Official documents Here, the plug-ins are installed according to the official documents. The documents are still very detailed. The plug-ins are divided into two parts. There is nothing to say about the package management platform. It's OK to basically follow the official documents. Here we mainly talk about the use of APP plug-ins and some perfect parts

Silent update

According to the official documents, we can basically update the package. Because we don't want users to be aware of the update process, we choose to use silent update, but there are problems in the process... After silent update, the interface style will be completely disordered occasionally, and then restart the application manually

This problem has been tossed for some time. It seems that the official has no solution, so we can only do it ourselves

Fix silent update bug

Now it is known that if the package is updated during use, the interface style may be confused. After the update, we can help the user restart directly. It's perfect~

However, things are not so simple. At this time, the product manager told me that if the user restarts directly during use, the user will think there is a problem with our software... I said... Why don't I add a pop-up window to tell him, and then the product manager thinks it's still bad. Maybe the user can't just exit after filling out some forms... After a long discussion

The final conclusion is... Rewrite the whole silent update process, which is probably that the user runs the APP foreground - detects whether there is an updated version - if so, download it, save it locally, install it the next time the user runs the APP foreground, and prompt that it needs to be restarted to use the latest functions, and then help the user restart

code implementation

Looking at the plug-in source code, we know that the logic of hot update is relatively simple. It is mainly updated by using the H5 + plus.runtime.install method. In other words, we can completely customize how to download and deal with this hot update package. As long as we use the install method mentioned above at the end

Let's start with the place where we downloaded the package

Find the location of the APP hot update plug-in source code, uni in the root directory_ Modules / uni-upgrade-center-APP/utils/check-update.js, where the download is successful and the update is silent, the original is to download and install directly. Remove the installed code and save it first

if (is_silently) {
	uni.downloadFile({
		url: e.result.url,
		success: res => {
			if (res.statusCode == 200) {
				// Save the file first, and then judge the installation when you enter the home page
				uni.saveFile({
					tempFilePath:res.tempFilePath,
					success:function(res){
						if(globalConfig.gitName != 'master'){
                                                // It can be added here. It is judged according to the git branch, which will be mentioned later
							uni.showModal({
								title:"WGT Saved successfully",
							})
						}
						uni.setStorageSync('wgtFile',res.savedFilePath)
					},
					fail(e){
						if(globalConfig.gitName != 'master'){
							uni.showModal({
								title:"Save failed",
								content:JSON.stringify(e)
							})
						}
					},
				})
				// plus.runtime.install(res.tempFilePath, {
				// 	force: false
				// });
			}
		},
		fail(e){
			if(globalConfig.gitName != 'master'){
				uni.showModal({
					title:"Download failed",
					content:JSON.stringify(e)
				})
			}
		
		},
	});
	return;
}



Handling of hot update package installation

After the download is completed, we will deal with the installation logic. At the main entrance of App.vue, in the life cycle of onShow, this life cycle is the life cycle that the user will enter when the software foreground is running to judge whether there are uninstalled hot update packages. If there are, enter the installation. After the installation is completed, a window will pop up to prompt the user to restart. After the user clicks OK, the whole APP will be killed, In fact, at the beginning, I wanted to use restart for hot restart, but the problem of disordered style still exists, so I chose to kill the whole APP, that is, the following nativeQuit method, which will be mentioned later

if(uni.getStorageSync('wgtFile')){
	installFinish = false
	console.log("have wgtFile")
        // Here is the main function of hot update. Use H5 + install to install the hot update package
	plus.runtime.install(uni.getStorageSync('wgtFile'), {
		force: true
	},function(){
		uni.removeStorageSync('wgtFile')
		uni.showModal({
		    title: 'Tips',
		    content: 'APP A reboot is required to use the latest features',
			showCancel:false,
		    success: function (res) {
					console.log("Ready to restart")
		        if (res.confirm) {
					_this.nativeQuit();
					// plus.runtime.restart();  
		        } 
		    }
		});
	},function(error){
		uni.showModal({
		    title: 'Tips',
		    content: 'Installation failed' + error,
			showCancel:false,
		});
	});
}
if(installFinish && !uni.getStorageSync('wgtFile') && !uni.getStorageSync('downing')){
	// Call the APP update method. If the installation package is being installed or downloaded, do not get it
	wgtUpdate().then((e)=>{
	}).catch((e)=>{
	})
}

Customize the method of exiting APP nativeQuit

As mentioned above, the hot restart of H5 + still can not solve the problem of disordered style. We can only forcibly kill the APP and let the user manually enable the APP. Here, because the uniapp is the webview of the native APP, we need to realize different ways to exit the APP according to IOS and Android

Android exits APP and kills the background

//  After the hot update, the process needs to be killed and reopened, which needs to be implemented by introducing the class of Android system
let system = plus.android.importClass('java.lang.System')
_this.nativeQuit = function(){
	system.exit(0);
};

IOS exits APP and kills the background

_this.nativeQuit = function(){
	plus.ios.import('UIApplication').sharedApplication().performSelector('exit');
}

nativeQuit complete code

let _this = this;
uni.getSystemInfo({
	success: function(e) {
		console.log(e.platform)
		Vue.prototype.StatusBar = e.statusBarHeight;
		Vue.prototype.$phoneInfo = e
		// let system = plus.android.import('java.lang.System')
		
		if (e.platform == 'android') {
			let main = plus.android.runtimeMainActivity();
			//  After the hot update, the process needs to be killed and reopened, which needs to be implemented by introducing the class of Android system
			let system = plus.android.importClass('java.lang.System')
			_this.nativeQuit = function(){
				system.exit(0);
			};
		}else{
			_this.nativeQuit = function(){
				plus.ios.import('UIApplication').sharedApplication().performSelector('exit');
			}
		}
	}
})

So far, the hot update part has been basically completed. However, since the plug-in directly uses the cloud space of uni, and the test is different from the hot update package address of the formal environment, it brings a problem. Manually switch the cloud space every branch update? Once you forget to switch, send the package of link test cloud space to the official... Won't you be fired the next day for entering the company with your left foot

Automatic switching of cloud space according to branch name

Here is a note about the cloud space. The uniCloud folder will not be generated automatically!!!, And without this folder, if you use cloud space like me, you will find that you can't hot update any more... So if a new partner pulls a project, remember to let him generate it manually. Don't ask me why I know

Gets the name of the current git branch

In vue.config.js, borrow child_process library, you can run the script, get the branch name through the script, and save it for global access

const childProcess = require('child_process')
//  Get the current git branch name
const branchName = childProcess.execSync("git rev-parse --abbrev-ref HEAD", {
	cwd: __dirname.replace(/\\/g, '/')
}).toString().trim()

module.exports = {
  chainWebpack: config => {
    config
      .plugin('define')
      .tap(args => {
      // Save the branch name
        args[0]['process.env'].GIT_BRANCH_NAME = JSON.stringify(branchName)
        return args
      })
  }
}

Create a new file to save the configuration items of cloud space

Find the configuration of the space in the background of the uni cloud space

Create a new file to save the configuration items. It needs to be used to initialize the cloud space

const config = {
	gitName: process.env.GIT_BRANCH_NAME, // Branch name of current git
	uniClound: { // The configuration information of cloud space is used for hot update
		master: {
			provider: 'aliyun', // Alibaba cloud here is aliyun. What is Tencent? I forgot
			spaceId: 'Cloud space Id',
			clientSecret: 'Cloud space secret key'
		},
		test: {
			provider: 'Cloud space service provider',
			spaceId: 'Cloud space Id',
			clientSecret: 'Cloud space secret key'
		}
	}
}

export default config

Dynamically initialize cloud space

The official plug-in initializes cloud space files in uni_ Modules / UNI upgrade center app / utils / call check version.js, modify the function exported by default

import config from "../../../globalConfig.js" // Import the above configuration file
export default function() {
	// #ifdef APP-PLUS
	return new Promise((resolve, reject) => {
		plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
                // This method can obtain the version number and other information of the current APP, which should be used to judge the version number
			let myCloud = ''
			//  Use the branch name of git to initialize different cloud spaces
			if(config.gitName == 'master'){
                        // Cloud space connected to production environment
				myCloud = uniCloud.init(config.uniClound.master);
			}else{
                        // Connect the cloud space of the test environment
				myCloud = uniCloud.init(config.uniClound.test);
			}
			myCloud.callFunction({
				name: 'check-version',
				data: {
					appid: plus.runtime.appid,
					// appVersion: plus.runtime.version,
					appVersion: widgetInfo.version, // The application name is a large version number, which is used to compare the whole package updates of the APP
					// wgtVersion: widgetInfo.version
					wgtVersion: widgetInfo.versionCode, // Instead, use the application version number to judge the hot update of wgt
				},
				success: (e) => {
					resolve(e)
				},
				fail: (error) => {
					reject(error)
				}
			})
		})
	})
	// #endif
	// #ifndef APP-PLUS
	return new Promise((resolve, reject) => {
		reject({
			message: 'Please in App Used in'
		})
	})
	// #endif
}

In the test environment, the hot update pop-up window

It's convenient to eliminate errors. Personally, I think it's very important, because there are almost no prompts for silent updates, so it's best to catch errors and pop up windows at several download and update nodes, but only in the test environment, so we can also judge according to the branch name in uni_ Add pop-up prompt of test environment under modules / UNI upgrade center app / utils / check-update.js

uni.downloadFile({
	url: e.result.url,
	success: res => {
		if (res.statusCode == 200) {
			// Save the file first, and then judge the installation when you enter the home page
			uni.saveFile({
				tempFilePath:res.tempFilePath,
				success:function(res){
					if(globalConfig.gitName != 'master'){
                                        // Pop up the prompt when the branch is not the master
						uni.showModal({
							title:"WGT Saved successfully",
						})
					}
					uni.setStorageSync('wgtFile',res.savedFilePath)
				},
				fail(e){
					if(globalConfig.gitName != 'master'){
                                         // Pop up the prompt when the branch is not the master
						uni.showModal({
							title:"Save failed",
							content:JSON.stringify(e)
						})
					}
				},
			})
			// plus.runtime.install(res.tempFilePath, {
			// 	force: false
			// });
		}
	},
	fail(e){
		if(globalConfig.gitName != 'master'){
                 // Pop up the prompt when the branch is not the master
			uni.showModal({
				title:"Download failed",
				content:JSON.stringify(e)
			})
		}
	
	},
});

Finally, let's say a few points of attention

  1. The silent update can only update the business code. If you add the underlying calling capabilities, such as Bluetooth calls and push these, you need to use the whole package update
  2. If there is a problem that the cloud space cannot be accessed, the most likely reason is that the person who packed the last time did not have a uniCould folder, or the account is incorrect, because the cloud space follows the account
  3. IDE account switching will cause the uniCloud connection to be disconnected. Remember to manually connect and then package, otherwise there will be no hot update function

It's all over now. It's over~

Keywords: Javascript Front-end Vue.js

Added by CiPH on Thu, 09 Dec 2021 08:55:11 +0200