uniapp的原生鸿蒙系统APP热更新解决方案

前提

uniapp已经支持安卓、iOS、鸿蒙,但是到目前发稿前,uniapp对鸿蒙的支持还是存在不少问题,例如在HBuilderX里选择发行>AppHarmony-本地打包时,编译过程中,会有报错。我的做法就是不用发行的方式编译打包,改用运行>运行到手机或模拟器>运行到鸿蒙(前提是需要有个鸿蒙5.0以上的手机)。

另外有个更重要的问题,就是uniapp目前对鸿蒙APP热更新支持存在问题,可以在到论坛搜索相关问题。我也提了相关bug及解决方案,但官方没有回应。

看到网友的提问,还是不清楚怎么处理,便在这里把我的操作流程说一下,目前我们公司按照这个流程操作,不会出现第一次热更新成功,第二次失败,每次都是成功的。

另外有一点很重要,就是安卓和iOS的热更新包 与 鸿蒙的热更新包 要分开使用。即安卓和iOS的只能使用安卓和iOS的热更新包,鸿蒙5.0以上的系统只能使用鸿蒙的热更新包。

鸿蒙APP热更新支持操作流程

按照uniapp官方调用鸿蒙原生API文档添加后文提供的代码,安卓和iOS相关的用不到,如果编译异常,空实现即可
先删除旧的 HBuilderX 编译出鸿蒙工程,再通过 HBuilderX 编译出新的鸿蒙工程
关闭 HBuilderX 运行的鸿蒙
到编译出来新鸿蒙工程里(刚刚通过HBuilderX 编译出新的鸿蒙工程),将 entry/src/main/ets/entryability/EntryAbility.ets 文件里的 HBuilder替换为 uniapp工程的mainfest.jsonappid的值(即构造函数中,调用super方法的第一个参数替换为你的appid)
entry/src/main/resources/resfile/apps/HBuilder 复制一份处理到 entry/src/main/resources/resfile/apps 目前下,且文件夹名为 uniapp工程的mainfest.jsonappid的值
使用DevEco-Studio编译打包

代码部分

鸿蒙热更新使用,这部分添加到uniapp的工程里需要热更新的地方,注意与安卓、iOS的热更新分开

if(){
            
	/**
	 * 判断是原生鸿蒙系统后进入这里热更新处理
	 * 需要注意,原生鸿蒙系统要先申请网络权限和存储权限
	 */
	let appid = 'uni-app应用标识(AppID),查看uniapp工程的manifest.json文件'
	let url = 'wgt热更新包下载链接'
	let filename = 'wgt热更新包本地存储名称'
	let progressFun = function(res){
            
			const progress = parseInt(res.receivedSize / res.totalSize * 100);
			// 显示下载进度
		}
	uni.myFileDownload({
            
		url: url,
		name: filename,
		progress : progressFun,
		complete : (res) =>{
            
			if(res.err == 0){
            
				// 下载完成 准备安装
				...
				// 安装
				uni.myInstall(appid, res.filePath, (res2)=>{
            
					// 安装结束 
					...
					// 安装成功重启
					res2 && uni.myRestartApp()
				})
			}
		}
	})
}

package.json文件内容

...其他属性
		"uni-ext-api": {
            
			"uni": {
            
				"myFileDownload": {
            
					"name": "myFileDownload",
					"app": {
            
						"js": false,
						"kotlin": false,
						"swift": false,
						"arkts": true
					}
				},
				"myInstall": {
            
					"name": "myInstall",
					"app": {
            
						"js": false,
						"kotlin": false,
						"swift": false,
						"arkts": true
					}
				},
				"myRestartApp": {
            
					"name": "myRestartApp",
					"app": {
            
						"js": false,
						"kotlin": false,
						"swift": false,
						"arkts": true
					}
				}
			}
		},
...其他属性

/utssdk/interface.uts文件内容

export interface Uni {
            
	/**
	 * 下载热更新包
	 * @param {FileDownloadOptions} options 下载参数
	 */
	myFileDownload(options : FileDownloadOptions) : void;
	
	/**
	 * 安装热更新包
	 * @param {string} appid uni-app应用标识(AppID),查看uniapp工程的manifest.json文件
	 * @param {string} path wgt下载路径
	 * @param {null|Callback} callback 回调
	 */
	myInstall(appid: string, path: string, callback : null|Callback) : void;
	
	/**
	 * 重启app
	 */
	myRestartApp() : void;
}

// 下载完成结果
export type FileDownloadCallbackResult = {
            
	url: string,
	name: string,
	filePath: null|string,
	/**
	 * 错误码 0表示没有错误
	 */
	err: number,
	/**
	 * 错误信息
	 */
	errMsg : null|string
};

// 下载进度信息
export type FileDownloadProgress = {
            
	receivedSize: number, 
	totalSize: number
};

// 下载进度回调
export type FileDownloadProgressCallback = (callbackResult : FileDownloadProgress) => void;
// 下载完成回调
export type FileDownloadCallback = (callbackResult : FileDownloadCallbackResult) => void;

export type FileDownloadOptions = {
            
	url: string,
	name: string,
	progress : null|FileDownloadProgressCallback,
	complete : FileDownloadCallback
};
export type MyFileDownload = (options : FileDownloadOptions) => void;

export type Callback = (callbackResult : boolean) => void;

export type MyInstall = (appid: string, path: string, callback : null|Callback) => void;

export type MyRestartApp = () => void;

/utssdk/app-harmony/index.uts文件内容

import {
            
	FileDownloadOptions,
	FileDownloadProgress,
	FileDownloadCallbackResult,
	MyFileDownload,
	
	Callback,
	MyInstall,
	
	MyRestartApp
} from '../interface.uts';

import {
             common as myAppcommon, Want } from '@kit.AbilityKit';
import {
             request as myAppRequest } from '@kit.BasicServicesKit';
import {
             fileIo } from '@kit.CoreFileKit';

// 热更新的核心方法
import {
             releaseWgtToRunPath } from '@dcloudio/uni-app-runtime';

export const myFileDownload: MyFileDownload = function(options : FileDownloadOptions) : void {
            
	let context = getContext() as myAppcommon.UIAbilityContext;
	let filesDir = context.filesDir
	let filePath = filesDir + '/' + options.name
	try {
            
		// 删除文件
		fileIo.unlinkSync(filePath)
	} catch (error) {
            }
	try {
            
		let config: myAppRequest.DownloadConfig = {
            
			url: options.url,
			filePath: filePath
		}
		myAppRequest.downloadFile(context, config).then((downloadTask: myAppRequest.DownloadTask) => {
            
			if(options.progress){
            
				let progressCallback = (receivedSize: number, totalSize: number) => {
            
					let progressInfo : FileDownloadProgress = {
            
						receivedSize: receivedSize, 
						totalSize: totalSize
					}
					options.progress?.(progressInfo)
				}
				downloadTask.on('progress', progressCallback);
			}
			let failCallback = (err: number) => {
            
				let errMsg = 'Failed to download the task('+options.url+'). Code: '+err
				let fileDownloadCallbackResult : FileDownloadCallbackResult = {
            
					url: options.url,
					name: options.name,
					filePath: null,
					err: 1,
					errMsg : errMsg
				}
				options.complete(fileDownloadCallbackResult)
			}
			let completeCallback = () => {
            
				let fileDownloadCallbackResult : FileDownloadCallbackResult = {
            
					url: options.url,
					name: options.name,
					filePath: filePath,
					err: 0,
					errMsg : 'ok'
				}
				options.complete(fileDownloadCallbackResult)
			}
			downloadTask.on('fail', failCallback)
			downloadTask.on('complete', completeCallback)
		}).catch(err => {
            });
	} catch (error) {
            }
}

export const myInstall: MyInstall = function(appid: string, path: string, callback : null|Callback) : void{
            
	try{
            
		releaseWgtToRunPath(appid, path, (code: 1 | -1, error?: Error)=>{
            
			callback?.(code == 1)
		})
	}catch(err){
            
		callback?.(false)
	}
}

export const myRestartApp: MyRestartApp = function() : void {
            
	try {
            
		const context = getContext() as myAppcommon.UIAbilityContext
		let applicationContext = context.getApplicationContext();
		let want: Want = {
            
			bundleName: context.abilityInfo.bundleName,
			abilityName: 'EntryAbility'
		};
		applicationContext.restartApp(want);
	} catch (error) {
            }
}

编译出来新鸿蒙工程里entry/src/main/ets/entryability/EntryAbility.ets 文件内容

import {
             UniEntryAbility } from "@dcloudio/uni-app-runtime";
import {
             initUniModules } from "../uni_modules/index.generated";
import BuildProfile from "BuildProfile";

const appid = 'uni-app应用标识(AppID),查看uniapp工程的manifest.json文件'

initUniModules();

interface IHmr {
            
  init: Function
}

export default class EntryAbility extends UniEntryAbility {
            
  constructor() {
            
    super(appid, {
            
      debug: BuildProfile.DEBUG,
    });

    if (BuildProfile.DEBUG) {
            
      import('@uni_modules/hmr-for-uni-app').then((hmr: IHmr) => {
            
        hmr.init()
      }).catch((err: Error) => {
            
        console.error('[HMR] import @uni_modules/hmr-for-uni-app failed: ' + err.message)
      })
    }
  }
}
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容