鸿蒙uiapp支持读取当地和在线拉取运行

打印 上一主题 下一主题

主题 1809|帖子 1809|积分 5427

   前言:鸿蒙运行uiapp小程序来源于uiapp的SDK包提供的能力,如有更多定制化业务,请自行查看SDK内容导入相应的API,比方以下
  1. import {
  2.   openUniMP,
  3.   releaseWgtToRunPath,
  4.   getUniMPRunPath,
  5.   isExistsUniMP,
  6.   getAppVersionInfo,
  7.   registerModule
  8. } from '@dcloudio/uni-app-runtime'
复制代码
告示:只支持Vue3版本运行

uniMP小程序

#初始化 sdk 全局环境

  1. interface IInitConfig {
  2.   debug?: boolean
  3. }
  4. function init(ability: UIAbility, stage: window.WindowStage, config?: IInitConfig)
复制代码
复制代码
#获取uni小程序应用资源部署路径

  1. function getUniMPRunPath(appID: string): string
复制代码
复制代码
#判断应用资源是否已经部署

  1. function isExistsUniMP(appId: string): boolean
复制代码
复制代码
#将wgt应用资源包部署到运行路径中

  1. type ReleaseWgtToRunPathCallback = (code: 1 | -1, error?: Error) => void
  2. function releaseWgtToRunPath(appID: string, wgtPath: string, callback: ReleaseWgtToRunPathCallback)
复制代码
复制代码
#获取已经部署的小程序应用资源版本信息

  1. interface IAppVersionInfo {
  2.   name: string
  3.   code: string
  4. }
  5. function getAppVersionInfo(appId: string): IAppVersionInfo | null
复制代码
复制代码
#启动小程序应用并获得IUniMP实例

  1. interface ICapsuleStyle {
  2.   backgroundColor?: string
  3.   textColor?: string
  4.   highlightColor?: string
  5.   borderColor?: string
  6. }
  7. interface ICapsuleMenuActionSheetItem {
  8.   id: string
  9.   title: string
  10. }
  11. interface IOpenUniMPConfig {
  12.   showCapsuleButton?: boolean
  13.   capsuleMenuActionSheetItems?: ICapsuleMenuActionSheetItem[]
  14.   capsuleButtonStyle?: ICapsuleStyle
  15.   redirectPath?: string
  16.   extraData?: Object // 传递给小程序的额外数据,小程序中onLaunch,uni.getLaunchOptionsSync可以获取
  17. }
  18. export interface IUniMP {
  19.   hide(): void
  20.   show(): void
  21.   close(): void
  22.   sendUniMPEvent: (event: string, data: ESObject) => void
  23.   on(event: 'uniMPEvent', callback: (event: string, data: ESObject, notify: (...args: ESObject[]) => void) => void): void
  24.   on(event: 'menuItemClick', callback: (id: string) => void): void
  25.   on(event: 'close' | 'show' | 'hide', callback: () => void): void
  26.   off(name: string, callback: Function): void
  27. }
  28. function openUniMP(appID: string, config?: IOpenUniMPConfig): IUniMP
复制代码
复制代码
#IUniMP实例下的API

#隐藏当前小程序应用

  1. hide(): void
复制代码
复制代码
#表现当前小程序应用

  1. show(): void
复制代码
复制代码
#关闭当前小程序应用

  1. close(): void
复制代码
复制代码
#向小程序发送变乱

  1. sendUniMPEvent: (event: string, data: ESObject) => void
复制代码
复制代码
#监听小程序发送过来的变乱

  1. on(event: 'uniMPEvent', callback: (event: string, data: ESObject, notify: (...args: ESObject[]) => void) => void): void
复制代码
复制代码
#监听右上角菜单的点击变乱

  1. on(event: 'menuItemClick', callback: (id: string) => void): void
复制代码
复制代码
#监听小程序隐藏、表现、关闭变乱

  1. on(event: 'close' | 'show' | 'hide', callback: () => void): void
复制代码
复制代码
#移除对应的监听变乱

  1. off(name: string, callback: Function): void
复制代码

  • 修改鸿蒙项目根目次文件 oh-package.json5 的依赖 "@dcloudio/uni-app-runtime": "版本号",如下图所示 查看最新版本号

  • 点击右上角 Sync Now,并等候 Sync 结束
  • 首先需要初始化uiapp SDK包 ,初始化如下
    1. 在EntryAbility文件入口如下
    2. import { init } from '@dcloudio/uni-app-runtime'
    复制代码
    1. init(this, windowStage, {
    2.   debug: true
    3. })
    复制代码
    1. <img alt=""  src="https://i-blog.csdnimg.cn/direct/1eb1fece6be84fdf85cbfa2791a61251.png"  />
    复制代码
  • 将生成的wgt包拷贝到 entry/src/main/resources/resfile 目次下,如下图所示


 4.再通过 releaseWgtToRunPath 函数释放 wgt 包到运行目次,最后通过 openUniMP 函数打开小程序,代码如下

在线拉取运行:
  1. /**
  2. *  下载 uiapp 包
  3. */
  4. function downloadUiApp(fileName: string, downUrl: string, md5Code: string): Promise<string> {
  5.   return new Promise((resolve) => {
  6.     DownloadUiApp.downloadUiApp(
  7.       downUrl,
  8.       fileName,
  9.       md5Code,
  10.       {
  11.         onError: (code: string = '') => {
  12.           // 下载错误
  13.           if (code === 'MD5_error') {
  14.             promptAction.showToast({
  15.               message: '文件损坏,请重新点击应用'
  16.             })
  17.           }
  18.           resolve(code)
  19.         },
  20.         onSuccess: (srcFilePath: string) => {
  21.           // 下载并保存成功
  22.           Logger.info('下载文件完成  地址为', srcFilePath)
  23.           resolve(srcFilePath)
  24.         }
  25.       })
  26.   })
  27. }
复制代码
下载文件Class

  1. import { common } from '@kit.AbilityKit';
  2. import fs, { ReadOptions, WriteOptions } from '@ohos.file.fs';
  3. import { BusinessError, request } from '@kit.BasicServicesKit';
  4. import { fileIo } from '@kit.CoreFileKit';
  5. import Logger from './Logger'
  6. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  7. Logger.prefix = 'uiapp'
  8. export class DownloadUiApp {
  9.   /**
  10.    * 获取应用沙箱存储目录 统一一下,方便APP设置页面删除
  11.    * @returns
  12.    */
  13.   static getLocalCacheDir(): string {
  14.     let cache_dir_name = "uiapps";
  15.     let cache_dir = getContext().filesDir + "/" + cache_dir_name;
  16.     console.info(`createCacheDir cache_dir: ${cache_dir}`);
  17.     let existAppCacheDir = fileIo.accessSync(cache_dir, fileIo.AccessModeType.EXIST)
  18.     if (!existAppCacheDir) {
  19.       fileIo.mkdirSync(cache_dir, true);
  20.     }
  21.     return cache_dir
  22.   }
  23.   static async checkAndRemoveFile(path: string) {
  24.     const exist = await fs.access(path);
  25.     if (exist) {
  26.       await fs.unlink(path);
  27.     }
  28.   }
  29.   //计算文件的MD5码
  30.   public static computeMD5(file: string | number): Uint8Array {
  31.     let md5Stream: fs.Stream
  32.     if (typeof file === 'string') {
  33.       md5Stream = fs.createStreamSync(file, "r");
  34.     } else {
  35.       md5Stream = fs.fdopenStreamSync(file, "r")
  36.     }
  37.     let dataBuff: ArrayBuffer = new ArrayBuffer(4096)
  38.     let mdAlgName = "MD5";
  39.     let md = cryptoFramework.createMd(mdAlgName);
  40.     let readCount = md5Stream.readSync(dataBuff)
  41.     while (readCount > 0) {
  42.       let messageData = new Uint8Array(dataBuff.slice(0, readCount));
  43.       let updateMessageBlob: cryptoFramework.DataBlob = { data: messageData };
  44.       md.updateSync(updateMessageBlob);
  45.       readCount = md5Stream.readSync(dataBuff)
  46.     }
  47.     let mdResult = md.digestSync();
  48.     md5Stream.closeSync()
  49.     return mdResult.data
  50.   }
  51.   public static uint8ArrayToHexStr(data: Uint8Array): string {
  52.     let hexString = '';
  53.     let i: number;
  54.     for (i = 0; i < data.length; i++) {
  55.       let char = ('00' + data[i].toString(16)).slice(-2);
  56.       hexString += char;
  57.     }
  58.     return hexString;
  59.   }
  60.   public static checkFileMD5(srcFilePath: string, fileMd5Code: string) {
  61.     let file = fs.openSync(srcFilePath, fs.OpenMode.READ_ONLY)
  62.     // 校验文件完整性
  63.     let md5Code = DownloadUiApp.uint8ArrayToHexStr(DownloadUiApp.computeMD5(file.fd)).toUpperCase()
  64.     Logger.info('md5Code:' + md5Code)
  65.     return md5Code === fileMd5Code
  66.   }
  67.   public static async downloadUiApp(
  68.     downUrl: string,
  69.     fileName: string,
  70.     fileMd5Code: string,
  71.     callback: DownloadCallback
  72.   ) {
  73.     if (!downUrl.startsWith("http://") && !downUrl.startsWith("https://")) {
  74.       callback.onError()
  75.       Logger.info('uiapp  请传入正确的请求URL!')
  76.       return
  77.     }
  78.     try {
  79.       // 获取应用文件路径
  80.       const localCacheDir = DownloadUiApp.getLocalCacheDir();
  81.       let srcFilePath = `${localCacheDir}/${fileName}`
  82.       await DownloadUiApp.checkAndRemoveFile(srcFilePath);
  83.       let context = getContext() as common.UIAbilityContext;
  84.       request.downloadFile(context, {
  85.         url: downUrl,
  86.         filePath: srcFilePath,
  87.         background: false,
  88.         enableMetered: true,
  89.         enableRoaming: true
  90.       })
  91.         .then((downloadTask: request.DownloadTask) => {
  92.           downloadTask.on('complete', () => {
  93.             let isComplete = DownloadUiApp.checkFileMD5(srcFilePath, fileMd5Code)
  94.             if (isComplete) {
  95.               Logger.info('uiapp  下载uiapp 成功!')
  96.               callback.onSuccess(srcFilePath)
  97.             } else {
  98.               fs.unlinkSync(srcFilePath)
  99.               callback.onError('MD5_error')
  100.             }
  101.           })
  102.           downloadTask.on('fail', () => {
  103.             Logger.info('uiapp  下载uiapp失败')
  104.             callback.onError('down_fail')
  105.           });
  106.         }).catch((err: BusinessError) => {
  107.         Logger.info('uiapp  下载uiapp异常')
  108.         callback.onError('down_error')
  109.       });
  110.     } catch (error) {
  111.       let err: BusinessError = error as BusinessError;
  112.       Logger.info('uiapp  打开下载过程失败')
  113.       callback.onError('down_open_error')
  114.     }
  115.   }
  116. }
  117. /**
  118. * 下载文件的返回结果
  119. */
  120. interface DownloadCallback {
  121.   // MD5_error MD5校验失败
  122.   onError: (code?: string) => void;
  123.   onStart?: () => void;
  124.   inProgress?: (progress: number, total: number) => void;
  125.   onSuccess: (srcFilePath: string) => void;
  126. }
  127. 运行在线拉取的uiapp包
复制代码

  1. /**
  2. *  更新 uiapp 版本信息
  3. */
  4. function updateUiAppFile(appId: string, newSrcFilePath: string) {
  5.   let oldSrcFilePath = preferenceUtil.getPreference(appId, '') as string
  6.   if (oldSrcFilePath && fs.accessSync(oldSrcFilePath)) {
  7.     fs.unlinkSync(oldSrcFilePath)
  8.     Logger.info('删除 uiapp 老版本地址' + oldSrcFilePath)
  9.   }
  10.   if (oldSrcFilePath !== newSrcFilePath) {
  11.     preferenceUtil.putPreference(appId, newSrcFilePath)
  12.     Logger.info('更新 uiapp 新版本地址' + newSrcFilePath)
  13.   }
  14. }
  15. function readLocalUiAppToSandbox(appId: string): Promise<string> {
  16.   return new Promise(async (resolve) => {
  17.     try {
  18.       Logger.info(`读取本地文件 ${appId}.wgt`)
  19.       let context = getContext() as common.UIAbilityContext;
  20.       let val: Uint8Array = context.resourceManager.getRawFileContentSync(`${appId}.wgt`);
  21.       let localCacheDir: string = DownloadUiApp.getLocalCacheDir()
  22.       //目录不存在,则先创建存放小程序目录
  23.       if (!fs.accessSync(localCacheDir, fs.AccessModeType.EXIST)) {
  24.         await fs.mkdir(localCacheDir, true)
  25.       }
  26.       Logger.info("小程序 pathDir: " + localCacheDir);
  27.       // 待拷贝文件沙箱路径
  28.       let filePath: string = `${localCacheDir}/${appId}.wgt`;
  29.       // 若文件不存在,则创建文件。
  30.       let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  31.       let writeLen = fs.writeSync(file.fd, val.buffer as ArrayBuffer);
  32.       fs.closeSync(file);
  33.       Logger.info('复制到沙箱路径', filePath)
  34.       resolve(filePath)
  35.     } catch (error) {
  36.       Logger.info('读取本地文件错误')
  37.       resolve('')
  38.     }
  39.   })
  40. }
  41. // 小程序运行实例集合
  42. let uniMPInstanceCollections: Record<string, ESObject> = {}
  43. function uniMenuItemsClick(code: string, uniMPInstance: ESObject) {
  44.   Logger.info('menuItemClick::' + code)
  45.   switch (code) {
  46.     case CommonConstants.UNI_BACKEND_CODE:
  47.       uniMPInstance.hide()
  48.       break;
  49.     case CommonConstants.UNI_CLOSE_CODE:
  50.       //  关闭小程序
  51.       uniMPInstance.close()
  52.       uniMPInstance = null
  53.       break;
  54.   }
  55. }
  56. /**
  57. *  调起 uiapp
  58. */
  59. async function loadUiApp(appId: string, redirectPath?: string) {
  60.   //进度加载类弹出框
  61.   const dialogId = DialogHelper.showLoadingDialog({
  62.     loadType: SpinType.spinP,
  63.     loadColor: Color.White,
  64.     loadSize: 30,
  65.     backgroundColor: '#BB000000',
  66.     content: "加载中…",
  67.     fontSize: 15,
  68.     autoCancel: true
  69.   })
  70.   try {
  71.     let uiappInfo = await checkUiAppStatus(appId)
  72.     let uiAppDirPath = uiappInfo.uiAppDirPath
  73.     if (uiappInfo.code === 'version_error' || true) {
  74.       let localCacheDir: string = DownloadUiApp.getLocalCacheDir()
  75.       let srcFilePath = `${localCacheDir}/${appId}`
  76.       if (!fs.accessSync(srcFilePath)) {
  77.         let sandboxFilePath = await readLocalUiAppToSandbox(appId)
  78.         if (sandboxFilePath) {
  79.           uiAppDirPath = sandboxFilePath
  80.         } else {
  81.           promptAction.showToast({
  82.             message: '获取版本信息异常'
  83.           })
  84.           Logger.info('version_error return')
  85.           return
  86.         }
  87.       }
  88.     }
  89.     Logger.info(`完成检查动作  获得地址 :: ${JSON.stringify(uiappInfo)}`)
  90.     DialogHelper.closeDialog(dialogId)
  91.     // 已经存在,并且运行中直接打开小程序
  92.     if (uniMPInstanceCollections[appId] && uniMPInstanceCollections[appId].isRunning()) {
  93.       Logger.info('小程序 uniMPInstance: show')
  94.       uniMPInstanceCollections[appId].show()
  95.     } else {
  96.       // 小程序没执行或者已经被关闭掉了的重新运行
  97.       releaseWgtToRunPath(appId, uiAppDirPath, (code) => {
  98.         Logger.info('小程序::  getAppVersionInfo' + JSON.stringify(getAppVersionInfo(appId)))
  99.         if (code === CommonConstants.UIAPP_RUN_SUCCESS) {
  100.           updateUiAppFile(appId, uiAppDirPath)
  101.           uniMPInstanceCollections[appId] = openUniMP(appId, {
  102.             redirectPath: redirectPath || '',
  103.             capsuleMenuActionSheetItems: [
  104.               {
  105.                 id: CommonConstants.UNI_BACKEND_CODE,
  106.                 title: '将小程序隐藏到后台'
  107.               },
  108.               {
  109.                 id: CommonConstants.UNI_CLOSE_CODE,
  110.                 title: '关闭小程序'
  111.               }
  112.             ]
  113.           });
  114.           uniMPInstanceCollections[appId].capsuleCloseButtonClick = () => {
  115.             uniMenuItemsClick(CommonConstants.UNI_CLOSE_CODE, uniMPInstanceCollections[appId])
  116.           }
  117.           uniMPInstanceCollections[appId].on('menuItemClick', (id: string) => {
  118.             uniMenuItemsClick(id, uniMPInstanceCollections[appId])
  119.           })
  120.         }
  121.       })
  122.     }
  123.    
  124.   } catch (error) {
  125.     DialogHelper.closeDialog(dialogId)
  126.     let code = (error as BusinessError).code;
  127.     let message = (error as BusinessError).message;
  128.     Logger.info(`执行打开小程序失败 code: ${code}  message: ${message}`)
  129.   }
  130. }
复制代码
注册模块提供给uiapp小程序使用
  1. /**
  2. *  注册 BGYUniCommonNativeModule 模块,供uiapp小程序调用
  3. */
  4. registerModule('BGYUniCommonNativeModule', {
  5.   getUserInfo(data: object, callback: Function) {
  6.     let userInfoData = preferenceUtil.getPreference('userInfoData', '') as string
  7.     Logger.info('小程序 getUserInfo' + userInfoData)
  8.     callback && callback({
  9.       code: 200,
  10.       data: JSONUtil.isJSONStr(userInfoData) && JSONUtil.jsonToBean(userInfoData) || null
  11.     })
  12.   }
  13. })
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

温锦文欧普厨电及净水器总代理

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表