在这里插入图片描述

每日一句正能量

“站在山顶的人,不会嘲笑山脚下努力攀登的人。”
真正抵达过顶峰的人,最懂得路途的险峻与付出的代价。因此,他的目光里没有居高临下的怜悯,只有对“曾经和正在路上的自己”的深切共鸣与尊重。

一、引言:当服务能力突破设备边界

在万物互联的鸿蒙生态中,"服务"不再局限于单一设备的进程边界。想象一下这样的场景:你在手机上编辑完一份工作报告,轻点"打印"按钮,客厅的智慧屏连接的打印机立即开始工作;你戴着运动手表跑步,手表实时调用手机上的健康数据分析服务,计算当前心率区间并给出训练建议;你在平板上浏览相册,点击"投屏"后,电视端的媒体播放服务无缝接管,继续展示高清照片——这些场景的背后,都是跨设备服务共享在发挥作用。

与跨设备数据共享(上一篇内容)不同,服务共享聚焦于能力的远程调用与协同执行。它不仅仅是数据的传输,更是"将设备A上的功能,像调用本地API一样在设备B上使用"。HarmonyOS通过ServiceExtensionAbilitystartAbilityconnectAbility以及基于IRemoteObject的RPC机制,将跨设备服务调用从"网络编程问题"降维为"接口调用问题"。

本文将基于HarmonyOS 5+(API 12+)的最新接口规范,从分布式服务架构到RPC远程调用,完整解析跨设备服务共享的全链路实现,并深入探讨服务注册发现、负载均衡、容错恢复等生产级议题。


二、技术架构:分布式服务共享的五层模型

理解分布式服务共享的架构,是避免"服务调用失败"、"连接泄漏"等问题的第一步。以下架构图展示了从服务消费端到服务提供端的完整层次:

在这里插入图片描述

2.1 服务消费端(Consumer)

服务消费端是发起跨设备服务调用的主体,通常运行在用户手持设备(如手机)上。其内部可细分为五层:

  • 业务应用层:用户交互界面,如打印文档、查询健康数据、远程控制等场景入口。
  • 服务代理层:通过ServiceProxy封装远程调用细节,消费端像调用本地方法一样使用远端服务。
  • 能力协商层:通过DeviceManager发现周边设备,按能力标签筛选目标设备,校验服务版本兼容性。
  • 安全认证层:同华为账号认证、应用BundleName签名校验、运行时权限检查。
  • 通信传输层:分布式软总线自动选择星闪/Wi-Fi/蓝牙最优通道。

2.2 服务提供端(Provider)

服务提供端是暴露能力供其他设备调用的主体,通常运行在资源富集设备(如平板、智慧屏、IoT设备)上:

  • ServiceExtensionAbility:继承自ServiceExtensionAbility的服务组件,提供onCreate/onDestroy/onConnect/onDisconnect生命周期管理。
  • 服务实现层:具体的业务逻辑,如打印任务处理、健康数据查询、硬件控制等。
  • 服务注册层:通过ServiceStub将服务能力暴露为IRemoteObject接口,供远端消费端连接。
  • 安全认证层:校验调用方身份、拦截未授权请求、记录访问日志。
  • 通信传输层:与消费端共享同一套分布式软总线基础设施。

2.3 分布式软总线:透明通信底座

分布式软总线作为底层通信抽象层,屏蔽了设备发现、协议协商、安全加密等复杂细节。消费端和提供端只需关注"调用什么服务",无需关心"如何找到设备"或"用什么协议通信"。


三、核心API:startAbility 与 connectAbility

HarmonyOS提供两种跨设备服务调用模式:startAbility(无状态远程启动)和connectAbility(有状态远程连接)。

在这里插入图片描述

3.1 startAbility:远程启动服务

startAbility用于在远端设备上启动一个ServiceExtensionAbility,适用于"触发即走"的场景,如远程唤醒打印服务、启动健康数据采集等。

import { common, Want } from '@kit.AbilityKit';
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

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

export class ServiceLauncher {
  private context: common.UIAbilityContext;
  private dmInstance: distributedDeviceManager.DeviceManager | null = null;

  constructor(context: common.UIAbilityContext) {
    this.context = context;
  }

  async initialize(): Promise<void> {
    this.dmInstance = distributedDeviceManager.createDeviceManager(
      this.context.applicationInfo.name
    );
    hilog.info(DOMAIN, TAG, 'DeviceManager initialized for service discovery');
  }

  // 发现支持指定服务的设备
  async discoverServiceProviders(serviceType: string): Promise<Array<{
    networkId: string;
    deviceName: string;
    deviceType: string;
  }>> {
    if (!this.dmInstance) return [];

    try {
      const devices = this.dmInstance.getAvailableDeviceListSync();
      const providers = devices
        .filter(device => {
          // 实际工程中:通过设备类型或能力标签筛选
          return device.deviceType === 'tablet' || device.deviceType === 'smartScreen';
        })
        .map(device => ({
          networkId: device.networkId,
          deviceName: device.deviceName,
          deviceType: device.deviceType
        }));

      hilog.info(DOMAIN, TAG, `Found ${providers.length} service providers`);
      return providers;
    } catch (err) {
      hilog.error(DOMAIN, TAG, `Discovery failed: ${(err as BusinessError).message}`);
      return [];
    }
  }

  // 远程启动服务
  async startRemoteService(networkId: string, serviceName: string): Promise<void> {
    const want: Want = {
      deviceId: networkId,
      bundleName: this.context.applicationInfo.name,
      abilityName: serviceName,
      flags: 0  // 分布式调用标志
    };

    try {
      await this.context.startAbility(want);
      hilog.info(DOMAIN, TAG, `Remote service started: ${serviceName} on ${networkId}`);
    } catch (err) {
      hilog.error(DOMAIN, TAG, `Start remote service failed: ${(err as BusinessError).message}`);
      throw err;
    }
  }
}

3.2 connectAbility:建立远程服务连接

connectAbility用于与远端ServiceExtensionAbility建立持久连接,获取IRemoteObject代理对象,进行双向RPC调用。这是跨设备服务共享的核心机制。

import { common, Want } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

export class ServiceConnector {
  private context: common.UIAbilityContext;
  private connectionMap: Map<string, common.ServiceConnection> = new Map();
  private proxyMap: Map<string, rpc.IRemoteObject> = new Map();

  constructor(context: common.UIAbilityContext) {
    this.context = context;
  }

  // 连接远程服务
  async connectRemoteService(networkId: string, abilityName: string): Promise<rpc.IRemoteObject | null> {
    const connectionId = `${networkId}#${abilityName}`;

    // 检查是否已连接
    if (this.proxyMap.has(connectionId)) {
      hilog.info(DOMAIN, TAG, `Reusing existing connection: ${connectionId}`);
      return this.proxyMap.get(connectionId)!;
    }

    const want: Want = {
      deviceId: networkId,
      bundleName: this.context.applicationInfo.name,
      abilityName: abilityName
    };

    return new Promise((resolve, reject) => {
      const connection: common.ServiceConnection = {
        onConnect: (elementName, remoteObject) => {
          hilog.info(DOMAIN, TAG, `Service connected: ${elementName.abilityName}`);
          this.proxyMap.set(connectionId, remoteObject);
          resolve(remoteObject);
        },
        onDisconnect: (elementName) => {
          hilog.info(DOMAIN, TAG, `Service disconnected: ${elementName.abilityName}`);
          this.proxyMap.delete(connectionId);
          this.connectionMap.delete(connectionId);
        },
        onFailed: (code) => {
          hilog.error(DOMAIN, TAG, `Service connection failed: code=${code}`);
          reject(new Error(`Connection failed: ${code}`));
        }
      };

      this.context.connectAbility(want, connection)
        .then(() => {
          this.connectionMap.set(connectionId, connection);
          hilog.info(DOMAIN, TAG, `Connection request sent: ${connectionId}`);
        })
        .catch((err) => {
          hilog.error(DOMAIN, TAG, `Connect ability failed: ${(err as BusinessError).message}`);
          reject(err);
        });
    });
  }

  // 断开远程服务连接
  async disconnectRemoteService(networkId: string, abilityName: string): Promise<void> {
    const connectionId = `${networkId}#${abilityName}`;
    const connection = this.connectionMap.get(connectionId);

    if (connection) {
      try {
        await this.context.disconnectAbility(connection);
        this.connectionMap.delete(connectionId);
        this.proxyMap.delete(connectionId);
        hilog.info(DOMAIN, TAG, `Service disconnected: ${connectionId}`);
      } catch (err) {
        hilog.error(DOMAIN, TAG, `Disconnect failed: ${(err as BusinessError).message}`);
      }
    }
  }

  // 断开所有连接(应用退出时调用)
  async disconnectAll(): Promise<void> {
    for (const [connectionId, connection] of this.connectionMap.entries()) {
      try {
        await this.context.disconnectAbility(connection);
        hilog.info(DOMAIN, TAG, `Disconnected: ${connectionId}`);
      } catch (err) {
        hilog.warn(DOMAIN, TAG, `Disconnect error: ${connectionId}`);
      }
    }
    this.connectionMap.clear();
    this.proxyMap.clear();
  }

  getProxy(networkId: string, abilityName: string): rpc.IRemoteObject | undefined {
    return this.proxyMap.get(`${networkId}#${abilityName}`);
  }
}

关键设计要点

  • 连接复用:同一networkId+abilityName的连接应复用,避免重复建链开销。
  • 连接生命周期connectAbility建立的连接必须在使用完毕后调用disconnectAbility释放,否则会导致远端服务进程无法销毁,造成资源泄漏。
  • 异常处理onFailed回调必须处理,常见失败原因包括目标设备离线、服务未安装、权限不足等。

四、ServiceExtensionAbility 与 RPC 远程调用

4.1 生命周期管理

ServiceExtensionAbility是鸿蒙中提供后台服务的核心组件,其生命周期与远程调用模型如下:

在这里插入图片描述

import { ServiceExtensionAbility, Want } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

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

// 定义服务接口常量
enum PrinterServiceCode {
  PRINT_DOCUMENT = 1,
  GET_PRINTER_STATUS = 2,
  CANCEL_PRINT = 3
}

export default class PrinterServiceAbility extends ServiceExtensionAbility {
  onCreate(want: Want): void {
    hilog.info(DOMAIN, TAG, 'PrinterService onCreate');
    // 初始化打印机驱动、连接硬件等
  }

  onDestroy(): void {
    hilog.info(DOMAIN, TAG, 'PrinterService onDestroy');
    // 释放打印机资源
  }

  onConnect(want: Want): rpc.RemoteObject {
    hilog.info(DOMAIN, TAG, 'PrinterService onConnect');
    // 返回IRemoteObject,供消费端进行RPC调用
    return new PrinterServiceStub('PrinterService');
  }

  onDisconnect(want: Want): void {
    hilog.info(DOMAIN, TAG, 'PrinterService onDisconnect');
  }
}

// 服务端Stub:处理远程调用请求
class PrinterServiceStub extends rpc.RemoteObject {
  private printQueue: Array<{ docId: string; content: string; status: string }> = [];

  constructor(descriptor: string) {
    super(descriptor);
  }

  // 处理远程消息请求(核心RPC入口)
  async onRemoteMessageRequest(code: number, data: rpc.MessageParcel,
                                reply: rpc.MessageParcel,
                                option: rpc.MessageOption): Promise<boolean> {
    hilog.info(DOMAIN, TAG, `Remote request received: code=${code}`);

    // 安全校验:校验调用方权限(生产环境必须)
    // const callingUid = rpc.IPCSkeleton.getCallingUid();
    // if (!this.isAuthorized(callingUid)) { return false; }

    switch (code) {
      case PrinterServiceCode.PRINT_DOCUMENT:
        return this.handlePrintDocument(data, reply);
      case PrinterServiceCode.GET_PRINTER_STATUS:
        return this.handleGetStatus(reply);
      case PrinterServiceCode.CANCEL_PRINT:
        return this.handleCancelPrint(data, reply);
      default:
        hilog.warn(DOMAIN, TAG, `Unknown request code: ${code}`);
        return false;
    }
  }

  private handlePrintDocument(data: rpc.MessageParcel, reply: rpc.MessageParcel): boolean {
    try {
      const docId = data.readString();
      const content = data.readString();
      const copies = data.readInt();

      hilog.info(DOMAIN, TAG, `Print request: docId=${docId}, copies=${copies}`);

      // 将打印任务加入队列
      this.printQueue.push({ docId, content, status: 'queued' });

      // 异步执行打印(实际工程中应使用TaskDispatcher)
      this.executePrint(docId, content, copies);

      // 写入响应
      reply.writeInt(0); // 成功码
      reply.writeString(docId);
      return true;
    } catch (err) {
      hilog.error(DOMAIN, TAG, `Print document failed: ${err}`);
      reply.writeInt(-1); // 失败码
      return false;
    }
  }

  private handleGetStatus(reply: rpc.MessageParcel): boolean {
    const status = {
      isOnline: true,
      queueLength: this.printQueue.length,
      currentJob: this.printQueue.find(job => job.status === 'printing')?.docId ?? ''
    };
    reply.writeString(JSON.stringify(status));
    return true;
  }

  private handleCancelPrint(data: rpc.MessageParcel, reply: rpc.MessageParcel): boolean {
    const docId = data.readString();
    const index = this.printQueue.findIndex(job => job.docId === docId);
    if (index !== -1) {
      this.printQueue[index].status = 'cancelled';
      reply.writeInt(0);
      hilog.info(DOMAIN, TAG, `Print cancelled: ${docId}`);
    } else {
      reply.writeInt(-1);
    }
    return true;
  }

  private async executePrint(docId: string, content: string, copies: number): Promise<void> {
    const job = this.printQueue.find(j => j.docId === docId);
    if (!job) return;

    job.status = 'printing';
    hilog.info(DOMAIN, TAG, `Printing started: ${docId}`);

    // 模拟打印耗时
    await new Promise(resolve => setTimeout(resolve, 3000));

    job.status = 'completed';
    hilog.info(DOMAIN, TAG, `Printing completed: ${docId}`);
  }
}

4.2 客户端Proxy封装

import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

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

enum PrinterServiceCode {
  PRINT_DOCUMENT = 1,
  GET_PRINTER_STATUS = 2,
  CANCEL_PRINT = 3
}

export class PrinterServiceProxy {
  private proxy: rpc.IRemoteObject;

  constructor(proxy: rpc.IRemoteObject) {
    this.proxy = proxy;
  }

  // 远程打印文档
  async printDocument(docId: string, content: string, copies: number = 1): Promise<string | null> {
    const data = rpc.MessageParcel.create();
    const reply = rpc.MessageParcel.create();
    const option = new rpc.MessageOption();

    try {
      data.writeString(docId);
      data.writeString(content);
      data.writeInt(copies);

      const result = await this.proxy.sendRequest(PrinterServiceCode.PRINT_DOCUMENT, data, reply, option);
      if (result.errCode === 0) {
        const code = reply.readInt();
        if (code === 0) {
          const returnedDocId = reply.readString();
          hilog.info(DOMAIN, TAG, `Print success: ${returnedDocId}`);
          return returnedDocId;
        }
      }
      hilog.error(DOMAIN, TAG, `Print failed: errCode=${result.errCode}`);
      return null;
    } finally {
      data.reclaim();
      reply.reclaim();
    }
  }

  // 查询打印机状态
  async getPrinterStatus(): Promise<{ isOnline: boolean; queueLength: number; currentJob: string } | null> {
    const data = rpc.MessageParcel.create();
    const reply = rpc.MessageParcel.create();
    const option = new rpc.MessageOption();

    try {
      const result = await this.proxy.sendRequest(PrinterServiceCode.GET_PRINTER_STATUS, data, reply, option);
      if (result.errCode === 0) {
        const statusJson = reply.readString();
        return JSON.parse(statusJson);
      }
      return null;
    } finally {
      data.reclaim();
      reply.reclaim();
    }
  }

  // 取消打印任务
  async cancelPrint(docId: string): Promise<boolean> {
    const data = rpc.MessageParcel.create();
    const reply = rpc.MessageParcel.create();
    const option = new rpc.MessageOption();

    try {
      data.writeString(docId);
      const result = await this.proxy.sendRequest(PrinterServiceCode.CANCEL_PRINT, data, reply, option);
      if (result.errCode === 0) {
        return reply.readInt() === 0;
      }
      return false;
    } finally {
      data.reclaim();
      reply.reclaim();
    }
  }
}

RPC调用关键要点

  • MessageParcel管理datareply在使用完毕后必须调用reclaim()释放,否则造成内存泄漏。
  • 序列化顺序:写入和读取的顺序必须严格一致,否则会导致数据解析错误。
  • 错误码处理sendRequest返回的errCode需校验,reply中的业务错误码需单独处理。

五、分布式服务治理:注册发现、负载均衡与容错恢复

在生产环境中,单一设备提供服务往往不够健壮。分布式服务治理确保在设备上下线、网络波动等异常场景下,服务调用依然可靠。

在这里插入图片描述

5.1 服务注册与发现

import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

interface ServiceEndpoint {
  networkId: string;
  deviceName: string;
  deviceType: string;
  serviceName: string;
  capabilities: string[];
  priority: number; // 优先级:智慧屏 > 平板 > 其他
}

export class ServiceRegistry {
  private dmInstance: distributedDeviceManager.DeviceManager | null = null;
  private endpoints: Map<string, ServiceEndpoint> = new Map();

  async initialize(bundleName: string): Promise<void> {
    this.dmInstance = distributedDeviceManager.createDeviceManager(bundleName);

    // 监听设备上下线
    this.dmInstance.on('deviceStateChange', (data) => {
      for (const device of data) {
        if (device.state === distributedDeviceManager.DeviceState.ONLINE) {
          this.registerEndpoint(device.device);
        } else if (device.state === distributedDeviceManager.DeviceState.OFFLINE) {
          this.unregisterEndpoint(device.device.networkId);
        }
      }
    });

    // 初始扫描
    this.scanExistingDevices();
  }

  private registerEndpoint(device: distributedDeviceManager.DeviceBasicInfo): void {
    // 根据设备类型推断服务能力
    const capabilities = this.inferCapabilities(device.deviceType);
    const priority = this.getDevicePriority(device.deviceType);

    const endpoint: ServiceEndpoint = {
      networkId: device.networkId,
      deviceName: device.deviceName,
      deviceType: device.deviceType,
      serviceName: 'PrinterService',
      capabilities,
      priority
    };

    this.endpoints.set(device.networkId, endpoint);
    hilog.info(0xFF00, 'Registry', `Endpoint registered: ${device.deviceName}`);
  }

  private unregisterEndpoint(networkId: string): void {
    this.endpoints.delete(networkId);
    hilog.info(0xFF00, 'Registry', `Endpoint unregistered: ${networkId}`);
  }

  private scanExistingDevices(): void {
    if (!this.dmInstance) return;
    const devices = this.dmInstance.getAvailableDeviceListSync();
    for (const device of devices) {
      this.registerEndpoint(device);
    }
  }

  private inferCapabilities(deviceType: string): string[] {
    const map: Record<string, string[]> = {
      'tablet': ['print', 'display', 'storage'],
      'smartScreen': ['print', 'display', 'media'],
      'wearable': ['sensor', 'health'],
      'phone': ['print', 'camera', 'location']
    };
    return map[deviceType] ?? [];
  }

  private getDevicePriority(deviceType: string): number {
    const priorities: Record<string, number> = {
      'smartScreen': 100,
      'tablet': 80,
      'phone': 60,
      'wearable': 40
    };
    return priorities[deviceType] ?? 0;
  }

  // 按能力筛选服务节点
  findEndpointsByCapability(capability: string): ServiceEndpoint[] {
    return Array.from(this.endpoints.values())
      .filter(ep => ep.capabilities.includes(capability))
      .sort((a, b) => b.priority - a.priority);
  }

  getEndpoint(networkId: string): ServiceEndpoint | undefined {
    return this.endpoints.get(networkId);
  }
}

5.2 负载均衡策略

export class LoadBalancer {
  private currentIndex: number = 0;

  // 轮询策略
  roundRobin(endpoints: ServiceEndpoint[]): ServiceEndpoint | null {
    if (endpoints.length === 0) return null;
    const selected = endpoints[this.currentIndex % endpoints.length];
    this.currentIndex++;
    return selected;
  }

  // 随机策略
  random(endpoints: ServiceEndpoint[]): ServiceEndpoint | null {
    if (endpoints.length === 0) return null;
    return endpoints[Math.floor(Math.random() * endpoints.length)];
  }

  // 加权轮询(按设备性能优先级)
  weightedRoundRobin(endpoints: ServiceEndpoint[]): ServiceEndpoint | null {
    if (endpoints.length === 0) return null;
    const totalWeight = endpoints.reduce((sum, ep) => sum + ep.priority, 0);
    let random = Math.random() * totalWeight;
    for (const ep of endpoints) {
      random -= ep.priority;
      if (random <= 0) return ep;
    }
    return endpoints[0];
  }

  // 就近优先(网络延迟最低)
  proximityFirst(endpoints: ServiceEndpoint[]): ServiceEndpoint | null {
    // 实际工程中:通过ping或软总线延迟测量
    // 简化版:星闪设备优先,其次WiFi,最后蓝牙
    const proximityOrder = ['smartScreen', 'tablet', 'phone', 'wearable'];
    for (const type of proximityOrder) {
      const match = endpoints.find(ep => ep.deviceType === type);
      if (match) return match;
    }
    return endpoints[0] ?? null;
  }
}

5.3 容错与恢复机制

import { hilog } from '@kit.PerformanceAnalysisKit';

interface RetryConfig {
  maxRetries: number;
  baseDelay: number; // 毫秒
  maxDelay: number;
}

export class ServiceFaultTolerance {
  private retryConfig: RetryConfig = {
    maxRetries: 3,
    baseDelay: 1000,
    maxDelay: 8000
  };

  private failureCounts: Map<string, number> = new Map();
  private circuitOpen: Set<string> = new Set();

  // 指数退避重试
  async executeWithRetry<T>(
    operation: () => Promise<T>,
    serviceId: string
  ): Promise<T | null> {
    if (this.circuitOpen.has(serviceId)) {
      hilog.warn(0xFF00, 'FaultTolerance', `Circuit open for ${serviceId}, fast fail`);
      return null;
    }

    for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
      try {
        const result = await operation();
        this.failureCounts.delete(serviceId);
        return result;
      } catch (err) {
        hilog.warn(0xFF00, 'FaultTolerance', `Attempt ${attempt + 1} failed: ${err}`);

        if (attempt === this.retryConfig.maxRetries) {
          this.recordFailure(serviceId);
          throw err;
        }

        // 指数退避延迟
        const delay = Math.min(
          this.retryConfig.baseDelay * Math.pow(2, attempt),
          this.retryConfig.maxDelay
        );
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
    return null;
  }

  private recordFailure(serviceId: string): void {
    const count = (this.failureCounts.get(serviceId) ?? 0) + 1;
    this.failureCounts.set(serviceId, count);

    // 连续失败5次后打开熔断器
    if (count >= 5) {
      this.circuitOpen.add(serviceId);
      hilog.error(0xFF00, 'FaultTolerance', `Circuit opened for ${serviceId}`);

      // 30秒后尝试半开
      setTimeout(() => {
        this.circuitOpen.delete(serviceId);
        this.failureCounts.delete(serviceId);
        hilog.info(0xFF00, 'FaultTolerance', `Circuit half-open for ${serviceId}`);
      }, 30000);
    }
  }

  // 健康检查
  async healthCheck(networkId: string, pingOperation: () => Promise<boolean>): Promise<boolean> {
    try {
      const isHealthy = await pingOperation();
      if (isHealthy) {
        this.failureCounts.delete(networkId);
        this.circuitOpen.delete(networkId);
      }
      return isHealthy;
    } catch {
      return false;
    }
  }
}

六、实战案例:跨设备智能打印服务

以下是一个完整的跨设备智能打印服务实现,整合服务发现、负载均衡、RPC调用与容错恢复:

import { common, Want } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { promptAction } from '@kit.ArkUI';

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

@Entry
@Component
struct SmartPrinterPage {
  @State documentContent: string = '';
  @State printStatus: string = '就绪';
  @State selectedDevice: string = '';
  @State availableDevices: Array<{ networkId: string; name: string }> = [];

  private context = getContext(this) as common.UIAbilityContext;
  private serviceLauncher: ServiceLauncher = new ServiceLauncher(this.context);
  private serviceConnector: ServiceConnector = new ServiceConnector(this.context);
  private serviceRegistry: ServiceRegistry = new ServiceRegistry();
  private loadBalancer: LoadBalancer = new LoadBalancer();
  private faultTolerance: ServiceFaultTolerance = new ServiceFaultTolerance();

  aboutToAppear(): void {
    this.initialize();
  }

  aboutToDisappear(): void {
    this.serviceConnector.disconnectAll();
  }

  async initialize(): Promise<void> {
    await this.serviceLauncher.initialize();
    await this.serviceRegistry.initialize(this.context.applicationInfo.name);
    await this.refreshDevices();
  }

  async refreshDevices(): Promise<void> {
    const endpoints = this.serviceRegistry.findEndpointsByCapability('print');
    this.availableDevices = endpoints.map(ep => ({
      networkId: ep.networkId,
      name: `${ep.deviceName} (${ep.deviceType})`
    }));
  }

  async onPrintClick(): Promise<void> {
    if (!this.documentContent.trim()) {
      promptAction.showToast({ message: '请先输入文档内容', duration: 1500 });
      return;
    }

    try {
      this.printStatus = '正在查找打印机...';

      // 1. 选择目标设备(负载均衡)
      const endpoints = this.serviceRegistry.findEndpointsByCapability('print');
      if (endpoints.length === 0) {
        promptAction.showToast({ message: '未找到可用打印机', duration: 1500 });
        this.printStatus = '未找到打印机';
        return;
      }

      const target = this.selectedDevice
        ? endpoints.find(ep => ep.networkId === this.selectedDevice)
        : this.loadBalancer.proximityFirst(endpoints);

      if (!target) {
        this.printStatus = '设备选择失败';
        return;
      }

      // 2. 启动远程服务
      this.printStatus = `正在连接 ${target.deviceName}...`;
      await this.serviceLauncher.startRemoteService(target.networkId, 'PrinterServiceAbility');

      // 3. 建立连接(带容错重试)
      const proxy = await this.faultTolerance.executeWithRetry(
        () => this.serviceConnector.connectRemoteService(target.networkId, 'PrinterServiceAbility'),
        target.networkId
      );

      if (!proxy) {
        this.printStatus = '连接失败,已触发熔断';
        return;
      }

      // 4. 执行打印
      this.printStatus = '正在打印...';
      const printerProxy = new PrinterServiceProxy(proxy);
      const docId = await this.faultTolerance.executeWithRetry(
        () => printerProxy.printDocument(
          `doc_${Date.now()}`,
          this.documentContent,
          1
        ),
        target.networkId
      );

      if (docId) {
        this.printStatus = `打印成功: ${docId}`;
        promptAction.showToast({ message: '打印任务已提交', duration: 1500 });
      } else {
        this.printStatus = '打印失败';
      }

      // 5. 查询状态(可选)
      const status = await printerProxy.getPrinterStatus();
      hilog.info(DOMAIN, TAG, `Printer status: ${JSON.stringify(status)}`);

    } catch (err) {
      hilog.error(DOMAIN, TAG, `Print error: ${err}`);
      this.printStatus = '打印异常';
      promptAction.showToast({ message: '打印失败,请重试', duration: 2000 });
    }
  }

  build() {
    Column({ space: 16 }) {
      Text('跨设备智能打印')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333')

      TextArea({ text: this.documentContent })
        .placeholder('输入要打印的文档内容...')
        .height(200)
        .onChange((value) => { this.documentContent = value; })

      if (this.availableDevices.length > 0) {
        Text('选择打印机:')
          .fontSize(14)
          .fontColor('#666')
          .width('100%')

        List() {
          ForEach(this.availableDevices, (device: { networkId: string; name: string }) => {
            ListItem() {
              Row() {
                Text(device.name)
                  .fontSize(16)
                  .fontColor(this.selectedDevice === device.networkId ? '#2196F3' : '#333')
                  .layoutWeight(1)

                if (this.selectedDevice === device.networkId) {
                  Text('✓')
                    .fontSize(18)
                    .fontColor('#2196F3')
                }
              }
              .width('100%')
              .padding(12)
              .backgroundColor(this.selectedDevice === device.networkId ? '#E3F2FD' : '#FAFAFA')
              .borderRadius(8)
              .onClick(() => { this.selectedDevice = device.networkId; })
            }
          })
        }
        .width('100%')
        .height(120)
      }

      Text(`状态: ${this.printStatus}`)
        .fontSize(14)
        .fontColor('#666')
        .width('100%')

      Button('开始打印')
        .width('80%')
        .height(50)
        .fontSize(18)
        .backgroundColor('#2196F3')
        .onClick(() => this.onPrintClick())

      Button('刷新设备列表')
        .width('80%')
        .height(45)
        .fontSize(16)
        .backgroundColor('#4CAF50')
        .onClick(() => this.refreshDevices())
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#F5F5F5')
  }
}

七、工程化最佳实践与避坑指南

7.1 module.json5 配置

{
  "module": {
    "name": "entry",
    "type": "entry",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      },
      {
        "name": "PrinterServiceAbility",
        "srcEntry": "./ets/service/PrinterServiceAbility.ets",
        "type": "serviceExtension",
        "description": "跨设备打印服务",
        "exported": true
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "$string:dist_data_sync_reason"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_TASK_SCHEDULING",
        "reason": "$string:dist_task_scheduling_reason"
      }
    ]
  }
}

7.2 避坑清单

坑点现象解决方案
未释放连接远端服务进程无法销毁,系统资源耗尽必须在aboutToDisappear()或应用退出时调用disconnectAbility()
MessageParcel未回收内存泄漏,应用卡顿finally块中调用data.reclaim()reply.reclaim()
序列化顺序不一致RPC调用返回乱码或异常写入和读取顺序必须严格一致
忽略onFailed回调连接失败无感知,后续调用空指针异常必须处理onFailed,并提供降级方案
未做版本校验新旧设备接口不兼容,调用崩溃onRemoteMessageRequest中校验接口版本号
缺少权限声明startAbility直接抛出权限异常module.json5声明权限,运行时动态申请
服务单例问题多次startAbility创建多个服务实例使用singleton启动模式或手动管理实例状态
未处理设备离线调用离线设备服务,长时间挂起使用健康检查+熔断机制,快速失败

7.3 性能优化建议

  1. 连接池管理:对高频调用的服务建立连接池,避免每次调用都重新建链。连接空闲超过5分钟自动释放。

  2. 异步RPC:耗时操作(如大文件传输)使用MessageOption设置异步标志,避免阻塞调用方主线程。

  3. 批量调用:将多个小请求合并为一个批量RPC调用,减少网络往返次数。

  4. 服务预热:应用启动时预连接常用服务,用户触发时即可立即调用,消除首次连接延迟。

  5. 心跳保活:对长连接服务发送定期心跳(如每30秒),及时感知设备离线,避免半开连接。


八、总结

跨设备服务共享是HarmonyOS"超级终端"理念在能力层的核心落地点。通过本文的深入剖析,我们系统掌握了服务共享的完整技术链路:

  • 架构层面:理解了服务消费端五层模型与服务提供端五层模型的对称设计,以及分布式软总线如何屏蔽底层通信差异;
  • API层面:掌握了startAbility无状态远程启动与connectAbility有状态远程连接的适用场景与实现细节;
  • RPC层面:理解了IRemoteObject代理模型、MessageParcel序列化机制、onRemoteMessageRequest请求分发入口;
  • 治理层面:获得了服务注册发现、轮询/随机/加权/就近负载均衡、超时重试/熔断降级/健康检查等生产级方案;
  • 实战层面:通过完整的跨设备智能打印服务案例,展示了从设备发现到打印执行的端到端流程。

分布式服务共享的本质,是将"设备能力边界"从用户感知中抹除。当开发者能够熟练驾驭这套能力时,所构建的应用便不再受限于单设备的硬件能力——手机可以调用智慧屏的显示能力、手表可以调用手机的健康分析能力、平板可以调用IoT设备的传感能力——这正是鸿蒙生态"一生万物,万物归一"的技术哲学在服务层的最佳诠释。


转载自:https://blog.csdn.net/u014727709/article/details/164126229
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐