For app continuation, you need to select a proper data migration solution. Selecting a migration solution is directly related to data volume and whether file migration is involved, as described in the following table.
< 100 KB | > 100 KB | |
|---|---|---|
File migration is required | ||
File migration is not required |
To implement app continuation, you need to enable the app continuation capability, configure the app launch type, save migration data on the source device, and restore data on the target device. For details about the development process, see App Continuation Overview. This topic only describes how to select a data migration solution in different scenarios.

// ...
export default class EntryAbility extends UIAbility {
// ...
onContinue(wantParam: Record<string, Object>) {
const continueInput = 'Data to migrate';
if (continueInput) {
wantParam['data'] = continueInput;
}
// ...
return AbilityConstant.OnContinueResult.AGREE;
}
// ...
} export default class EntryAbility extends UIAbility {
storage: LocalStorage = new LocalStorage();
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
let continueInput = '';
if (want.parameters !== undefined) {
continueInput = JSON.stringify(want.parameters.data);
console.info(`continue input ${continueInput}`)
}
// ...
this.context.restoreWindowStage(this.storage);
}
// ...
}
// ...
} Create a distributed data object in onContinue() of the source UIAbility and save the data. The execution process is as follows:
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { distributedDataObject } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
// ...
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
class ParentObject {
mother: string
father: string
constructor(mother: string, father: string) {
this.mother = mother
this.father = father
}
}
class SourceObject {
name: string | undefined
age: number | undefined
isVis: boolean | undefined
parent: ParentObject | undefined
constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
this.name = name
this.age = age
this.isVis = isVis
this.parent = parent
}
}
export default class MigrationAbility extends UIAbility {
d_object?: distributedDataObject.DataObject;
// ...
// ...
async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
// ...
let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
let source: SourceObject = new SourceObject('jack', 18, false, parentSource);
this.d_object = distributedDataObject.create(this.context, source);
let dataSessionId: string = distributedDataObject.genSessionId();
this.d_object.setSessionId(dataSessionId);
wantParam['dataSessionId'] = dataSessionId;
try {
await this.d_object.save(wantParam.targetDevice as string);
} catch (error) {
hilog.error(DOMAIN_NUMBER, TAG, 'Failed to save. Error: ', JSON.stringify(error) ?? '');
}
// ...
}
// ...
} In onCreate() or onNewWant() of the target UIAbility, add the same networking ID to the distributed data object on the target device to restore data. The process is as follows:
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
d_object?: distributedDataObject.DataObject;
// ...
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
this.handleDistributedData(want);
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
if (want.parameters !== undefined) {
this.handleDistributedData(want);
}
}
}
handleDistributedData(want: Want) {
let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
this.d_object = distributedDataObject.create(this.context, remoteSource);
let dataSessionId = '';
if (want.parameters !== undefined) {
dataSessionId = want.parameters.dataSessionId as string;
}
this.d_object.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {
hilog.info(DOMAIN_NUMBER, TAG, 'status changed ' + sessionId + ' ' + status + ' ' + networkId);
if (status === 'restored') {
if (this.d_object) {
hilog.info(DOMAIN_NUMBER, TAG, 'restored name:' + this.d_object['name']);
hilog.info(DOMAIN_NUMBER, TAG, 'restored parents:' + JSON.stringify(this.d_object['parent']));
// ...
}
}
});
this.d_object.setSessionId(dataSessionId);
}
// ...
// ...
} For file data such as images and documents, you need to convert the data to the commonType.Asset type and then encapsulate the data into distributed data objects for migration. The migration implementation is similar to that of common distributed data objects. The following describes only the differences:
let distributedDir: string = this.context.distributedFilesDir; // Obtain the distributed file directory.
let fileName: string = '/test.txt'; // File name.
let filePath: string = distributedDir + fileName; // File path.
try {
let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
hilog.info(DOMAIN_NUMBER, TAG, 'Create file success.');
fileIo.writeSync(file.fd, '[Sample] Insert file content here.');
fileIo.closeSync(file.fd);
} catch (error) {
let err: BusinessError = error as BusinessError;
hilog.error(DOMAIN_NUMBER, TAG,
`Failed to openSync / writeSync / closeSync. Code: ${err.code}, message: ${err.message}`);
} let distributedUri: string = fileUri.getUriFromPath(filePath);
let ctime: string = '';
let mtime: string = '';
let size: string = '';
await fileIo.stat(filePath).then((stat: fileIo.Stat) => {
ctime = stat.ctime.toString();
mtime = stat.mtime.toString();
size = stat.size.toString();
})
let attachment: commonType.Asset = {
name: fileName,
uri: distributedUri,
path: filePath,
createTime: ctime,
modifyTime: mtime,
size: size,
} let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
let source: SourceObject = new SourceObject('jack', 18, false, parentSource, attachment);
this.d_object = distributedDataObject.create(this.context, source); Then, add the data object to the network and persist it. The procedure is the same as the migration of a common data object on the source device.
If multiple assets in an app need to be synchronized, use either of the following methods:
To implement method 1, you can add more assets by referring to the method of adding an asset. To implement method 2, refer to the code snippet below:
import { distributedDataObject, commonType } from '@kit.ArkData';
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
// ...
class SourceObject {
name: string | undefined
assets: Object | undefined
constructor(name: string | undefined, assets: Object | undefined) {
this.name = name
this.assets = assets;
}
}
export default class MigrationAbility_multi_asset extends UIAbility {
d_object?: distributedDataObject.DataObject;
// ...
GetAssetsWrapper(assets: commonType.Assets): Record<string, commonType.Asset> {
let wrapper: Record<string, commonType.Asset> = {}
let num: number = assets.length;
for (let i: number = 0; i < num; i++) {
wrapper[`asset${i}`] = assets[i];
}
return wrapper;
}
async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {
// ...
let attachment1: commonType.Asset = {
// ...
}
let attachment2: commonType.Asset = {
// ...
}
let assets: commonType.Assets = [];
assets.push(attachment1);
assets.push(attachment2);
let assetsWrapper: Object = this.GetAssetsWrapper(assets);
let source: SourceObject = new SourceObject('jack', assetsWrapper);
this.d_object = distributedDataObject.create(this.context, source);
// ...
}
}