温锦文欧普厨电及净水器总代理 发表于 2025-4-10 07:41:26

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

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

uniMP小程序

#初始化 sdk 全局环境

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

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

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

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

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

interface ICapsuleStyle {
backgroundColor?: string
textColor?: string
highlightColor?: string
borderColor?: string
}

interface ICapsuleMenuActionSheetItem {
id: string
title: string
}

interface IOpenUniMPConfig {
showCapsuleButton?: boolean
capsuleMenuActionSheetItems?: ICapsuleMenuActionSheetItem[]
capsuleButtonStyle?: ICapsuleStyle
redirectPath?: string
extraData?: Object // 传递给小程序的额外数据,小程序中onLaunch,uni.getLaunchOptionsSync可以获取
}

export interface IUniMP {
hide(): void

show(): void

close(): void

sendUniMPEvent: (event: string, data: ESObject) => void
on(event: 'uniMPEvent', callback: (event: string, data: ESObject, notify: (...args: ESObject[]) => void) => void): void
on(event: 'menuItemClick', callback: (id: string) => void): void
on(event: 'close' | 'show' | 'hide', callback: () => void): void
off(name: string, callback: Function): void
}

function openUniMP(appID: string, config?: IOpenUniMPConfig): IUniMP
复制代码
#IUniMP实例下的API

#隐藏当前小程序应用

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

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

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

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

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

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

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

off(name: string, callback: Function): void
[*]修改鸿蒙项目根目次文件 oh-package.json5 的依赖 "@dcloudio/uni-app-runtime": "版本号",如下图所示 查看最新版本号https://i-blog.csdnimg.cn/direct/f785ca361e97485e869e6ef45463daf1.png
[*]点击右上角 Sync Now,并等候 Sync 结束
[*]首先需要初始化uiapp SDK包 ,初始化如下 在EntryAbility文件入口如下
import { init } from '@dcloudio/uni-app-runtime' init(this, windowStage, {
debug: true
}) <img alt=""src="https://i-blog.csdnimg.cn/direct/1eb1fece6be84fdf85cbfa2791a61251.png"/>
[*] 将生成的wgt包拷贝到 entry/src/main/resources/resfile 目次下,如下图所示

https://i-blog.csdnimg.cn/img_convert/fdffc7f3c035a8dc21b4b460d419d909.png
 4.再通过 releaseWgtToRunPath 函数释放 wgt 包到运行目次,最后通过 openUniMP 函数打开小程序,代码如下https://i-blog.csdnimg.cn/direct/01bf32f87c9f42448a4beb6f7081328e.png
在线拉取运行:
/**
*下载 uiapp 包
*/
function downloadUiApp(fileName: string, downUrl: string, md5Code: string): Promise<string> {
return new Promise((resolve) => {
    DownloadUiApp.downloadUiApp(
      downUrl,
      fileName,
      md5Code,
      {
      onError: (code: string = '') => {
          // 下载错误
          if (code === 'MD5_error') {
            promptAction.showToast({
            message: '文件损坏,请重新点击应用'
            })
          }
          resolve(code)
      },
      onSuccess: (srcFilePath: string) => {
          // 下载并保存成功
          Logger.info('下载文件完成地址为', srcFilePath)
          resolve(srcFilePath)
      }
      })
})
} 下载文件Class

import { common } from '@kit.AbilityKit';
import fs, { ReadOptions, WriteOptions } from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';
import Logger from './Logger'
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
Logger.prefix = 'uiapp'

export class DownloadUiApp {
/**
   * 获取应用沙箱存储目录 统一一下,方便APP设置页面删除
   * @returns
   */
static getLocalCacheDir(): string {
    let cache_dir_name = "uiapps";
    let cache_dir = getContext().filesDir + "/" + cache_dir_name;
    console.info(`createCacheDir cache_dir: ${cache_dir}`);
    let existAppCacheDir = fileIo.accessSync(cache_dir, fileIo.AccessModeType.EXIST)
    if (!existAppCacheDir) {
      fileIo.mkdirSync(cache_dir, true);
    }
    return cache_dir
}

static async checkAndRemoveFile(path: string) {
    const exist = await fs.access(path);
    if (exist) {
      await fs.unlink(path);
    }
}

//计算文件的MD5码
public static computeMD5(file: string | number): Uint8Array {
    let md5Stream: fs.Stream

    if (typeof file === 'string') {
      md5Stream = fs.createStreamSync(file, "r");
    } else {
      md5Stream = fs.fdopenStreamSync(file, "r")
    }

    let dataBuff: ArrayBuffer = new ArrayBuffer(4096)
    let mdAlgName = "MD5";
    let md = cryptoFramework.createMd(mdAlgName);
    let readCount = md5Stream.readSync(dataBuff)
    while (readCount > 0) {
      let messageData = new Uint8Array(dataBuff.slice(0, readCount));
      let updateMessageBlob: cryptoFramework.DataBlob = { data: messageData };
      md.updateSync(updateMessageBlob);
      readCount = md5Stream.readSync(dataBuff)
    }
    let mdResult = md.digestSync();

    md5Stream.closeSync()
    return mdResult.data
}

public static uint8ArrayToHexStr(data: Uint8Array): string {
    let hexString = '';
    let i: number;
    for (i = 0; i < data.length; i++) {
      let char = ('00' + data.toString(16)).slice(-2);
      hexString += char;
    }
    return hexString;
}

public static checkFileMD5(srcFilePath: string, fileMd5Code: string) {
    let file = fs.openSync(srcFilePath, fs.OpenMode.READ_ONLY)
    // 校验文件完整性
    let md5Code = DownloadUiApp.uint8ArrayToHexStr(DownloadUiApp.computeMD5(file.fd)).toUpperCase()
    Logger.info('md5Code:' + md5Code)
    return md5Code === fileMd5Code
}

public static async downloadUiApp(
    downUrl: string,
    fileName: string,
    fileMd5Code: string,
    callback: DownloadCallback
) {
    if (!downUrl.startsWith("http://") && !downUrl.startsWith("https://")) {
      callback.onError()
      Logger.info('uiapp请传入正确的请求URL!')
      return
    }
    try {
      // 获取应用文件路径
      const localCacheDir = DownloadUiApp.getLocalCacheDir();
      let srcFilePath = `${localCacheDir}/${fileName}`
      await DownloadUiApp.checkAndRemoveFile(srcFilePath);
      let context = getContext() as common.UIAbilityContext;

      request.downloadFile(context, {
      url: downUrl,
      filePath: srcFilePath,
      background: false,
      enableMetered: true,
      enableRoaming: true
      })
      .then((downloadTask: request.DownloadTask) => {
          downloadTask.on('complete', () => {
            let isComplete = DownloadUiApp.checkFileMD5(srcFilePath, fileMd5Code)
            if (isComplete) {
            Logger.info('uiapp下载uiapp 成功!')
            callback.onSuccess(srcFilePath)
            } else {
            fs.unlinkSync(srcFilePath)
            callback.onError('MD5_error')
            }
          })
          downloadTask.on('fail', () => {
            Logger.info('uiapp下载uiapp失败')
            callback.onError('down_fail')
          });
      }).catch((err: BusinessError) => {
      Logger.info('uiapp下载uiapp异常')
      callback.onError('down_error')
      });
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      Logger.info('uiapp打开下载过程失败')
      callback.onError('down_open_error')
    }

}
}

/**
* 下载文件的返回结果
*/
interface DownloadCallback {
// MD5_error MD5校验失败
onError: (code?: string) => void;
onStart?: () => void;
inProgress?: (progress: number, total: number) => void;
onSuccess: (srcFilePath: string) => void;
}
运行在线拉取的uiapp包
/**
*更新 uiapp 版本信息
*/

function updateUiAppFile(appId: string, newSrcFilePath: string) {
let oldSrcFilePath = preferenceUtil.getPreference(appId, '') as string
if (oldSrcFilePath && fs.accessSync(oldSrcFilePath)) {
    fs.unlinkSync(oldSrcFilePath)
    Logger.info('删除 uiapp 老版本地址' + oldSrcFilePath)
}
if (oldSrcFilePath !== newSrcFilePath) {
    preferenceUtil.putPreference(appId, newSrcFilePath)
    Logger.info('更新 uiapp 新版本地址' + newSrcFilePath)
}
}

function readLocalUiAppToSandbox(appId: string): Promise<string> {
return new Promise(async (resolve) => {
    try {
      Logger.info(`读取本地文件 ${appId}.wgt`)
      let context = getContext() as common.UIAbilityContext;
      let val: Uint8Array = context.resourceManager.getRawFileContentSync(`${appId}.wgt`);
      let localCacheDir: string = DownloadUiApp.getLocalCacheDir()
      //目录不存在,则先创建存放小程序目录
      if (!fs.accessSync(localCacheDir, fs.AccessModeType.EXIST)) {
      await fs.mkdir(localCacheDir, true)
      }
      Logger.info("小程序 pathDir: " + localCacheDir);
      // 待拷贝文件沙箱路径
      let filePath: string = `${localCacheDir}/${appId}.wgt`;
      // 若文件不存在,则创建文件。
      let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
      let writeLen = fs.writeSync(file.fd, val.buffer as ArrayBuffer);
      fs.closeSync(file);
      Logger.info('复制到沙箱路径', filePath)
      resolve(filePath)
    } catch (error) {
      Logger.info('读取本地文件错误')
      resolve('')
    }
})
}

// 小程序运行实例集合
let uniMPInstanceCollections: Record<string, ESObject> = {}

function uniMenuItemsClick(code: string, uniMPInstance: ESObject) {
Logger.info('menuItemClick::' + code)
switch (code) {
    case CommonConstants.UNI_BACKEND_CODE:
      uniMPInstance.hide()
      break;
    case CommonConstants.UNI_CLOSE_CODE:
      //关闭小程序
      uniMPInstance.close()
      uniMPInstance = null
      break;
}
}

/**
*调起 uiapp
*/
async function loadUiApp(appId: string, redirectPath?: string) {
//进度加载类弹出框
const dialogId = DialogHelper.showLoadingDialog({
    loadType: SpinType.spinP,
    loadColor: Color.White,
    loadSize: 30,
    backgroundColor: '#BB000000',
    content: "加载中…",
    fontSize: 15,
    autoCancel: true
})
try {
    let uiappInfo = await checkUiAppStatus(appId)
    let uiAppDirPath = uiappInfo.uiAppDirPath
    if (uiappInfo.code === 'version_error' || true) {
      let localCacheDir: string = DownloadUiApp.getLocalCacheDir()
      let srcFilePath = `${localCacheDir}/${appId}`
      if (!fs.accessSync(srcFilePath)) {
      let sandboxFilePath = await readLocalUiAppToSandbox(appId)
      if (sandboxFilePath) {
          uiAppDirPath = sandboxFilePath
      } else {
          promptAction.showToast({
            message: '获取版本信息异常'
          })
          Logger.info('version_error return')
          return
      }
      }
    }
    Logger.info(`完成检查动作获得地址 :: ${JSON.stringify(uiappInfo)}`)

    DialogHelper.closeDialog(dialogId)

    // 已经存在,并且运行中直接打开小程序
    if (uniMPInstanceCollections && uniMPInstanceCollections.isRunning()) {
      Logger.info('小程序 uniMPInstance: show')
      uniMPInstanceCollections.show()
    } else {
      // 小程序没执行或者已经被关闭掉了的重新运行
      releaseWgtToRunPath(appId, uiAppDirPath, (code) => {
      Logger.info('小程序::getAppVersionInfo' + JSON.stringify(getAppVersionInfo(appId)))
      if (code === CommonConstants.UIAPP_RUN_SUCCESS) {
          updateUiAppFile(appId, uiAppDirPath)
          uniMPInstanceCollections = openUniMP(appId, {
            redirectPath: redirectPath || '',
            capsuleMenuActionSheetItems: [
            {
                id: CommonConstants.UNI_BACKEND_CODE,
                title: '将小程序隐藏到后台'
            },
            {
                id: CommonConstants.UNI_CLOSE_CODE,
                title: '关闭小程序'
            }
            ]
          });

          uniMPInstanceCollections.capsuleCloseButtonClick = () => {
            uniMenuItemsClick(CommonConstants.UNI_CLOSE_CODE, uniMPInstanceCollections)
          }
          uniMPInstanceCollections.on('menuItemClick', (id: string) => {
            uniMenuItemsClick(id, uniMPInstanceCollections)
          })

      }
      })
    }
   
} catch (error) {
    DialogHelper.closeDialog(dialogId)
    let code = (error as BusinessError).code;
    let message = (error as BusinessError).message;
    Logger.info(`执行打开小程序失败 code: ${code}message: ${message}`)
}
} 注册模块提供给uiapp小程序使用
/**
*注册 BGYUniCommonNativeModule 模块,供uiapp小程序调用
*/

registerModule('BGYUniCommonNativeModule', {
getUserInfo(data: object, callback: Function) {
    let userInfoData = preferenceUtil.getPreference('userInfoData', '') as string
    Logger.info('小程序 getUserInfo' + userInfoData)
    callback && callback({
      code: 200,
      data: JSONUtil.isJSONStr(userInfoData) && JSONUtil.jsonToBean(userInfoData) || null
    })
}
})
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙uiapp支持读取当地和在线拉取运行