51 KiB
TSGame HarmonyOS应用开发指南
项目概述
本文档提供了基于HarmonyOS平台开发TSGame棋牌游戏应用的完整技术指南,包括架构设计、开发环境搭建、核心功能实现、JS Bridge接口开发等详细内容。
HarmonyOS 5.0新特性适配
本项目充分利用HarmonyOS 5.0的新特性和性能优化:
1. 增强的WebView引擎
- 性能提升: 利用5.0优化的WebView渲染引擎,提高H5游戏运行性能
- 内存优化: 改进的内存管理机制,减少WebView内存占用
- 启动加速: WebView初始化和页面加载速度显著提升
2. 新的权限管理机制
- 精细化权限: 支持5.0的精细化权限控制
- 动态权限: 改进的运行时权限申请机制
- 隐私保护: 增强的用户隐私保护特性
3. 文件系统API改进
- 沙盒机制: 适配5.0的新沙盒文件访问机制
- 存储优化: 利用改进的文件存储API提高I/O性能
- 安全增强: 文件访问安全性提升
4. 应用启动优化
- 冷启动: 利用5.0的应用预加载机制
- 热启动: 优化的应用恢复机制
- 资源预加载: 智能资源预加载策略
5. 向下兼容性
- API适配层: 建立兼容性适配层支持HarmonyOS 4.0
- 特性检测: 运行时检测系统版本并启用对应特性
- 降级策略: 为不支持的特性提供降级方案
应用基本信息
- 应用名称: 进贤聚友棋牌 (HarmonyOS版)
- Bundle名称:
com.jx.jyhd.harmonyos - 目标API: HarmonyOS API 12+ (支持HarmonyOS 5.0)
- 最低支持: HarmonyOS API 11 (HarmonyOS 4.0)
- 开发语言: ArkTS/TypeScript
- 应用模型: Stage模型
- 项目周期: 8-10周
- 团队规模: 3-5人
1. 程序设计框架
1.1 整体架构设计
┌─────────────────────────────────────────────────────────┐
│ H5游戏前端层 │
│ (HTML5 + CSS3 + JavaScript) │
│ - 游戏逻辑实现 │
│ - UI界面展示 │
│ - 用户交互处理 │
└─────────────────────────────────────────────────────────┘
│
JavaScript Bridge API
│
┌─────────────────────────────────────────────────────────┐
│ HarmonyOS Native层 │
│ │
│ ┌─────────────┬─────────────┬─────────────────────┐ │
│ │ AbilityKit │ WebView组件 │ 系统服务集成 │ │
│ │ 生命周期 │ JS Bridge │ - 位置服务 │ │
│ │ 页面管理 │ 通信管理 │ - 多媒体服务 │ │
│ │ 权限管理 │ 事件处理 │ - 网络服务 │ │
│ └─────────────┴─────────────┴─────────────────────┘ │
│ │
│ ┌─────────────┬─────────────┬─────────────────────┐ │
│ │ 第三方SDK集成│ 数据存储 │ 平台特性适配 │ │
│ │ - 华为地图 │ - 本地存储 │ - 华为账号 │ │
│ │ - 原生音频 │ - 文件管理 │ - 华为分享 │ │
│ │ - 华为分析 │ - 配置管理 │ - HarmonyOS规范 │ │
│ └─────────────┴─────────────┴─────────────────────┘ │
└─────────────────────────────────────────────────────────┘
1.2 双WebView架构设计
TSGame应用采用双WebView架构,实现大厅和子游戏的分离管理:
应用启动流程:
启动App → 加载远程配置 → 下载大厅ZIP包 → 解压大厅资源 → 显示大厅WebView
↓
用户点击子游戏 → 验证子游戏ZIP包 → 下载子游戏ZIP → 解压子游戏资源 → 显示子游戏WebView
1.2.1 表现层 (UI Layer)
// 双WebView UI组件层次结构
EntryAbility
├── MainPage (应用主页面)
│ ├── HallWebView (大厅WebView)
│ │ ├── HallWebComponent (大厅Web组件)
│ │ ├── HallLoadingComponent (大厅加载组件)
│ │ └── HallErrorComponent (大厅错误处理)
│ ├── GameWebView (子游戏WebView)
│ │ ├── GameWebComponent (子游戏Web组件)
│ │ ├── GameLoadingComponent (子游戏加载组件)
│ │ └── GameErrorComponent (子游戏错误处理)
│ ├── NavigationController (WebView切换控制器)
│ ├── DownloadProgressView (下载进度显示)
│ └── GlobalLoadingView (全局加载组件)
└── SettingsPage (设置页面)
├── PermissionSettings (权限设置)
├── AboutPage (关于页面)
└── VersionCheck (版本检查)
1.2.2 业务逻辑层 (Business Layer)
// 核心业务模块 - 双WebView架构
├── HallManager (大厅管理器)
│ ├── HallLauncher (大厅启动器)
│ ├── HallResourceManager (大厅资源管理器)
│ └── HallConfigManager (大厅配置管理器)
├── GameManager (子游戏管理器)
│ ├── GameLauncher (子游戏启动器)
│ ├── GameResourceManager (子游戏资源管理器)
│ ├── GameValidation (子游戏验证器)
│ └── GamePackageManager (子游戏包管理器)
├── WebViewManager (WebView管理器)
│ ├── HallWebViewController (大厅WebView控制器)
│ ├── GameWebViewController (子游戏WebView控制器)
│ └── WebViewSwitcher (WebView切换器)
├── BridgeManager (桥接管理器)
│ ├── JSBridgeService (JS桥接服务)
│ ├── APIDispatcher (API分发器)
│ └── MessageHandler (消息处理器)
├── ResourceManager (资源管理器)
│ ├── ZipDownloader (ZIP下载器)
│ ├── ZipExtractor (ZIP解压器)
│ ├── ResourceValidator (资源验证器)
│ └── CacheManager (缓存管理器)
├── UserManager (用户管理器)
│ ├── AuthService (认证服务)
│ ├── ProfileManager (用户档案管理)
│ └── SessionManager (会话管理)
└── MediaManager (多媒体管理器)
├── AudioService (音频服务)
├── VideoService (视频服务)
└── CameraService (相机服务)
1.2.3 数据访问层 (Data Layer)
// 数据管理模块
├── LocalStorage (本地存储)
│ ├── PreferenceUtil (首选项工具)
│ ├── FileManager (文件管理)
│ └── DatabaseManager (数据库管理)
├── NetworkService (网络服务)
│ ├── HttpClient (HTTP客户端)
│ ├── DownloadManager (下载管理器)
│ └── UploadManager (上传管理器)
└── CacheManager (缓存管理)
├── ImageCache (图片缓存)
├── DataCache (数据缓存)
└── WebCache (Web缓存)
1.3 模块依赖关系
// 双WebView架构模块依赖图
HallManager
├── 依赖 → ResourceManager (ZIP下载解压)
├── 依赖 → ConfigManager (配置管理)
├── 依赖 → WebViewManager (大厅WebView控制)
└── 依赖 → NetworkService (网络服务)
GameManager
├── 依赖 → ResourceManager (子游戏ZIP管理)
├── 依赖 → WebViewManager (子游戏WebView控制)
├── 依赖 → BridgeManager (JS桥接)
└── 依赖 → ValidationService (ZIP包验证)
WebViewManager
├── 依赖 → BridgeManager (JS Bridge注册)
├── 依赖 → MediaManager (多媒体功能)
├── 依赖 → LocationService (定位服务)
└── 依赖 → UserManager (用户管理)
ResourceManager
├── 依赖 → NetworkService (ZIP下载)
├── 依赖 → FileManager (文件解压)
├── 依赖 → CacheManager (资源缓存)
└── 依赖 → ValidationService (完整性验证)
BridgeManager
├── 依赖 → MediaManager (音视频通话)
├── 依赖 → LocationService (定位功能)
├── 依赖 → UserManager (认证登录)
└── 依赖 → SystemService (系统功能)
2. 项目目录结构设计
2.1 标准HarmonyOS项目结构 (双WebView架构)
tsgame_harmonyos/
├── AppScope/ # 应用级配置
│ └── app.json5 # 应用配置文件
├── entry/ # 主模块
│ ├── src/main/
│ │ ├── ets/ # ArkTS源码
│ │ │ ├── entryability/ # Ability入口
│ │ │ │ └── EntryAbility.ts
│ │ │ ├── pages/ # 页面文件
│ │ │ │ ├── MainPage.ets # 主页面(双WebView容器)
│ │ │ │ └── SettingsPage.ets # 设置页面
│ │ │ ├── common/ # 通用工具类
│ │ │ │ ├── constants/ # 常量定义
│ │ │ │ │ ├── AppConstants.ts # 应用常量
│ │ │ │ │ └── WebViewConstants.ts # WebView常量
│ │ │ │ ├── utils/ # 工具类
│ │ │ │ │ ├── ConfigParameterHelper.ts # 配置参数工具
│ │ │ │ │ ├── ZipExtractor.ts # ZIP解压工具
│ │ │ │ │ └── FileUtils.ts # 文件工具
│ │ │ │ └── types/ # 类型定义
│ │ │ │ ├── GameTypes.ts # 游戏类型定义
│ │ │ │ ├── ConfigTypes.ts # 配置类型定义
│ │ │ │ └── WebViewTypes.ts # WebView类型定义
│ │ │ ├── manager/ # 管理器类
│ │ │ │ ├── HallManager.ts # 大厅管理器
│ │ │ │ ├── GameManager.ts # 子游戏管理器
│ │ │ │ ├── WebViewManager.ts # WebView管理器
│ │ │ │ ├── BridgeManager.ts # 桥接管理器
│ │ │ │ ├── ResourceManager.ts # 资源管理器
│ │ │ │ ├── MediaManager.ts # 多媒体管理器
│ │ │ │ ├── UserManager.ts # 用户管理器
│ │ │ │ └── StartupManager.ts # 启动管理器
│ │ │ ├── service/ # 服务类
│ │ │ │ ├── AuthService.ts # 认证服务
│ │ │ │ ├── LocationService.ts # 定位服务
│ │ │ │ ├── NetworkService.ts # 网络服务
│ │ │ │ ├── MediaService.ts # 媒体服务
│ │ │ │ ├── ConfigManager.ts # 配置管理服务
│ │ │ │ └── DownloadService.ts # 下载服务
│ │ │ ├── bridge/ # JS桥接实现
│ │ │ │ ├── JSBridgeService.ts # 桥接核心服务
│ │ │ │ ├── handlers/ # API处理器
│ │ │ │ │ ├── AuthHandler.ts # 认证处理器
│ │ │ │ │ ├── MediaHandler.ts# 媒体处理器
│ │ │ │ │ ├── SystemHandler.ts# 系统处理器
│ │ │ │ │ ├── LocationHandler.ts# 定位处理器
│ │ │ │ │ ├── HallHandler.ts # 大厅处理器
│ │ │ │ │ └── GameHandler.ts # 游戏处理器
│ │ │ │ └── types/ # 桥接类型定义
│ │ │ │ └── BridgeTypes.ts
│ │ │ └── components/ # 自定义组件
│ │ │ ├── HallWebComponent.ets # 大厅Web组件
│ │ │ ├── GameWebComponent.ets # 子游戏Web组件
│ │ │ ├── LoadingComponent.ets # 加载组件
│ │ │ ├── ProgressComponent.ets # 进度组件
│ │ │ └── ErrorComponent.ets # 错误组件
│ │ ├── resources/ # 资源文件
│ │ │ ├── base/ # 基础资源
│ │ │ │ ├── element/ # 元素资源
│ │ │ │ ├── media/ # 媒体资源
│ │ │ │ └── profile/ # 配置文件
│ │ │ │ ├── main_pages.json # 页面路由配置
│ │ │ │ └── startup_config.json # 启动配置
│ │ │ ├── rawfile/ # 原始文件
│ │ │ │ ├── default_hall/ # 默认大厅资源
│ │ │ │ │ ├── index.html # 大厅入口页面
│ │ │ │ │ ├── js/ # JavaScript文件
│ │ │ │ │ ├── css/ # 样式文件
│ │ │ │ │ └── images/ # 图片资源
│ │ │ │ ├── default_games/ # 默认游戏资源
│ │ │ │ │ └── placeholder/ # 占位游戏
│ │ │ │ └── bridge/ # 桥接相关
│ │ │ │ ├── hall_bridge.js # 大厅桥接JS
│ │ │ │ └── game_bridge.js # 游戏桥接JS
│ │ │ └── en_US/ # 英文资源
│ │ └── module.json5 # 模块配置文件
│ ├── build-profile.json5 # 构建配置
│ ├── hvigorfile.ts # 构建脚本
│ └── oh-package.json5 # 依赖配置
├── har/ # HAR模块(可选)
│ └── common_lib/ # 通用库模块
├── libs/ # 第三方库
│ ├── huawei-map-sdk/ # 华为地图SDK
│ └── wechat-harmonyos-sdk/ # 微信SDK(如有)
├── docs/ # 文档目录
│ ├── API_REFERENCE.md # API参考文档
│ ├── DEPLOYMENT_GUIDE.md # 部署指南
│ └── TROUBLESHOOTING.md # 故障排除
├── build-profile.json5 # 项目构建配置
├── hvigorfile.ts # 项目构建脚本
└── oh-package.json5 # 项目依赖配置
2.2 运行时资源目录结构
应用运行时会在设备上创建以下目录结构来管理动态下载的游戏资源:
/data/app/el2/100/base/com.jx.jyhd.harmonyos/haps/entry/files/
├── game_resources/ # 游戏资源根目录
│ ├── hall/ # 大厅资源目录
│ │ ├── index.html # 大厅入口文件
│ │ ├── js/ # 大厅JS文件
│ │ ├── css/ # 大厅样式文件
│ │ ├── images/ # 大厅图片资源
│ │ └── version.txt # 大厅版本信息
│ ├── games/ # 子游戏资源目录
│ │ ├── game001/ # 斗地主游戏
│ │ │ ├── index.html # 游戏入口文件
│ │ │ ├── js/ # 游戏JS文件
│ │ │ ├── css/ # 游戏样式文件
│ │ │ ├── images/ # 游戏图片资源
│ │ │ ├── audio/ # 游戏音频资源
│ │ │ └── version.txt # 游戏版本信息
│ │ ├── game002/ # 麻将游戏
│ │ │ ├── index.html
│ │ │ ├── js/
│ │ │ ├── css/
│ │ │ ├── images/
│ │ │ ├── audio/
│ │ │ └── version.txt
│ │ └── ... # 其他子游戏
│ ├── downloads/ # 临时下载目录
│ │ ├── hall_temp.zip # 临时大厅ZIP包
│ │ ├── game001_temp.zip # 临时游戏ZIP包
│ │ └── ...
│ └── cache/ # 缓存目录
│ ├── images/ # 图片缓存
│ ├── config/ # 配置缓存
│ └── temp/ # 临时文件
├── config_cache/ # 配置缓存目录
│ ├── remote_config.json # 远程配置缓存
│ └── startup_config.json # 启动配置缓存
└── logs/ # 日志目录
├── app.log # 应用日志
├── download.log # 下载日志
└── error.log # 错误日志
2.2 关键配置文件说明
2.2.1 应用配置 (app.json5)
{
"app": {
"bundleName": "com.jx.jyhd.harmonyos",
"vendor": "YouleGames",
"versionCode": 1000360,
"versionName": "3.6.0",
"icon": "$media:app_icon",
"label": "$string:app_name",
"description": "$string:app_description",
"distributedNotificationEnabled": true,
"targetAPIVersion": 9,
"compatibleAPIVersion": 8
}
}
2.2.2 模块配置 (module.json5)
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:permission_internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:permission_location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:permission_location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "$string:permission_camera_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:permission_microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "$string:permission_storage_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "$string:permission_storage_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.VIBRATE",
"reason": "$string:permission_vibrate_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
2. 应用启动流程设计
TSGame HarmonyOS应用采用分阶段启动流程,通过远程配置文件驱动,支持大厅和子游戏的动态资源管理。由于启动流程涉及多个复杂的组件和详细的配置参数,已将完整的启动逻辑分离到独立文档中。
2.1 启动流程文档
📖 详细启动流程说明请参考:TSGame_应用启动流程详解.md
该文档包含以下完整内容:
2.1.1 核心启动流程
- 启动时序图:从应用启动到大厅显示的完整流程
- 阶段化启动:EntryAbility.onCreate() → 配置检查 → 资源下载 → WebView初始化
- 大厅启动逻辑:大厅资源管理、WebView配置、页面加载
- 子游戏跳转:游戏资源检查、下载解压、WebView切换
2.1.2 远程配置文件管理
- 配置文件结构:JSON格式的分层配置,存储在七牛云CDN
- 配置参数获取:ConfigParameterHelper类的详细实现
- 属性读取逻辑:具体到每个配置属性的获取方法
- 分层覆盖机制:全局 → 代理商 → 渠道 → 市场的参数覆盖
2.1.3 关键配置属性说明
app_download:应用下载地址,用于应用更新app_version:应用版本号,用于版本比较game_download:游戏ZIP包下载地址,用于游戏资源更新game_version:游戏版本号,用于资源版本比较url:API服务器地址,用于网络请求showmessage:版本更新说明,用于用户提示
2.1.4 ZIP包解压路径规范
- 文件系统结构:HarmonyOS应用的完整目录布局
- 解压路径管理:PathManager类的路径规范
- 资源目录说明:大厅、子游戏、缓存、配置等目录结构
2.1.5 版本检查和更新
- 版本检测流程:启动时的版本比较逻辑
- 更新策略:应用更新vs资源更新的区别
- 异常处理:配置获取失败、下载失败等异常的降级处理
2.2 HarmonyOS适配要点
2.2.1 启动性能优化
- 利用HarmonyOS 5.0的应用预加载机制
- 异步加载非关键资源
- WebView初始化优化
2.2.2 资源管理适配
- 适配HarmonyOS的沙盒文件系统
- 文件权限管理
- 存储空间优化
2.2.3 网络请求优化
- 网络状态检测和适配
- CDN加速配置
- 下载断点续传支持
3. JavaScript与App双向调用接口
TSGame应用通过JS Bridge实现WebView(H5游戏)与原生Android/HarmonyOS应用之间的双向通信。由于接口数量众多且参数复杂,已将所有JavaScript与App双向调用的接口详细说明分离到独立文档中。
3.1 接口详解文档
📖 详细接口说明请参考:TSGame_JSBridge_接口详解.md
该文档包含以下完整内容:
3.1.1 WebView调用App接口
- 游戏相关接口:SwitchOverGameData、getGameinstall、downloadGame等
- 用户信息接口:getUserInfo、updateUserCoins等
- 分享接口:friendsSharetypeUrlToptitleDescript、getSharePlatforms等
- 媒体播放接口:mediaTypeAudio、mediaTypeVideo等
- 设备信息接口:getDeviceInfo、getPhoneInfo等
- 网络请求接口:httpRequest等
- 系统功能接口:showToast、showDialog、openUrl等
3.1.2 App调用WebView接口
- 页面生命周期:onPageResume、onPagePause、onPageDestroy
- 网络状态通知:onNetworkChanged
- 数据更新通知:onUserDataChanged
3.1.3 数据结构定义
- 基础数据结构:BaseResponse、Position、Size等
- 游戏数据结构:GameInfo、GameProgress等
- 分享数据结构:ShareConfig、ShareResult等
- 媒体数据结构:AudioInfo、VideoInfo等
3.1.4 完整的接口参考
- 每个接口的作用、调用时机、参数结构、字段说明
- TypeScript类型定义和JSON数据格式
- 实际使用示例和错误处理
- 错误码定义和注意事项
3.2 HarmonyOS适配要点
基于Android工程webviewActivity.java和NewwebviewActivity.java代码完整分析,HarmonyOS版本的JS Bridge实现需要注意以下几点:
3.2.1 接口兼容性
- 所有接口方法名、参数格式与Android工程代码保持完全一致
- 保持与现有H5游戏的无缝兼容
- 接口调用方式和数据格式保持不变
3.2.2 权限适配
- 相机、定位、通讯录等接口需要动态申请权限
- 适配HarmonyOS 5.0的精细化权限控制
- 实现权限申请失败的降级处理
3.2.3 性能优化
- 避免频繁调用Bridge接口
- 大量数据传输考虑分批处理
- 适当使用本地缓存减少接口调用
3.2.4 异常处理
- 所有Bridge调用都是异步的,需要适当的超时处理
- 网络中断时部分接口可能失效
- 提供友好的错误提示和重试机制
4. 总结
TSGame HarmonyOS版本通过完整的JavaScript Bridge接口体系,实现了WebView与原生应用的深度集成。本文档详细说明了所有40多个双向接口的用法、参数格式和调用时机,为HarmonyOS版本的开发提供了完整的技术参考。
所有接口的方法名、参数格式均与Android工程保持完全一致,确保了跨平台的兼容性和一致性。开发者可以基于这些接口实现丰富的原生功能集成。
8. 应用和游戏更新机制
TSGame支持应用APK/APP和游戏ZIP资源的自动更新机制,确保用户始终使用最新版本的应用和游戏内容。HarmonyOS版本需要完全实现与Android一致的更新逻辑。
8.1 更新机制概述
8.1.1 更新类型
应用更新(APP级别):
- 更新应用本身的代码和核心功能
- 涉及应用包(.hap文件)的下载和安装
- 需要用户确认和重启应用
- 对应Android的APK更新
游戏更新(资源级别):
- 更新游戏资源文件(HTML、CSS、JS、图片等)
- 涉及ZIP包的下载、解压和替换
- 支持静默更新,无需用户干预
- 不需要重启应用,热更新生效
8.1.2 版本控制策略
更新机制采用分层配置的版本控制策略:
interface VersionConfig {
// 应用版本信息
app_version: string; // 应用版本号,如"3.6.3"
app_download: string; // 应用下载URL
app_size: string; // 应用包大小
// 游戏版本信息
game_version: string; // 游戏版本号,如"2.1.0"
game_download: string; // 游戏ZIP包下载URL
game_size: string; // 游戏包大小
// 显示信息
showmessage: string; // 版本更新说明文本
}
8.2 版本检测流程
8.2.1 启动时版本检测
class VersionChecker {
/**
* 启动时执行版本检测流程
*/
public async checkVersionOnLaunch(): Promise<void> {
try {
// 1. 获取本地版本信息
const localVersions = await this.getLocalVersionInfo();
// 2. 获取服务器版本配置
const serverConfig = await this.fetchServerVersionConfig();
// 3. 比较版本并决定更新策略
const updatePlan = this.compareVersions(localVersions, serverConfig);
// 4. 执行相应的更新流程
await this.executeUpdatePlan(updatePlan);
} catch (error) {
console.error('版本检测失败:', error);
// 失败时继续启动,使用本地版本
this.startGameWithLocalVersion();
}
}
/**
* 获取本地版本信息
*/
private async getLocalVersionInfo(): Promise<LocalVersionInfo> {
const preferences = dataPreferences.getPreferences(getContext(), 'version_prefs');
return {
appVersion: await preferences.get('app_version', ''),
gameVersion: await preferences.get('game_version', ''),
gameId: await preferences.get('game_id', ''),
lastUpdateTime: await preferences.get('last_update_time', 0)
};
}
/**
* 获取服务器版本配置
*/
private async fetchServerVersionConfig(): Promise<VersionConfig> {
const gameData = this.getCurrentGameData();
const configUrl = this.buildConfigUrl(gameData);
const response = await fetch(configUrl + '?t=' + Date.now());
if (!response.ok) {
throw new Error(`配置获取失败: ${response.status}`);
}
return response.json();
}
/**
* 比较版本并生成更新计划
*/
private compareVersions(local: LocalVersionInfo, server: VersionConfig): UpdatePlan {
const plan: UpdatePlan = {
needAppUpdate: false,
needGameUpdate: false,
appUpdateInfo: null,
gameUpdateInfo: null
};
// 比较应用版本
if (this.isVersionNewer(server.app_version, local.appVersion)) {
plan.needAppUpdate = true;
plan.appUpdateInfo = {
version: server.app_version,
downloadUrl: server.app_download,
size: server.app_size,
message: server.showmessage
};
}
// 比较游戏版本
if (this.isVersionNewer(server.game_version, local.gameVersion)) {
plan.needGameUpdate = true;
plan.gameUpdateInfo = {
version: server.game_version,
downloadUrl: server.game_download,
size: server.game_size,
message: server.showmessage
};
}
return plan;
}
}
8.2.2 版本比较逻辑
class VersionComparator {
/**
* 比较两个版本号,判断newVersion是否比oldVersion新
*/
public static isVersionNewer(newVersion: string, oldVersion: string): boolean {
if (!newVersion || !oldVersion) {
return !!newVersion && !oldVersion;
}
const newParts = newVersion.split('.').map(Number);
const oldParts = oldVersion.split('.').map(Number);
const maxLength = Math.max(newParts.length, oldParts.length);
for (let i = 0; i < maxLength; i++) {
const newPart = newParts[i] || 0;
const oldPart = oldParts[i] || 0;
if (newPart > oldPart) return true;
if (newPart < oldPart) return false;
}
return false;
}
/**
* 获取版本比较结果码(用于JS Bridge接口)
* @returns "1" - 本地版本高于服务器版本,"0" - 本地版本低于或等于服务器版本
*/
public static getCompareCode(localVersion: string, serverVersion: string): string {
return this.isVersionNewer(localVersion, serverVersion) ? "1" : "0";
}
}
8.3 应用更新实现
8.3.1 应用更新流程
class AppUpdater {
/**
* 执行应用更新
*/
public async updateApp(updateInfo: AppUpdateInfo): Promise<void> {
try {
// 1. 显示更新确认对话框
const userConfirmed = await this.showUpdateConfirmDialog(updateInfo);
if (!userConfirmed) {
return;
}
// 2. 显示下载进度界面
this.showDownloadProgress();
// 3. 下载应用包
const appPackagePath = await this.downloadAppPackage(updateInfo.downloadUrl);
// 4. 验证包完整性
await this.verifyPackageIntegrity(appPackagePath);
// 5. 启动安装流程
await this.installAppPackage(appPackagePath);
} catch (error) {
console.error('应用更新失败:', error);
this.showUpdateErrorDialog(error.message);
}
}
/**
* 显示更新确认对话框
*/
private async showUpdateConfirmDialog(updateInfo: AppUpdateInfo): Promise<boolean> {
return new Promise((resolve) => {
AlertDialog.show({
title: '发现新版本',
message: `版本 ${updateInfo.version}\n大小: ${updateInfo.size}\n\n${updateInfo.message}`,
primaryButton: {
value: '立即更新',
action: () => resolve(true)
},
secondaryButton: {
value: '稍后更新',
action: () => resolve(false)
}
});
});
}
/**
* 下载应用包
*/
private async downloadAppPackage(downloadUrl: string): Promise<string> {
const downloader = new FileDownloader();
const fileName = `tsgame_update_${Date.now()}.hap`;
const downloadPath = getContext().filesDir + '/downloads/' + fileName;
// 创建下载目录
await fileIo.mkdir(downloadPath.substring(0, downloadPath.lastIndexOf('/')));
return new Promise((resolve, reject) => {
downloader.download(downloadUrl, downloadPath, {
onProgress: (progress: number, total: number) => {
this.updateDownloadProgress(progress, total);
},
onSuccess: () => {
console.log('应用包下载完成:', downloadPath);
resolve(downloadPath);
},
onError: (error: Error) => {
console.error('应用包下载失败:', error);
reject(error);
}
});
});
}
/**
* 安装应用包
*/
private async installAppPackage(packagePath: string): Promise<void> {
try {
// HarmonyOS应用安装API调用
const bundleInstaller = installer.getBundleInstaller();
await bundleInstaller.install([packagePath], {
userId: 100,
installFlag: installer.InstallFlag.REPLACE_EXISTING
});
console.log('应用安装成功');
// 安装成功后重启应用
this.restartApp();
} catch (error) {
console.error('应用安装失败:', error);
throw new Error('安装失败,请手动安装');
}
}
}
8.3.2 下载进度管理
class DownloadProgressManager {
private progressDialog: CustomDialogController | null = null;
/**
* 显示下载进度界面
*/
public showDownloadProgress(): void {
this.progressDialog = new CustomDialogController({
builder: DownloadProgressDialog({
progress: 0,
downloadedSize: '0 MB',
totalSize: '0 MB',
onCancel: () => this.cancelDownload()
}),
autoCancel: false,
customStyle: true
});
this.progressDialog.open();
}
/**
* 更新下载进度
*/
public updateProgress(downloaded: number, total: number): void {
if (!this.progressDialog) return;
const progress = Math.floor((downloaded / total) * 100);
const downloadedMB = (downloaded / 1024 / 1024).toFixed(2);
const totalMB = (total / 1024 / 1024).toFixed(2);
// 更新进度界面显示
this.progressDialog.update({
progress: progress,
downloadedSize: `${downloadedMB} MB`,
totalSize: `${totalMB} MB`
});
}
/**
* 隐藏下载进度界面
*/
public hideDownloadProgress(): void {
if (this.progressDialog) {
this.progressDialog.close();
this.progressDialog = null;
}
}
}
8.4 游戏资源更新实现
8.4.1 游戏资源更新流程
class GameResourceUpdater {
/**
* 执行游戏资源更新
*/
public async updateGameResources(updateInfo: GameUpdateInfo): Promise<void> {
try {
// 1. 静默下载游戏ZIP包
this.showUpdateProgress('正在下载游戏资源...');
const zipFilePath = await this.downloadGameZip(updateInfo.downloadUrl);
// 2. 验证ZIP包完整性
this.showUpdateProgress('正在验证资源包...');
await this.verifyZipIntegrity(zipFilePath);
// 3. 备份当前游戏资源
this.showUpdateProgress('正在备份当前资源...');
await this.backupCurrentGameResources();
// 4. 解压新的游戏资源
this.showUpdateProgress('正在解压游戏资源...');
await this.extractGameZip(zipFilePath);
// 5. 更新本地版本信息
await this.updateLocalVersionInfo(updateInfo.version);
// 6. 清理临时文件
await this.cleanupTempFiles(zipFilePath);
console.log('游戏资源更新完成');
this.hideUpdateProgress();
} catch (error) {
console.error('游戏资源更新失败:', error);
// 失败时恢复备份
await this.restoreBackupResources();
this.showUpdateErrorDialog('游戏资源更新失败,已恢复原始资源');
}
}
/**
* 下载游戏ZIP包
*/
private async downloadGameZip(downloadUrl: string): Promise<string> {
const downloader = new FileDownloader();
const fileName = `game_${Date.now()}.zip`;
const downloadPath = getContext().filesDir + '/temp/' + fileName;
// 创建临时目录
await fileIo.mkdir(downloadPath.substring(0, downloadPath.lastIndexOf('/')));
return new Promise((resolve, reject) => {
downloader.download(downloadUrl, downloadPath, {
onProgress: (progress: number, total: number) => {
const percent = Math.floor((progress / total) * 100);
this.updateProgressText(`下载进度: ${percent}%`);
},
onSuccess: () => {
console.log('游戏ZIP包下载完成:', downloadPath);
resolve(downloadPath);
},
onError: (error: Error) => {
console.error('游戏ZIP包下载失败:', error);
reject(error);
}
});
});
}
/**
* 解压游戏ZIP包
*/
private async extractGameZip(zipFilePath: string): Promise<void> {
const gameResourceDir = this.getGameResourceDirectory();
// 删除旧的游戏资源
if (await fileIo.access(gameResourceDir)) {
await fileIo.rmdir(gameResourceDir, true);
}
// 创建游戏资源目录
await fileIo.mkdir(gameResourceDir, true);
// 解压ZIP包到游戏资源目录
const zipExtractor = new ZipExtractor();
await zipExtractor.extract(zipFilePath, gameResourceDir, {
onProgress: (extractedFiles: number, totalFiles: number) => {
const percent = Math.floor((extractedFiles / totalFiles) * 100);
this.updateProgressText(`解压进度: ${percent}%`);
}
});
console.log('游戏资源解压完成');
}
/**
* 备份当前游戏资源
*/
private async backupCurrentGameResources(): Promise<void> {
const gameResourceDir = this.getGameResourceDirectory();
const backupDir = this.getGameBackupDirectory();
if (await fileIo.access(gameResourceDir)) {
// 删除旧备份
if (await fileIo.access(backupDir)) {
await fileIo.rmdir(backupDir, true);
}
// 创建新备份
await this.copyDirectory(gameResourceDir, backupDir);
console.log('游戏资源备份完成');
}
}
/**
* 恢复备份资源
*/
private async restoreBackupResources(): Promise<void> {
const gameResourceDir = this.getGameResourceDirectory();
const backupDir = this.getGameBackupDirectory();
if (await fileIo.access(backupDir)) {
// 删除损坏的资源
if (await fileIo.access(gameResourceDir)) {
await fileIo.rmdir(gameResourceDir, true);
}
// 恢复备份资源
await this.copyDirectory(backupDir, gameResourceDir);
console.log('游戏资源恢复完成');
}
}
}
8.4.2 ZIP包处理工具
class ZipExtractor {
/**
* 解压ZIP文件
*/
public async extract(zipPath: string, targetDir: string, options?: ExtractOptions): Promise<void> {
return new Promise((resolve, reject) => {
// 使用HarmonyOS的压缩解压API
const decompressor = zlib.createDecompressor();
decompressor.decompress(zipPath, targetDir, (error, result) => {
if (error) {
reject(new Error(`解压失败: ${error.message}`));
return;
}
if (options?.onProgress) {
options.onProgress(result.extractedFiles, result.totalFiles);
}
resolve();
});
});
}
/**
* 验证ZIP包完整性
*/
public async verifyIntegrity(zipPath: string): Promise<boolean> {
try {
const validator = zlib.createValidator();
const result = await validator.validate(zipPath);
return result.isValid;
} catch (error) {
console.error('ZIP包验证失败:', error);
return false;
}
}
}
8.5 文件下载器实现
8.5.1 网络下载管理
class FileDownloader {
private downloadTask: request.DownloadTask | null = null;
/**
* 下载文件
*/
public download(url: string, filePath: string, callbacks: DownloadCallbacks): void {
const downloadConfig: request.DownloadConfig = {
url: url,
filePath: filePath,
enableMetered: true,
enableRoaming: true,
description: '正在下载更新包...',
networkType: request.NETWORK_WIFI | request.NETWORK_MOBILE
};
request.downloadFile(getContext(), downloadConfig).then((task) => {
this.downloadTask = task;
// 监听下载进度
task.on('progress', (receivedSize: number, totalSize: number) => {
callbacks.onProgress?.(receivedSize, totalSize);
});
// 监听下载完成
task.on('complete', () => {
callbacks.onSuccess?.();
this.downloadTask = null;
});
// 监听下载失败
task.on('fail', (error: number) => {
const errorMsg = this.getDownloadErrorMessage(error);
callbacks.onError?.(new Error(errorMsg));
this.downloadTask = null;
});
}).catch((error) => {
callbacks.onError?.(error);
});
}
/**
* 取消下载
*/
public cancel(): void {
if (this.downloadTask) {
this.downloadTask.delete((error) => {
if (error) {
console.error('取消下载失败:', error);
} else {
console.log('下载已取消');
}
});
this.downloadTask = null;
}
}
/**
* 获取下载错误信息
*/
private getDownloadErrorMessage(errorCode: number): string {
const errorMessages = {
[request.ERROR_CANNOT_RESUME]: '无法恢复下载',
[request.ERROR_DEVICE_NOT_FOUND]: '设备未找到',
[request.ERROR_FILE_ALREADY_EXISTS]: '文件已存在',
[request.ERROR_FILE_ERROR]: '文件错误',
[request.ERROR_HTTP_DATA_ERROR]: 'HTTP数据错误',
[request.ERROR_INSUFFICIENT_SPACE]: '存储空间不足',
[request.ERROR_TOO_MANY_REDIRECTS]: '重定向次数过多',
[request.ERROR_UNHANDLED_HTTP_CODE]: '未处理的HTTP状态码',
[request.ERROR_UNKNOWN]: '未知错误'
};
return errorMessages[errorCode] || `下载失败,错误代码: ${errorCode}`;
}
}
8.6 更新状态管理
8.6.1 更新进度界面
@CustomDialog
struct DownloadProgressDialog {
@State progress: number = 0;
@State downloadedSize: string = '0 MB';
@State totalSize: string = '0 MB';
@State statusText: string = '正在下载...';
@State errorMessage: string = '';
onCancel?: () => void;
build() {
Column({ space: 20 }) {
// 标题
Text('更新下载')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
// 进度条
Progress({
value: this.progress,
total: 100,
type: ProgressType.Linear
})
.width('100%')
.height(10)
.color(Color.Blue)
.backgroundColor(Color.Gray)
// 进度文本
Row() {
Text(`${this.progress}%`)
.fontSize(14)
.fontColor(Color.Gray)
Spacer()
Text(`${this.downloadedSize} / ${this.totalSize}`)
.fontSize(14)
.fontColor(Color.Gray)
}
.width('100%')
// 状态文本
Text(this.statusText)
.fontSize(14)
.fontColor(Color.Gray)
.textAlign(TextAlign.Center)
// 错误信息
if (this.errorMessage.trim() !== '') {
Text(this.errorMessage)
.fontSize(14)
.fontColor(Color.Red)
.textAlign(TextAlign.Center)
}
// 取消按钮
Button('取消下载')
.width('100%')
.height(40)
.backgroundColor(Color.Gray)
.onClick(() => {
this.onCancel?.();
})
}
.width('80%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(10)
}
}
9. 项目风险评估与解决方案
9.1 高风险项
9.1.1 微信SDK集成 (风险等级: 高)
问题描述: 微信SDK可能不支持HarmonyOS平台 影响评估:
- 微信登录功能无法实现
- 微信分享功能受限
- 用户体验下降
解决方案:
- WebView内嵌登录: 使用WebView内嵌微信网页版登录
- 华为账号优先: 将华为账号作为主要登录方式
- 预留接口: 保留微信SDK接口,待官方支持后再集成
- 系统分享: 使用HarmonyOS系统级分享功能替代
9.1.2 双WebView架构性能 (风险等级: 中)
问题描述: 多个WebView可能导致内存占用过高 影响评估:
- 应用内存占用增加
- 系统性能下降
- 可能出现OOM崩溃
解决方案:
- WebView复用机制: 实现WebView池化管理
- 及时释放: 及时释放不活跃的WebView资源
- 资源优化: 优化H5资源加载策略
- 内存监控: 建立内存使用监控机制
9.1.3 HarmonyOS 5.0新API适配 (风险等级: 中)
问题描述: 新版本API可能存在兼容性问题或文档不完善 影响评估:
- 新特性无法正常使用
- 向下兼容性问题
- 开发周期延长
解决方案:
- 官方文档跟踪: 及时关注华为官方文档更新
- 兼容性测试: 建立API兼容性测试用例
- 技术支持: 保持与华为技术支持的沟通
- 降级方案: 准备API降级方案确保向下兼容
9.2 中风险项
9.2.1 华为地图SDK集成
解决方案: 参考华为官方文档,建立标准集成流程
9.2.2 版本更新机制
解决方案: 参考HarmonyOS应用更新最佳实践
9.2.3 性能优化
解决方案: 建立性能监控体系,持续优化
10. 团队分工与技能要求
10.1 核心开发团队 (3-5人)
10.1.1 架构师/技术负责人 (1人)
职责:
- 技术架构设计和决策
- 核心模块开发
- 代码审查和质量把控
- 技术难点攻关
技能要求:
- 精通HarmonyOS开发 (熟悉5.0新特性)
- 丰富的移动应用架构经验
- 熟悉WebView和JS Bridge技术
- 了解HarmonyOS 5.0性能优化技术
10.1.2 前端开发/UI开发 (1人)
职责:
- WebView组件开发
- 用户界面实现
- 用户体验优化
- 前端性能优化
技能要求:
- 熟练掌握ArkTS/TypeScript
- 熟悉HarmonyOS UI框架 (包括5.0新特性)
- 有WebView开发经验
- 了解HarmonyOS 5.0 UI渲染优化
10.1.3 后端集成/API开发 (1-2人)
职责:
- JS Bridge API实现
- 第三方SDK集成
- 系统服务集成
- 网络和存储功能
技能要求:
- 熟悉HarmonyOS系统API (特别是5.0新增API)
- 有移动应用API开发经验
- 了解第三方SDK集成
- 掌握HarmonyOS 5.0安全特性
10.1.4 测试工程师 (1人)
职责:
- 功能测试和验证
- 性能测试
- 兼容性测试
- Bug跟踪和质量保证
技能要求:
- 熟悉移动应用测试
- 了解HarmonyOS测试工具 (包括5.0新测试框架)
- 有自动化测试经验更佳
- 掌握跨版本兼容性测试方法
11. 开发里程碑与交付物
11.1 里程碑1: 基础框架完成 (第2周末)
交付物:
- 可运行的项目骨架
- 双WebView容器实现
- 基础目录结构
- 开发环境文档
11.2 里程碑2: 核心功能完成 (第5周末)
交付物:
- 资源下载和管理功能
- WebView加载和切换
- 基础JS Bridge API
- 功能演示版本
11.3 里程碑3: 功能集成完成 (第7周末)
交付物:
- 所有第三方SDK集成
- 完整的API接口
- 媒体和定位功能
- Beta测试版本
11.4 里程碑4: 产品就绪 (第10周末)
交付物:
- 完整功能的应用
- 性能优化版本
- 完整测试报告
- 发布准备材料
12. Git版本管理规范
12.1 Git工作流策略
采用Git Flow工作流模式,确保代码管理的规范性和可追溯性。
12.1.1 分支策略
master (主分支)
├── develop (开发分支)
│ ├── feature/user-auth # 功能分支:用户认证
│ ├── feature/webview-bridge # 功能分支:WebView桥接
│ ├── feature/media-manager # 功能分支:媒体管理
│ └── feature/location-service # 功能分支:定位服务
├── release/v1.0.0 # 发布分支
└── hotfix/critical-bug-fix # 热修复分支
分支说明:
- master: 生产环境分支,只接受release和hotfix的合并
- develop: 开发主分支,集成所有feature分支
- feature/*: 功能开发分支,从develop分出,完成后合并回develop
- release/*: 发布准备分支,从develop分出,用于发布前的bug修复
- hotfix/*: 紧急修复分支,从master分出,修复后合并到master和develop
12.1.2 分支命名规范
# 功能分支
feature/模块名-功能描述
例:feature/auth-wechat-login
feature/webview-js-bridge
feature/media-audio-record
# 发布分支
release/版本号
例:release/v1.0.0
release/v1.1.0
# 热修复分支
hotfix/问题描述
例:hotfix/webview-crash-fix
hotfix/login-timeout-issue
12.1.3 提交消息规范
<type>(<scope>): <subject>
# type类型:
feat: 新功能
fix: 修复bug
docs: 文档更新
style: 代码格式调整
refactor: 代码重构
test: 测试相关
chore: 构建过程或辅助工具的变动
# 示例:
feat(webview): 实现双WebView架构
fix(bridge): 修复JS Bridge通信异常
docs(api): 更新API接口文档
12.2 代码审查规范
- 必须审查: 所有PR必须经过代码审查
- 审查人员: 至少需要一名高级开发人员审查
- 审查内容: 代码质量、性能、安全性、规范性
- 合并条件: 审查通过且CI/CD通过后方可合并
13. 最佳实践和注意事项
13.1 性能优化最佳实践
-
WebView优化:
- 使用WebView池化管理
- 及时释放不使用的WebView
- 优化JS Bridge调用频率
-
内存管理:
- 监控内存使用情况
- 及时清理缓存数据
- 避免内存泄漏
-
网络优化:
- 使用CDN加速资源下载
- 实现断点续传机制
- 合理使用缓存策略
13.2 安全最佳实践
-
数据安全:
- 敏感数据加密存储
- 网络传输使用HTTPS
- 验证下载包完整性
-
权限管理:
- 最小权限原则
- 动态权限申请
- 权限使用说明
-
代码安全:
- 代码混淆保护
- 防止逆向工程
- 安全审计