目录

应用接续数据迁移

概述

在应用接续实践过程中,需要选择合适的数据迁移方案进行迁移,迁移方案的选择与数据大小和是否涉及文件迁移有直接关系,选择方案如下:

  

<100kb

>100kb

需要文件迁移

文件资产迁移

文件资产迁移

不需要文件迁移

使用want.param数据迁移

基础数据迁移

实现原理

实现原理见运作机制。

开发步骤

接入应用接续需要启用应用接续能力、配置应用启动模式类型、源端保存迁移数据和对端恢复数据,具体开发流程见应用接续概述,本文只关注在不同场景下迁移数据方案的选择。

使用want.param数据迁移

  1. 源端保存迁移数据
    export default class EntryAbility extends UIAbility {
      // ...
      async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {
        // ...
        // Migrate simple data using wantParam.param
        wantParam.wantBasicData = AppStorage.get('wantBasicData');
        // ...
        // Return AGREE to allow cross-device migration
        return AbilityConstant.OnContinueResult.AGREE;
      }
      // ...
    }
  2. 对端恢复数据
    export default class EntryAbility extends UIAbility {
      private distributedObject: distributedDataObject.DataObject | undefined = undefined;
      private storage: LocalStorage = new LocalStorage();
      private distributedFileUtil: DistributedFileUtil | undefined = undefined;
      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN, TAG, FORMAT, `Ability onCreate`);
        this.distributedFileUtil = new DistributedFileUtil(this.context);
        this.appContinuity(want, launchParam);
      }
    
      onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN, TAG, FORMAT, `Ability onNewWant`);
        this.appContinuity(want, launchParam);
      }
    
      appContinuity(want: Want, launchParam: AbilityConstant.LaunchParam) {
        if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
          if (want.parameters !== undefined) {
            // Use want.param to migrate the simple data page data
            AppStorage.setOrCreate('wantBasicData', want.parameters.wantBasicData);
          }
          // ...
          try {
            // Restore window stage with new LocalStorage instance
            this.context.restoreWindowStage(this.storage);
          } catch (err) {
            hilog.error(DOMAIN, TAG, FORMAT, `RestoreWindowStage failed. Cause : ${err}`);
          }
        }
      }
    
      // ...
    }

使用分布式对象迁移数据

[h2]基础数据迁移

  1. 源端保存迁移数据

    在源端UIAbility的onContinue()接口中创建分布式数据对象并保存数据,执行流程如下:

    1. 在onContinue()接口中使用create()接口创建分布式数据对象,将所要迁移的数据填充到分布式数据对象中。
    2. 调用genSessionId()接口生成分布式数据对象组网id,分布式数据对象调用setSessionId()方法将id传入,激活分布式数据对象。
    3. 使用save()接口将已激活的分布式数据对象持久化,确保源端退出后对端依然可以获取到数据。
    4. 将生成的sessionId通过want传递到对端,供对端激活同步使用。
    • 分布式数据对象需要先激活,再持久化,因此必须在调用setSessionId()后再调用save()接口。
    • 对于源端迁移后需要退出的应用,为了防止数据未保存完成应用就退出,应采用await的方式等待save()接口执行完毕。从API12起,onContinue()接口提供了async版本供该场景使用。
    • 当前,wantParams中“sessionId”字段在迁移流程中被系统占用,建议开发者在wantParams中定义其他key值存储该分布式数据对象生成的id,避免数据异常。
    export class ContinuationData {
      public basicDataStr: string | undefined;
      public basicDataNum: number | undefined;
      public distributedData: DistributedData | undefined;
      // ...
      constructor(
        basicDataStr: string | undefined,
        basicDataNum: number | undefined,
        distributedData: DistributedData | undefined,
        // ...
      ) {
        this.basicDataStr = basicDataStr;
        this.basicDataNum = basicDataNum;
        this.distributedData = distributedData;
        // ...
      }
      // ...
    }
    export default class EntryAbility extends UIAbility {
      private distributedObject: distributedDataObject.DataObject | undefined = undefined;
      private storage: LocalStorage = new LocalStorage();
      private distributedFileUtil: DistributedFileUtil | undefined = undefined;
      // ...
      async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {
        if (!this.distributedFileUtil) {
          return AbilityConstant.OnContinueResult.REJECT;
        }
        // ...
        let sessionId: string = distributedDataObject.genSessionId();
        wantParam.distributedSessionId = sessionId;
        // ...
        let contentInfo: ContinuationData = new ContinuationData(
          AppStorage.get('basicDataStr') ?? '',
          AppStorage.get('basicDataNum') ?? undefined,
          AppStorage.get('distributedData') ?? undefined,
          // ...
          (AppStorage.get('pageInfos') as NavPathStack).getAllPathName()[0]
        );
        let source = await contentInfo.flatAssets();
        this.distributedObject = distributedDataObject.create(this.context, source);
        this.distributedObject.setSessionId(sessionId).catch((err: BusinessError) => {
          hilog.info(DOMAIN, TAG, FORMAT, `SetSessionId failed. Cause code: ${err.code}`);
        });
        await this.distributedObject.save(wantParam.targetDevice as string).catch((err: BusinessError) => {
          hilog.info(DOMAIN, TAG, FORMAT, `Failed to save. Code: ${err.code}`);
        });
        // Return AGREE to allow cross-device migration
        return AbilityConstant.OnContinueResult.AGREE;
      }
      // ...
    }
  2. 对端恢复数据

    在对端UIAbility的onCreate()/onNewWant()中,通过加入与源端一致的分布式数据对象组网进行数据恢复。执行流程如下:

    1. 创建空的分布式数据对象,用于接收恢复的数据;
    2. 从want中读取分布式数据对象组网id;
    3. 注册on()接口监听数据变更。在收到status为restored的事件的回调中,实现数据恢复完毕时需要进行的业务操作。
    4. 调用setSessionId()加入组网,激活分布式数据对象。
    • 对端加入组网的分布式数据对象不能为临时变量,因为分布式数据对象on()接口为异步回调,可能在onCreate()/onNewWant()执行结束后才执行,临时变量被释放可能导致空指针异常。可以使用类成员变量避免该问题。
    • 对端用于创建分布式数据对象,其属性应在激活分布式数据对象设置为undefined,否则会导致新数据加入组网后覆盖源端数据,数据恢复失败。
    • 应当在激活分布式数据对象之前,调用分布式数据对象的on()接口进行注册监听,防止错过restored事件导致数据恢复失败。
    export default class EntryAbility extends UIAbility {
      private distributedObject: distributedDataObject.DataObject | undefined = undefined;
      private storage: LocalStorage = new LocalStorage();
      private distributedFileUtil: DistributedFileUtil | undefined = undefined;
      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN, TAG, FORMAT, `Ability onCreate`);
        this.distributedFileUtil = new DistributedFileUtil(this.context);
        this.appContinuity(want, launchParam);
      }
    
      onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        hilog.info(DOMAIN, TAG, FORMAT, `Ability onNewWant`);
        this.appContinuity(want, launchParam);
      }
    
      appContinuity(want: Want, launchParam: AbilityConstant.LaunchParam) {
        if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
          // ...
          let mailInfo: ContinuationData = new ContinuationData(undefined, undefined, undefined, undefined, undefined,
            undefined, undefined, undefined, undefined, undefined);
          this.distributedObject = distributedDataObject.create(this.context, mailInfo);
          // Add a data restored listener.
          try {
            this.distributedObject.on('status',
              (_sessionId: string, _networkId: string, status: 'online' | 'offline' | 'restored') => {
                if (status === 'restored') {
                  if (!this.distributedObject || !this.distributedFileUtil) {
                    return;
                  }
                  // Use the distributed object migration to transfer the basic page data
                  AppStorage.setOrCreate('basicDataStr', this.distributedObject['basicDataStr'] ?? '');
                  AppStorage.setOrCreate('basicDataNum', this.distributedObject['basicDataNum'] ?? undefined);
                  AppStorage.setOrCreate('distributedData', this.distributedObject['distributedData'] ?? undefined);
                  // ...
                  AppStorage.setOrCreate('curPageInfos', this.distributedObject['curPageInfos'] ?? undefined);
                }
              });
          } catch (err) {
            hilog.error(DOMAIN, TAG, FORMAT, `On status failed. Cause code: ${err.code}`);
          }
          let sessionId: string = want.parameters?.distributedSessionId as string;
          this.distributedObject.setSessionId(sessionId).catch((err: BusinessError) => {
            hilog.info(DOMAIN, TAG, FORMAT, `SetSessionId failed. Cause code: ${err.code}`);
          });
          try {
            // Restore window stage with new LocalStorage instance
            this.context.restoreWindowStage(this.storage);
          } catch (err) {
            hilog.error(DOMAIN, TAG, FORMAT, `RestoreWindowStage failed. Cause : ${err}`);
          }
        }
      }
    
      // ...
    }

[h2]文件资产迁移

示例代码