鸿蒙跨设备服务共享深度实战:从ServiceExtensionAbility到分布式服务治理
文章目录

每日一句正能量
“站在山顶的人,不会嘲笑山脚下努力攀登的人。”
真正抵达过顶峰的人,最懂得路途的险峻与付出的代价。因此,他的目光里没有居高临下的怜悯,只有对“曾经和正在路上的自己”的深切共鸣与尊重。
一、引言:当服务能力突破设备边界
在万物互联的鸿蒙生态中,"服务"不再局限于单一设备的进程边界。想象一下这样的场景:你在手机上编辑完一份工作报告,轻点"打印"按钮,客厅的智慧屏连接的打印机立即开始工作;你戴着运动手表跑步,手表实时调用手机上的健康数据分析服务,计算当前心率区间并给出训练建议;你在平板上浏览相册,点击"投屏"后,电视端的媒体播放服务无缝接管,继续展示高清照片——这些场景的背后,都是跨设备服务共享在发挥作用。
与跨设备数据共享(上一篇内容)不同,服务共享聚焦于能力的远程调用与协同执行。它不仅仅是数据的传输,更是"将设备A上的功能,像调用本地API一样在设备B上使用"。HarmonyOS通过ServiceExtensionAbility、startAbility、connectAbility以及基于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管理:
data和reply在使用完毕后必须调用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 性能优化建议
-
连接池管理:对高频调用的服务建立连接池,避免每次调用都重新建链。连接空闲超过5分钟自动释放。
-
异步RPC:耗时操作(如大文件传输)使用
MessageOption设置异步标志,避免阻塞调用方主线程。 -
批量调用:将多个小请求合并为一个批量RPC调用,减少网络往返次数。
-
服务预热:应用启动时预连接常用服务,用户触发时即可立即调用,消除首次连接延迟。
-
心跳保活:对长连接服务发送定期心跳(如每30秒),及时感知设备离线,避免半开连接。
八、总结
跨设备服务共享是HarmonyOS"超级终端"理念在能力层的核心落地点。通过本文的深入剖析,我们系统掌握了服务共享的完整技术链路:
- 架构层面:理解了服务消费端五层模型与服务提供端五层模型的对称设计,以及分布式软总线如何屏蔽底层通信差异;
- API层面:掌握了
startAbility无状态远程启动与connectAbility有状态远程连接的适用场景与实现细节; - RPC层面:理解了
IRemoteObject代理模型、MessageParcel序列化机制、onRemoteMessageRequest请求分发入口; - 治理层面:获得了服务注册发现、轮询/随机/加权/就近负载均衡、超时重试/熔断降级/健康检查等生产级方案;
- 实战层面:通过完整的跨设备智能打印服务案例,展示了从设备发现到打印执行的端到端流程。
分布式服务共享的本质,是将"设备能力边界"从用户感知中抹除。当开发者能够熟练驾驭这套能力时,所构建的应用便不再受限于单设备的硬件能力——手机可以调用智慧屏的显示能力、手表可以调用手机的健康分析能力、平板可以调用IoT设备的传感能力——这正是鸿蒙生态"一生万物,万物归一"的技术哲学在服务层的最佳诠释。
转载自:https://blog.csdn.net/u014727709/article/details/164126229
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐

所有评论(0)