鸿蒙操作系统最大的差异化能力之一,就是分布式技术。AbilityKit 提供的跨设备流转能力,让应用可以在手机、平板、智慧屏、车机等多种设备间无缝迁移和协同。这篇文章从技术原理和开发实践两个维度,聊聊跨端迁移和多端协同的实现方式。

一、跨设备流转的两种形态

AbilityKit 支持两种跨设备流转形态:跨端迁移多端协同。两者的技术原理不同,适用场景也不同。

1.1 跨端迁移:任务跟着用户走

跨端迁移指的是将一个设备上运行的 UIAbility 迁移到另一个设备上,迁移完成后,目标设备上的 UIAbility 继续执行任务,源设备上的 UIAbility 可以按需退出。

典型的场景:用户在平板上编辑文档,回到家后把文档迁移到智慧屏上继续编辑,利用大屏的显示优势;或者手机上看的视频,迁移到智慧屏上播放,获得更好的视听体验。

跨端迁移的核心特征是任务的单点执行。同一时刻,任务只在一个设备上运行。迁移完成后,源设备的 UIAbility 可以选择销毁或保留快照,但不再承担主要执行职责。

1.2 多端协同:多设备共同完成一个任务

多端协同指的是多个设备上的应用组件同时运行,相互配合完成一个任务。这跟跨端迁移的最大区别在于:多个设备上的组件同时活跃

典型的场景:手机上作为游戏手柄,智慧屏上显示游戏画面;或者手机上看商品详情,平板上同时显示对比列表。两个设备上的 UIAbility 各自负责不同的子任务,通过 RPC 调用进行数据同步和状态协调。

二、跨端迁移的技术实现

2.1 迁移的前提条件

跨端迁移不是随意发生的,需要满足一些前提:

  • 同一账号:源设备和目标设备需要登录同一个华为账号
  • 同一局域网:设备之间需要能够相互发现,通常要求在同一 WiFi 网络下
  • 应用支持:应用需要在 module.json5 中声明支持迁移能力
  • 组件可迁移:只有标记为可迁移的 UIAbility 才能参与迁移

2.2 声明迁移能力

module.json5 中配置 continuable 字段:

{
  "module": {
    "abilities": [
      {
        "name": "EntryAbility",
        "continuable": true
      }
    ]
  }
}

这个声明告诉系统:这个 UIAbility 支持跨端迁移。系统会在合适的时机(如检测到同账号的附近设备)向用户展示迁移入口。

2.3 迁移中的数据同步

跨端迁移的关键问题是如何把应用状态从源设备传递到目标设备。AbilityKit 提供了 onContinue 生命周期回调来处理这个逻辑。

import { UIAbility, Want } from '@kit.AbilityKit';

export default class EditorAbility extends UIAbility {
  private documentContent: string = '';
  private cursorPosition: number = 0;

  onContinue(want: Want): boolean {
    // 将要迁移的状态数据放入 want 参数
    want.parameters = {
      documentContent: this.documentContent,
      cursorPosition: this.cursorPosition,
      lastSaveTime: Date.now()
    };
    
    // 返回 true 表示同意迁移,返回 false 表示拒绝
    return true;
  }
}

onContinue 中,你需要把当前 UIAbility 的关键状态提取出来,放入 want.parameters 中。系统会将这些数据传输到目标设备,目标设备上的 UIAbility 在 onCreateonNewWant 中接收这些数据,恢复应用状态。

2.4 目标端的状态恢复

目标设备上的 UIAbility 在启动时,会通过 want 参数接收到迁移数据:

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
    // 这是迁移启动的场景
    this.documentContent = want.parameters?.documentContent as string;
    this.cursorPosition = want.parameters?.cursorPosition as number;
    
    // 通知 UI 层恢复状态
    AppStorage.setOrCreate('docContent', this.documentContent);
    AppStorage.setOrCreate('cursorPos', this.cursorPosition);
  }
}

这里有一个细节:launchParam.launchReason 可以用来判断启动原因。如果是 CONTINUATION,说明这是迁移场景,需要执行状态恢复逻辑。

2.5 UI 状态的自动恢复

Stage 模型的一个设计亮点是 UI 与业务逻辑的分离,这让跨端迁移后的 UI 恢复变得很简单。

由于 ArkUI 采用声明式语法,UI 是根据状态自动渲染的。只要迁移时把关键状态数据传递过去,目标设备上的 ArkUI 框架就能基于这些数据自动重建出相同的用户界面,无需开发者手动操作每个 UI 元素。

// 源设备上的页面
@Entry
@Component
struct EditorPage {
  @State documentContent: string = AppStorage.get<string>('docContent') ?? '';
  @State cursorPosition: number = AppStorage.get<number>('cursorPos') ?? 0;

  build() {
    Column() {
      TextArea({ text: this.documentContent })
        .caretPosition(this.cursorPosition)
    }
  }
}

迁移后,目标设备上的同一个页面会自动使用迁移过来的状态数据,UI 表现与源设备完全一致。这种"状态驱动 UI"的架构,是跨端迁移能够优雅实现的技术基础。

三、多端协同的技术实现

3.1 RPC 调用:跨设备的方法调用

多端协同的核心通信机制是 RPC(Remote Procedure Call)。鸿蒙提供了 rpc 模块,支持跨设备的方法调用和数据传递。

与 Want 机制不同,RPC 是双向的、持续的通信通道。建立连接后,两个设备上的组件可以随时相互调用方法、传递数据,而不需要每次都经过系统的调度。

3.2 建立 RPC 连接

import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { rpc } from '@kit.IPCKit';

// 获取可信设备列表
let deviceList = distributedDeviceManager.getAvailableDeviceListSync();

// 选择目标设备建立连接
let targetDevice = deviceList[0];
let connection = rpc.connect(targetDevice.networkId);

3.3 定义通信接口

多端协同需要定义清晰的通信接口。通常的做法是创建一个接口文件,双方共同遵守:

// 定义服务接口
interface IGameController extends rpc.IRemoteObject {
  onButtonPressed(buttonId: string): void;
  onJoystickMoved(x: number, y: number): void;
  getControllerStatus(): ControllerStatus;
}

interface ControllerStatus {
  batteryLevel: number;
  isConnected: boolean;
}

3.4 实际场景:手机作为游戏手柄

假设智慧屏上运行着游戏主界面(GameAbility),手机上运行着手柄控制界面(ControllerAbility)。两者的协作流程如下:

  1. 发现设备:手机端通过分布式设备管理发现附近的智慧屏
  2. 建立连接:手机端主动发起 RPC 连接到智慧屏的 GameAbility
  3. 事件传递:用户在手机上点击按钮,手机端通过 RPC 调用通知智慧屏
  4. 状态同步:智慧屏游戏状态变化时,通过 RPC 回调通知手机端更新显示
// 手机端:ControllerAbility
@Entry
@Component
struct ControllerPage {
  private remoteGame: IGameController | null = null;

  aboutToAppear(): void {
    // 连接到智慧屏的游戏服务
    this.connectToGameService();
  }

  async connectToGameService(): Promise<void> {
    let deviceList = distributedDeviceManager.getAvailableDeviceListSync();
    let tvDevice = deviceList.find(d => d.deviceType === 'smartTV');
    if (tvDevice) {
      this.remoteGame = await rpc.connect(tvDevice.networkId) as IGameController;
    }
  }

  build() {
    Column() {
      Button('跳跃')
        .onClick(() => {
          this.remoteGame?.onButtonPressed('JUMP');
        })
      
      // 摇杆控制区域
      JoystickArea()
        .onMove((x, y) => {
          this.remoteGame?.onJoystickMoved(x, y);
        })
    }
  }
}

这个架构下,手机端只负责采集用户输入并传递给智慧屏,实际的游戏逻辑渲染都在智慧屏上执行。两个设备各司其职,通过网络协同完成一个完整的游戏体验。

四、跨设备流转的架构设计要点

4.1 状态数据的精简与序列化

跨设备迁移时,状态数据需要通过网络传输。传输的数据量越大,迁移的耗时越长,用户体验越差。因此,在设计迁移状态时,要遵循最小必要原则

  • 只传递恢复界面所必需的核心状态
  • 不要传递可以通过核心状态推导出来的派生状态
  • 不要传递大对象(如图片、音频数据),可以传递引用或 ID
// 不好的做法:传递大量派生状态
want.parameters = {
  documentContent: this.documentContent,
  formattedHtml: this.generateHtml(),        // 可以从 documentContent 推导
  wordCount: this.documentContent.length,    // 可以实时计算
  thumbnail: this.generateThumbnail()         // 太大,不适合传输
};

// 好的做法:只传递核心状态
want.parameters = {
  documentId: this.documentId,               // 目标端可以从服务器拉取完整内容
  cursorPosition: this.cursorPosition,
  scrollOffset: this.scrollOffset
};

4.2 处理网络延迟和失败

跨设备通信依赖网络,网络可能不稳定。在设计多端协同功能时,要考虑:

  • 超时处理:RPC 调用设置合理的超时时间,避免长时间等待
  • 失败重试:关键操作失败后要有重试机制
  • 降级策略:当设备连接断开时,应用应该能降级为单设备模式继续运行
async callRemoteWithRetry(operation: () => Promise<void>, maxRetries: number = 3): Promise<void> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await operation();
      return;
    } catch (error) {
      if (i === maxRetries - 1) {
        // 最终失败,执行降级逻辑
        this.fallbackToLocalMode();
        throw error;
      }
      // 等待后重试
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

4.3 设备能力差异的适配

不同设备的能力差异很大。手机有触摸屏、摄像头、GPS;智慧屏有大屏幕、遥控器输入、音响系统;手表有传感器、小屏幕。多端协同时要考虑这些差异:

  • 输入方式适配:手机上用触摸,智慧屏上用遥控器,PC 上用键盘鼠标
  • 显示适配:同样的内容在不同屏幕尺寸上需要不同的布局
  • 能力检测:在运行期检测设备能力,动态调整功能
import { deviceInfo } from '@kit.BasicServicesKit';

// 获取设备类型
let deviceType = deviceInfo.deviceType;

// 根据设备类型调整交互方式
if (deviceType === 'phone') {
  // 手机:触摸操作
  this.setupTouchControls();
} else if (deviceType === 'tablet') {
  // 平板:触摸 + 手势
  this.setupTouchAndGestureControls();
} else if (deviceType === 'smartTV') {
  // 智慧屏:遥控器焦点导航
  this.setupFocusNavigation();
}

五、跨设备流转与 AbilityKit 其他能力的关系

跨设备流转不是孤立的能力,它与 AbilityKit 的其他特性紧密关联:

5.1 与 Want 机制的结合

跨设备启动组件时,同样使用 Want 机制,只是 deviceId 字段不再为空,而是指定目标设备的 ID:

let want: Want = {
  deviceId: targetDevice.networkId,  // 指定目标设备
  bundleName: 'com.example.myapp',
  abilityName: 'EditorAbility'
};

this.context.startAbility(want);

5.2 与生命周期的结合

跨端迁移会触发特定的生命周期回调。源设备触发 onContinue,目标设备以 LaunchReason.CONTINUATION 触发 onCreateonNewWant。理解这些回调的触发时机,对于正确实现迁移逻辑至关重要。

5.3 与多 Module 设计的结合

跨设备场景下,不同设备可能加载不同的 Module。比如手机上加载完整的 entry HAP,智慧屏上可能只加载包含播放功能的轻量模块。利用 feature HAP 的设备类型过滤能力,可以实现这种差异化的功能部署。

六、开发调试中的注意事项

6.1 模拟器的限制

AbilityKit 的跨设备流转移植到模拟器上时,有一些能力差异需要注意:

  • 模拟器不支持拉起垂类应用面板
  • 不支持以免安装方式拉起元服务
  • 不支持使用 App Linking 实现应用间跳转
  • 不支持使用 Deep Linking 拉起应用选择框

跨设备流转功能建议在真机上进行测试,模拟器主要用于功能逻辑验证。

6.2 日志定位

跨设备场景的调试比较复杂,因为涉及两个设备的日志。建议:

  • 在关键节点输出详细的日志,包括设备 ID、操作类型、状态数据等
  • 使用 hilog 模块统一日志格式,便于后续分析
  • 在日志中增加会话标识,方便将两个设备的日志关联起来
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0xFF00;
const TAG = '[CrossDevice]';

// 记录迁移操作
hilog.info(DOMAIN, TAG, `Migration started, source: ${sourceDeviceId}, target: ${targetDeviceId}`);

// 记录状态数据大小
let stateSize = JSON.stringify(stateData).length;
hilog.info(DOMAIN, TAG, `State data size: ${stateSize} bytes`);

七、总结一下下

跨设备流转是鸿蒙区别于其他操作系统的核心能力之一,也是"1+8+N"全场景战略的技术基础。AbilityKit 通过跨端迁移和多端协同两种形态,为开发者提供了实现分布式体验的完整工具链。

几个关键的设计原则再强调一下:

  1. 状态驱动 UI:利用 ArkUI 的声明式特性,通过状态同步实现跨设备的界面一致性
  2. 最小状态传输:迁移时只传递核心状态,减少网络传输开销
  3. 容错设计:网络不稳定是常态,多端协同要有降级和重试机制
  4. 设备能力感知:不同设备有不同的输入输出能力,应用要动态适配

跨设备开发确实比单设备开发复杂,但它带来的用户体验提升也是显著的。当用户在不同设备间无缝切换时,那种流畅感会让你的应用从"好用"变成"惊艳"。这或许是鸿蒙开发者最值得投入的方向之一。

Logo

一站式 AI 云服务平台

更多推荐