2024年6月21日 HarmonyOS NEXT (后续称之为 NEXT) 正式发布,随着 NEXT 稳定版的逐渐临近,各个应用及SDK正在忙于适配 NEXT 系统,同样也面临着系统升级时如何对数据的迁移适配。本文通过使用环信 SDK 介绍如何从 HarmonyOS 系统升级到 NEXT 系统时,进行应用数据的迁移适配。
应用数据迁移步骤
官方文档
在进行数据迁移之前,我们需要先了解升级到NEXT系统,应用数据迁移需要进行哪些步骤。下图是整个迁移流程:
上图1-4步骤,均是由系统触发完成,开发者需要在第4步实现自定义的“BackupExtensionAbility”逻辑,实现自定义的数据恢复及转换逻辑。
1. HarmonyOS APK沙箱数据搬迁到中间目录
在升级的过程中,HarmonyOS系统会按照一定的规则,将应用的沙箱数据放置到四个数据目录中,然后这些数据会被整体压缩后搬迁到中间目录。
开发过程中,应用数据常用到的数据目录为:
/data/user/{userId}/{APK包名}/
/data/media/{userId}/Android/data/{APK包名}/
`/data/user/{userId}/{APK包名}/` 对应的路径为 HarmonyOS APK 应用的`data/data/{包名}`路径目录,而`/data/media/{userId}/Android/data/{APK包名}/`对应的是应用的`sdcard/Android/data/{包名}/`路径目录。应用进行数据迁移的话,主要是关注这两块的数据迁移。
2. 华为应用市场安装 NEXT 版本的应用
NEXT系统的“数据迁移框架”会从华为应用市场根据开发者账号下载对应的 NEXT 版本的应用。具体关联,请参考文档:HarmonyOS应用关联Android应用。
3. 数据导入
在NEXT 应用安装完成后,“数据迁移框架”将应用沙箱数据从中间目录搬迁到备份恢复目录。
APK应用沙箱目录与备份恢复目录映射关系见下表中所示:
APK应用沙箱目录 | 备份恢复目录 | 备份恢复目录获取方式 |
---|---|---|
/data/user_de/{userId}/{APK包名}/ | /data/storage/el1/base/.backup/restore/{APK包名}/de/ | this.context.area = contextConstant.AreaMode.EL1; let deSourcePath = this.context.backupDir + "restore/{APK包名}/de/" |
/data/user/{userId}/{APK包名}/ | /data/storage/el2/base/.backup/restore/{APK包名}/ce/ | this.context.area = contextConstant.AreaMode.EL2; let ceSourcePath = this.context.backupDir + "restore/{APK包名}/ce/" |
/data/media/{userId}/Android/ data/{APK包名}/ | /data/storage/el2/base/.backup/restore/{APK包名}/A/data/ | this.context.area = contextConstant.AreaMode.EL2; let dataSourcePath = this.context.backupDir + "restore/{APK包名}/A/data/" |
/data/media/{userId}/Android/ obb/{APK包名}/ | /data/storage/el2/base/.backup/restore/{APK包名}/A/obb/ | this.context.area = contextConstant.AreaMode.EL2; let obbSourcePath = this.context.backupDir + "restore/{APK包名}/A/obb/" |
4. 数据转换
-
在应用数据搬迁到备份恢复目录后,“数据迁移框架”向“备份恢复框架”发送应用数据恢复请求。
-
“备份恢复框架”拉起应用的“BackupExtensionAbility”独立进程,启动应用数据恢复。
-
在“BackupExtensionAbility”独立进程中,开发者通过重写“onRestore”添加自定义逻辑,将备份恢复目录中的数据处理后保存到 NEXT 应用的沙箱中,完成应用的数据恢复。
-
“备份恢复框架”在应用数据恢复完成后,会清空备份恢复目录。
5. 启动 NEXT 应用,应用读取应用沙箱数据。
下面通过介绍环信 SDK 的数据迁移,来介绍具体的迁移过程。
需要迁移数据分析
以下为环信 SDK需要迁移的数据:
序号 | 数据名称 | 路径或者文件 | 备注 |
1 | SDK 数据库文件夹 | data/data/{包名}/files/easemobDB/ | |
2 | SDK 数据库相关文件夹 | data/data/{包名}/files/easemobDBPW/ | |
3 | SDK 配置文件 | data/data/{包名}/files/em_ap_config.json data/data/{包名}/files/em_config.json data/data/{包名}/files/server.json | |
4 | SDK 附件 | sdcard/Android/data/{包名}/{AppKey}/ |
-
环信 SDK 已经适配了在 NEXT 应用使用 HarmonyOS APK 数据库的逻辑。1 和 2 部分数据迁移到 NEXT的指定文件夹即可。
-
第 3 部分的配置文件直接迁移到NEXT的指定文件夹即可。
-
SDK 的附件,有如下情况:
(1)消息中的本地路径存储的是 Uri 。
这部分是在公共媒体库中,进行系统的升级后,经咨询华为,目前还没有映射关系。目前的处理可以参照如下思路:
-
判断本地文件不存在后,调用 `ChatManager#downloadAttachment` 或者
ChatManager#downloadThumbnail
方法从服务器下载文件资源后,然后再展示。 -
如果服务器文件过期,需要展示默认图片。
(2)消息中的本地路径存储的是第 4 部分的文件路径,则需要对路径进行转换。举例如下:
迁移过来的数据库中存储的附件本地路径为:
/storage/emulated/0/Android/data/{包名}/{appKey}/files/{receiver id}/{sender id}/f6dc0580-6b68-11ef-bac3-2d7c12bc3033.jpg
需要转换为:
/data/storage/el2/base/{appKey}/files/{receiver id}/{sender id}/f6dc0580-6b68-11ef-bac3-2d7c12bc3033.jpg
-
建议此步骤在展示附件时进行判断,并更新对应的localPath。
-
如果转换后,文件仍不存在,则需要展示默认图片。
环境准备及工具要求
HarmonyOS NEXT Developer Beta1或之后版本的终端设备一部。
HarmonyOS 系统终端设备一部。
工具要求:
工具 | 版本 | 说明 |
---|---|---|
“迁移调试”工具 | 205.0.0.115及之后版本 | 模拟验证数据迁移 |
DevEco Studio | DevEco Studio NEXT Developer Beta2及之后版本 | 请参考:DevEco Studio使用指南 |
Compatible SDK | 5.0.0(12) | 请参考:版本说明 |
注意:
“迁移调试”工具需要向华为申请获取。
HarmonyOS NEXT Developer Beta1及之后版本,厂商合作伙伴可通过IssueReporter平台提交工单,向华为方技术支持人员申请“迁移调试”工具,模拟进行数据迁移验证。公共开发者请通过“华为开发者联盟官网”->“支持”,在线提单方式获取。
HarmonyOS NEXT Developer Beta1之前版本,开发者可以通过系统内置“迁移调试”工具,模拟进行数据迁移验证。
BackupExtensionAbility实现
官方文档:BackupExtensionAbility的实现
1. 在entry/src/main/ets/目录下,点击 New > Directory 创建backupExtension目录。
2. 点击entry/src/main/ets/backupExtension/目录,点击 New > File 创建BackupExtension.ets文件。
3. 基于迁移环信SDK实现的BackupExtensionAbility示例代码。
import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
import { contextConstant } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { JSON } from '@kit.ArkTS';
const TAG = `BackupExtensionAbility`;
/**
* serviceExt进程入口
*/
export default class BackupExtension extends BackupExtensionAbility {
onBackup () {
console.log(TAG,`onBackup ok`);
}
/**
* 数据恢复处理接口。接口是同步接口,其内部所有的异步操作请进行同步等待。
*
* @param bundleVersion 版本信息
*/
async onRestore (bundleVersion : BundleVersion): Promise<void> {
console.log(TAG, `onRestore ok ${JSON.stringify(bundleVersion)}`);
if (bundleVersion.name.startsWith("0.0.0.0")){
this.context.area = contextConstant.AreaMode.EL2;
// 设置要迁移APK包的包名, 需要替换为需要迁移的APK的,下面为示例
let apkPackageName = "com.xxx.xxx";
let ceSourcePath = this.context.backupDir + `restore/${apkPackageName}/ce/`;
let dataSourcePath = this.context.backupDir + `restore/${apkPackageName}/A/data/`;
// 其中<USERID>当前固定为100, 参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/app-sandbox-directory-V5#应用沙箱路径和真实物理路径的对应关系
const userId = 100;
// 定义要迁移的APK沙箱目录
let targetSDKSourcePath = ceSourcePath + "data/user/" + userId + "/" + apkPackageName + "/";
if (fs.accessSync(targetSDKSourcePath)) {
// 设置要迁移的APK files文件夹下的数据库文件夹
const sdkDbDir = "easemobDB/";
// 设置要迁移的APK files文件夹下的数据库附属文件夹
const sdkDbPwdDir = "easemobDBPW/";
// 设置要迁移的APK files文件夹下的SDK配置文件(4.3.0版本后有em_ap_config.json文件,之前版本的请忽略)
const sdkConfigs = ["em_ap_config.json", "em_config.json", "server.json"];
// 迁移 APK 沙箱目录 files 文件夹下的文件及文件夹到目标文件夹
const fileDestDir = this.context.getApplicationContext().filesDir + "/";
console.log(TAG, "fileDestDir: "+fileDestDir);
console.log(TAG, "start to move db folder");
await this.moveDirToTargetDir(targetSDKSourcePath + "files/" +sdkDbDir, fileDestDir + sdkDbDir);
console.log(TAG, "end to move db folder");
console.log(TAG, "start to move db pw folder");
await this.moveDirToTargetDir(targetSDKSourcePath + "files/" +sdkDbPwdDir, fileDestDir + sdkDbPwdDir);
console.log(TAG, "end to move db pw folder");
sdkConfigs.forEach(async file => {
const filePath = targetSDKSourcePath + "files/" + file;
console.log(TAG, filePath + " to " + (fileDestDir+file));
await this.moveFileToTargetPath(filePath, fileDestDir + file);
})
}
let targetSDKDataSourcePath = dataSourcePath + "data/media/" + userId + "/Android/data/" + apkPackageName + "/";
if (fs.accessSync(targetSDKDataSourcePath)) {
// 设置要迁移 SD 卡下的的AppKey文件夹
const sdkAppKeySourceDir = "easemob#easeim/";
// 迁移 AppKey 文件夹到目标文件夹
console.log(TAG, "start to move appkey folder");
// 获取升级到 HarmonyOS NEXT 后SDK的目标文件夹路径
const sdkDestDir = this.getTargetDestDir() + "/";
console.log(TAG, sdkDestDir);
await this.moveDirToTargetDir(targetSDKDataSourcePath + sdkAppKeySourceDir, sdkDestDir + sdkAppKeySourceDir);
console.log(TAG, "end to move appkey folder");
}
// 在此处实现终端设备从HarmonyOS 4.0升级到HarmonyOS NEXT后,应用数据的转换和迁移
// 涉及异步操作请进行同步等待
console.log(TAG, `HarmonyOS to HarmonyOS NEXT scenario`);
} else {
// 在此处实现从HarmonyOS NEXT设备迁移到HarmonyOS NEXT设备后,应用数据的处理。无特殊要求,可以空实现
// 涉及异步操作请进行同步等待
console.log(TAG, `Other scenario`);
}
}
async moveFileToTargetPath(sourcePath: string, destPath: string) {
try {
// 若mode为0,移动位置存在同名文件时,强制移动覆盖。
await fs.moveFile(sourcePath, destPath);
} catch (e) {
console.log(TAG, "moveFileToTargetPath: " + JSON.stringify(e));
}
}
async moveDirToTargetDir(sourceDir: string, destDir: string) {
if (fs.accessSync(sourceDir)) {
if (!fs.accessSync(destDir)) {
fs.mkdirSync(destDir, true);
}
}
try {
// mode为2,文件级别强制覆盖。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则强制覆盖冲突文件夹下所有同名文件,未冲突文件将继续保留。
console.log(TAG, sourceDir + " to " + destDir);
await fs.moveDir(sourceDir, destDir, 2);
} catch (e) {
console.log(TAG, "moveDirToTargetDir: " + JSON.stringify(e));
}
}
getTargetDestDir(): string {
const filesDir = this.context.getApplicationContext().filesDir;
console.log(TAG, "getTargetDestDir: "+filesDir);
let lastPosition = filesDir.lastIndexOf('/files');
return lastPosition != -1 ? filesDir.substring(0, lastPosition) : filesDir;
}
}
4. 元数据资源配置
需要在 backup_config.json 文件中设置迁移场景,其他的元数据配置在本场景下不需要配置,示例如下:
{
"allowToBackupRestore": true,
"extraInfo": {
"supportScene": [
"hmos2next"
]
}
}
5. 在 module.json5 中注册 BackupExtension
需要在 entry 内的module.json5里面进行注册,示例代码如下:
"extensionAbilities": [
{
"description": "DemoBackupExtension",
"icon": "$media:app_icon",
"name": "BackupExtensionAbility",
"srcEntry": "./ets/backupExtension/BackupExtension.ets", // 对应BackupExtension.ets在代码仓中的位置
"type": "backup", // 类型需要选择backup
"exported": false,
"metadata": [ // 对应注册的元数据资源
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
]
}
]
开发者自验证
官方文档:开发者自验证
应用沙箱数据准备
APK 应用文件路径(从Android Studio的Device Explorer中看)与华为要求的准备的 APK 应用沙箱目录的映射关系:
APK 应用文件路径 | APK 应用沙箱目录 |
data/data/{包名}/ | /data/user/{userId}/{APK包名}/ |
sdcard/Android/data/{包名}/ | /data/media/{userId}/Android/data/{APK包名}/ |
应用沙箱数据准备参考官方文档即可,以下是需要注意的事项:
1. 准备好的应用沙箱文件需要整体放入到压缩包的目录下,例如:
/data/user/{userId}/{APK包名}/
放入到 {APK包名}/ce
后,目录路径为:{APK包名}/ce/data/user/{userId}/{APK包名}/
2. 准备好的压缩包,可以通过 DevEco Studio -> Device File Browser
找到目标设备,找到手机的 Download 文件夹路径(/storage/media/100/local/files/Docs/Download/
),右击 Download 文件夹,选择 “Upload…” 选中压缩好的压缩包。
3. 或者通过 hdc 命令,通过命令 hdc file send localPath/xxx.zip /storage/media/100/local/files/Docs/Download/xxx.zip
将文件从本地发送到手机。
NEXT 设备上模拟验证应用数据迁移
申请“迁移调试”工具
HarmonyOS NEXT Developer Beta1及之后版本,厂商合作伙伴可通过IssueReporter平台提交工单,向华为方技术支持人员申请“迁移调试”工具,模拟进行数据迁移验证。公共开发者请通过“华为开发者联盟官网”->“支持”,在线提单方式获取。
如果采取在线提单方式获取,可参考如下申请:
选择 `HarmonyOS NEXT -> HarmonyOS NEXT其他` 路径,申请格式为:
邮箱:
应用名称:
申请原因:
一般提单后,一天左右即可得到“迁移调试”工具的hap下载链接。
测试`BackupExtensionAbility`逻辑
按照官方文档进行调试时,点击启用迁移
按钮后,“迁移调试”工具会调起 NEXT 应用的`BackupExtensionAbility`独立进程,在这里会处理自定义的数据恢复操作。可以通过 DevEco Studio 的Log,选择backup进程({应用包名}:backup)查看迁移日志。可以在这一数据是否迁移成功。
其他
- 在数据迁移这一步,建议使用
mv
命令,文件的迁移效率要高于copy
命令。 - HarmonyOS应用关联Android应用。
端到端验证
在完成自验证步骤后,应用需要上架到华为应用市场,开发者需要模拟终端用户将终端设备从HarmonyOS升级到HarmonyOS NEXT的场景,端到端验证应用数据迁移结果。
具体步骤参考官方文档。
总结
经过“应用分析 -> 应用适配(含适配)-> 应用上架 -> 端到端验证”几个步骤最终完成应用数据的迁移,本文主要介绍的是 应用分析
和应用适配(含验证)
部分的介绍。希望本文可以帮助到正在和即将进行数据迁移的同学们。如果有需要探讨的地方可以在下方留言。