Files

413 lines
12 KiB
Plaintext
Raw Permalink Normal View History

---
description: "热更新系统开发规范"
globs: ["src/hotupdate/**/*.ts"]
alwaysApply: false
type: "system-module"
---
# 热更新系统开发规范
## 热更新架构设计
### 管理器单例模式
```typescript
export class HotUpdateManager {
private static _instance: HotUpdateManager;
/** 获取单例实例 */
public static getInstance(): HotUpdateManager {
if (!this._instance) {
this._instance = new HotUpdateManager();
}
return this._instance;
}
/** 禁用直接构造 */
private constructor() {}
/** 配置属性 */
public manifestUrl: string = "";
public versionUrl: string = "";
/** 初始化热更新 */
public init(manifestUrl: string, versionUrl: string): void {
this.manifestUrl = manifestUrl;
this.versionUrl = versionUrl;
}
}
```
### 热更新状态码定义
```typescript
export enum HotUpdateCode {
/** 成功 */
Succeed = 0,
/** 已是最新版本 */
LatestVersion = 1,
/** 检查更新失败 */
CheckFailed = 2,
/** 下载失败 */
DownloadFailed = 3,
/** 解压失败 */
UnzipFailed = 4,
/** 网络错误 */
NetworkError = 5,
/** 空间不足 */
NoSpace = 6,
/** 未知错误 */
UnknownError = 7
}
```
## Promise 结果模式
### 统一结果接口
```typescript
export interface IPromiseResult {
/** 状态码 */
code: HotUpdateCode;
/** 消息描述 */
message: string;
/** 扩展数据 */
data?: any;
}
```
### 热更新配置接口
```typescript
export interface IHotUpdateConfig {
/** 版本号 */
version: string;
/** 远程manifest文件URL */
remoteManifestUrl: string;
/** 远程version文件URL */
remoteVersionUrl: string;
/** 资源包URL */
packageUrl: string;
/** 资源文件列表 */
assets?: { [key: string]: any };
/** 搜索路径 */
searchPaths?: string[];
}
```
## 热更新核心实现
### HotUpdate 核心类
```typescript
export class HotUpdate {
private _am: jsb.AssetsManager;
private _updating: boolean = false;
constructor(manifestUrl: string) {
// 初始化 AssetsManager
this._am = new jsb.AssetsManager(manifestUrl, jsb.fileUtils.getWritablePath() + 'remote-assets');
this._am.setEventCallback(this.onUpdateEvent.bind(this));
this._am.setVerifyCallback(this.onVerifyCallback.bind(this));
}
/**
* 检查更新
* @returns Promise<IPromiseResult>
*/
public checkUpdate(): Promise<IPromiseResult> {
return new Promise((resolve) => {
if (this._updating) {
resolve({ code: HotUpdateCode.UnknownError, message: "正在更新中" });
return;
}
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
resolve({ code: HotUpdateCode.CheckFailed, message: "本地manifest加载失败" });
return;
}
this._am.setEventCallback((event) => {
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
resolve({ code: HotUpdateCode.CheckFailed, message: "本地manifest不存在" });
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
resolve({ code: HotUpdateCode.NetworkError, message: "下载manifest失败" });
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本" });
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
resolve({ code: HotUpdateCode.Succeed, message: "发现新版本" });
break;
}
});
this._am.checkUpdate();
});
}
/**
* 执行热更新
* @returns Promise<IPromiseResult>
*/
public hotUpdate(): Promise<IPromiseResult> {
return new Promise((resolve) => {
if (this._updating) {
resolve({ code: HotUpdateCode.UnknownError, message: "正在更新中" });
return;
}
this._updating = true;
this._am.setEventCallback((event) => {
switch (event.getEventCode()) {
case jsb.EventAssetsManager.UPDATE_FINISHED:
this._updating = false;
resolve({ code: HotUpdateCode.Succeed, message: "更新完成" });
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
this._updating = false;
resolve({ code: HotUpdateCode.DownloadFailed, message: "更新失败" });
break;
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
this._updating = false;
resolve({ code: HotUpdateCode.UnzipFailed, message: "解压失败" });
break;
}
});
this._am.update();
});
}
/**
* 事件回调处理
*/
private onUpdateEvent(event: jsb.EventAssetsManager): void {
const code = event.getEventCode();
debug(`热更新事件: ${code}`);
switch (code) {
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
const progress = event.getPercent();
debug(`更新进度: ${progress}%`);
break;
case jsb.EventAssetsManager.ASSET_UPDATED:
const assetId = event.getAssetId();
debug(`资源更新: ${assetId}`);
break;
}
}
/**
* 验证回调
*/
private onVerifyCallback(path: string, asset: any): boolean {
// 资源验证逻辑
return true;
}
}
```
## manifest 管理
### 本地 manifest 刷新
```typescript
/**
* 替换 project.manifest 中的内容并刷新本地manifest
*/
private refreshLocalManifest(manifest: IHotUpdateConfig, versionManifest: IHotUpdateConfig): Promise<IPromiseResult> {
return new Promise((resolve) => {
// 版本比较
if (Utils.compareVersion(manifest.version, versionManifest.version) >= 0) {
resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本" });
return;
}
// 更新 manifest 配置
manifest.remoteManifestUrl = Utils.addUrlParam(versionManifest.remoteManifestUrl, "timeStamp", `${Time.now()}`);
manifest.remoteVersionUrl = Utils.addUrlParam(versionManifest.remoteVersionUrl, "timeStamp", `${Time.now()}`);
manifest.packageUrl = versionManifest.packageUrl;
// 计算 manifest 根目录
let manifestRoot = "";
let manifestUrl = HotUpdateManager.getInstance().manifestUrl;
let found = manifestUrl.lastIndexOf("/");
if (found === -1) {
found = manifestUrl.lastIndexOf("\\");
}
if (found !== -1) {
manifestRoot = manifestUrl.substring(0, found + 1);
}
// 解析并设置本地 manifest
this._am.getLocalManifest().parseJSONString(JSON.stringify(manifest), manifestRoot);
resolve({ code: HotUpdateCode.Succeed, message: "更新热更新配置成功" });
});
}
```
## 版本比较工具
### 版本号比较函数
```typescript
export class Utils {
/**
* 版本号比较
* @param versionA 版本A
* @param versionB 版本B
* @returns 0: 相等, 1: A > B, -1: A < B
*/
public static compareVersion(versionA: string, versionB: string): number {
const a = versionA.split('.');
const b = versionB.split('.');
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const numA = parseInt(a[i] || '0', 10);
const numB = parseInt(b[i] || '0', 10);
if (numA > numB) return 1;
if (numA < numB) return -1;
}
return 0;
}
/**
* 给URL添加参数
*/
public static addUrlParam(url: string, key: string, value: string): string {
const separator = url.indexOf('?') !== -1 ? '&' : '?';
return `${url}${separator}${key}=${encodeURIComponent(value)}`;
}
}
```
## 进度监控
### 更新进度回调
```typescript
export interface HotUpdateProgress {
/** 当前进度百分比 (0-100) */
percent: number;
/** 已下载字节数 */
downloadedBytes: number;
/** 总字节数 */
totalBytes: number;
/** 当前下载的文件 */
currentFile?: string;
}
export interface HotUpdateCallbacks {
/** 进度回调 */
onProgress?: (progress: HotUpdateProgress) => void;
/** 完成回调 */
onComplete?: (result: IPromiseResult) => void;
/** 错误回调 */
onError?: (error: IPromiseResult) => void;
}
export class HotUpdate {
public updateWithCallbacks(callbacks: HotUpdateCallbacks): void {
this._am.setEventCallback((event) => {
switch (event.getEventCode()) {
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
callbacks.onProgress?.({
percent: event.getPercent(),
downloadedBytes: event.getDownloadedBytes(),
totalBytes: event.getTotalBytes()
});
break;
case jsb.EventAssetsManager.UPDATE_FINISHED:
callbacks.onComplete?.({ code: HotUpdateCode.Succeed, message: "更新完成" });
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
callbacks.onError?.({ code: HotUpdateCode.DownloadFailed, message: "更新失败" });
break;
}
});
this._am.update();
}
}
```
## 错误处理和重试
### 重试机制
```typescript
export class HotUpdate {
private _retryCount: number = 0;
private readonly _maxRetryCount: number = 3;
/**
* 带重试的热更新
*/
public async hotUpdateWithRetry(): Promise<IPromiseResult> {
for (let i = 0; i < this._maxRetryCount; i++) {
try {
const result = await this.hotUpdate();
if (result.code === HotUpdateCode.Succeed) {
return result;
}
// 网络错误可以重试
if (result.code === HotUpdateCode.NetworkError && i < this._maxRetryCount - 1) {
warn(`热更新失败,准备重试 (${i + 1}/${this._maxRetryCount})`);
await this.delay(1000 * (i + 1)); // 递增延迟
continue;
}
return result;
} catch (error) {
error('热更新异常', error);
if (i === this._maxRetryCount - 1) {
return { code: HotUpdateCode.UnknownError, message: `重试${this._maxRetryCount}次后仍然失败` };
}
}
}
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
```
## 热更新最佳实践
### 1. 版本管理
- 使用语义化版本号 (major.minor.patch)
- 提供版本比较和检查功能
- 记录版本更新历史
### 2. 网络处理
- 实现重试机制处理网络不稳定
- 添加超时控制
- 支持断点续传
### 3. 用户体验
- 提供详细的进度反馈
- 支持后台下载
- 提供更新取消选项
### 4. 错误恢复
- 验证下载文件完整性
- 支持回滚到上一版本
- 提供修复工具清理损坏文件
### 5. 安全性
- 验证manifest签名
- 检查文件哈希值
- 使用HTTPS传输