鸿蒙应用更新:updateManager 与 OTA 安装实现详解

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

鸿蒙开发中,通过 updateManager 模块实现的更新功能会在下载完成后自动触发安装流程,这与自定义 OTA 方式不同。

一、updateManager 的安装流程(自动安装)

1. updateManager 更新流程

2. 关键特性

  1. 无需额外权限:不需要 INSTALL_BUNDLE 权限
  2. 自动处理下载和安装:系统完成所有步骤
  3. 跳转到应用市场:安装过程在应用市场进行
  4. 标准化流程:用户体验统一

3. 示例代码

import updateManager from '@ohos.updateManager';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct UpdatePage {
  @State message: string = '点击检查更新';
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

  // 检查更新
  async checkUpdate() {
    try {
      this.message = '检查更新中...';
      const result = await updateManager.checkAppUpdate(this.context);
      
      if (result.hasUpdate) {
        this.message = `发现新版本 ${result.newVersion}`;
        // 显示系统更新弹窗
        await updateManager.showUpdateDialog(this.context);
      } else {
        this.message = '已是最新版本';
      }
    } catch (error) {
      this.message = `更新失败: ${error.message}`;
      console.error('检查更新出错:', error);
    }
  }

  build() {
    Column() {
      Text(this.message)
        .fontSize(20)
        .margin(20)
      
      Button('检查更新')
        .onClick(() => this.checkUpdate())
        .width('60%')
        .height(50)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

二、自定义 OTA 安装方式(手动安装)

与传统 OTA 方式的对比

特性updateManager自定义 OTA
安装权限无需需要 INSTALL_BUNDLE 权限
安装位置应用市场应用内直接安装
用户交互跳转到应用市场应用内完成所有流程
流程控制系统控制开发者完全控制
适用场景应用市场分发企业内部分发/热更新

自定义 OTA 安装实现

import installer from '@ohos.bundle.installer';
import fs from '@ohos.file.fs';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';

class CustomOtaInstaller {
  async installHap(hapPath: string) {
    const context = getContext();
    
    // 1. 检查文件是否存在
    if (!fs.accessSync(hapPath)) {
      throw new Error('HAP文件不存在');
    }
    
    // 2. 请求安装权限
    if (!await this.requestInstallPermission()) {
      throw new Error('用户拒绝安装权限');
    }
    
    // 3. 执行安装
    const bundleInstaller = await installer.getBundleInstaller();
    const installParam = {
      installFlag: installer.InstallFlag.REPLACE_EXISTING,
      userId: 100
    };
    
    return new Promise<void>((resolve, reject) => {
      bundleInstaller.install(hapPath, installParam, (err) => {
        if (err) {
          reject(new Error(`安装失败 [${err.code}]: ${err.message}`));
        } else {
          resolve();
        }
      });
    });
  }

  // 请求安装权限
  private async requestInstallPermission(): Promise<boolean> {
    const atManager = abilityAccessCtrl.createAtManager();
    try {
      const grantStatus = await atManager.requestPermissionsFromUser(
        getContext(), 
        ['ohos.permission.INSTALL_BUNDLE']
      );
      return grantStatus.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
    } catch (err) {
      console.error('权限请求失败:', err);
      return false;
    }
  }
}

// 使用示例
@Component
struct OtaInstallDemo {
  private installer = new CustomOtaInstaller();
  
  async installUpdate() {
    const hapPath = '/data/storage/el2/base/cache/update.hap';
    
    try {
      await this.installer.installHap(hapPath);
      console.log('安装成功!');
      // 提示用户重启应用
      AlertDialog.show({ message: '安装成功,请重启应用' });
    } catch (error) {
      console.error('安装失败:', error.message);
      AlertDialog.show({ message: `安装失败: ${error.message}` });
    }
  }
  
  build() {
    Button('安装更新')
      .onClick(() => this.installUpdate())
  }
}

三、混合解决示例(推荐)

结合两种方式的优势:

import updateManager from '@ohos.updateManager';
import installer from '@ohos.bundle.installer';

class SmartUpdater {
  async smartUpdate() {
    const context = getContext();
    
    try {
      // 1. 首先尝试使用系统更新
      const hasSystemUpdate = await updateManager.checkAppUpdate(context);
      if (hasSystemUpdate) {
        await updateManager.showUpdateDialog(context);
        return;
      }
      
      // 2. 系统无更新,检查自定义更新
      const hasCustomUpdate = await this.checkCustomUpdate();
      if (hasCustomUpdate) {
        await this.installCustomUpdate();
      }
    } catch (error) {
      console.error('智能更新失败:', error);
    }
  }
  
  private async checkCustomUpdate(): Promise<boolean> {
    // 自定义更新检查逻辑
    const serverUrl = 'https://your-update-server.com/check-update';
    const response = await fetch(serverUrl);
    const data = await response.json();
    return data.hasUpdate;
  }
  
  private async installCustomUpdate() {
    // 下载并安装自定义更新包
    const hapPath = await this.downloadUpdatePackage();
    await this.installHap(hapPath);
  }
  
  private async installHap(hapPath: string) {
    // 同上文自定义安装逻辑
  }
}

四、两种方式的区别

1. 权限管理

updateManager

// 无需声明安装权限

自定义OTA

// module.json5
"requestPermissions": [
  {
    "name": "ohos.permission.INSTALL_BUNDLE",
    "reason": "用于应用内更新"
  }
]

2. 安装触发方式

updateManager

// 系统自动触发安装
await updateManager.showUpdateDialog(context);

自定义OTA

// 需要手动调用安装
bundleInstaller.install(hapPath, installParam, callback);

3. 错误处理对比

updateManager 错误处理

try {
  await updateManager.showUpdateDialog(context);
} catch (error) {
  // 系统级错误代码
  if (error.code === 18000001) {
    console.error('网络不可用');
  }
}

自定义OTA 错误处理

bundleInstaller.install(hapPath, installParam, (err) => {
  if (err) {
    switch (err.code) {
      case 201: 
        console.error('安装包不存在');
        break;
      case 202:
        console.error('安装包验证失败');
        break;
      // ...其他错误代码
    }
  }
});

五、企业级实现

1. 安全增强方案

class SecureInstaller {
  async safeInstall(hapPath: string) {
    // 1. 验证签名
    const isValid = await this.verifySignature(hapPath);
    if (!isValid) throw new Error('签名验证失败');
    
    // 2. 检查包名匹配
    const packageInfo = await this.extractPackageInfo(hapPath);
    if (packageInfo.bundleName !== getBundleName()) {
      throw new Error('包名不匹配');
    }
    
    // 3. 执行安装
    return this.installHap(hapPath);
  }
  
  private async verifySignature(hapPath: string): Promise<boolean> {
    // 实现签名验证逻辑
    return true;
  }
}

2. 安装后验证

async function verifyAfterInstall() {
  const bundleManager = bundle.getBundleManager();
  const bundleName = getBundleName();
  
  const bundleInfo = await bundleManager.getBundleInfo(bundleName, 0);
  const installedVersion = bundleInfo.versionName;
  
  // 比对安装的版本是否符合预期
  if (installedVersion !== expectedVersion) {
    throw new Error('版本验证失败');
  }
  
  // 检查签名证书
  const certValid = verifyCertificate(bundleInfo.signature);
  if (!certValid) {
    throw new Error('证书验证失败');
  }
}

3. 用户体验优化

@Component
struct UpdateProgressView {
  @State progress: number = 0;
  @State status: string = '准备中';
  
  build() {
    Column() {
      Text(this.status)
        .fontSize(18)
      
      Progress({ value: this.progress, total: 100 })
        .width('80%')
        .margin(20)
        
      if (this.status.includes('失败')) {
        Button('重试')
          .onClick(() => this.startUpdate())
      }
    }
  }
}

总结

  1. updateManager 方式

    • 下载后自动跳转到应用市场安装
    • 无需处理安装权限和细节
    • 标准化流程,用户体验一致
    • 适合通过应用市场分发的标准应用
  2. 自定义 OTA 方式

    • 应用内直接完成安装
    • 需要申请安装权限
    • 完全控制安装流程
    • 适合企业内部分发或特殊场景
  3. 选择建议

    • 优先使用 updateManager 标准更新流程
    • 特殊需求(如企业内部分发)使用自定义 OTA
    • 大型应用可结合两种方式实现混合更新

在实际开发中,推荐使用官方的 updateManager 作为首选方案,除非有特殊需求才考虑自定义 OTA 安装方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值