HarmonyOS —— 使用 URPC 进行远程程序调用实战笔记

HarmonyOS —— 使用 URPC 进行远程程序调用实战笔记

Remote Communication Kit 里前面都是 HTTP/Session 的玩法,
这一节换个路子:直接用 URPC(Unified Remote Procedure Call)做“远程函数调用”

可以把 URPC 理解成:

“我在本地像调一个函数一样写 call("uploadFile", req, resp)
实际上是通过网络把请求丢给远端服务,拿回来一个响应对象。”


一、URPC 能力 & 设备支持快速扫一眼

鸿蒙开发者第四期活动

功能关键词:

  • 远程过程调用(Remote Procedure Call)
  • 支持:
    • 设置 调用优先级priority
    • 使用 Promise 异步回调
    • 支持 取消正在进行的调用
    • 支持 销毁 Stub 释放资源

设备 & 版本:

  • 支持:Phone / 2in1 / Tablet / Wearable
  • 5.1.1(19) 起:新增支持 TV

一句话背:

URPC 远程程序调用能力支持 Phone / 2in1 / Tablet / Wearable,5.1.1(19) 起新增 TV 支持。


二、URPC 核心接口一览

URPC 的核心逻辑都围绕一个 UrpcStub 对象展开,常用 3 个方法:

call(
  funcName: string,
  request: object,
  returnValue: object,
  config?: CallingOption
): UrpcPromise
  • 发送一个 URPC 请求;
  • funcName:远程服务端定义的方法名,比如 "uploadFile"
  • request:请求入参对象;
  • returnValue:用于承接返回数据的对象(类实例);
  • config:调用配置(例如 priority);
  • 返回 UrpcPromise,里面带 .promisecallingId
cancel(callingId?: number | number[]): void
  • 取消 指定全部 正在进行中的 URPC 请求;
  • 不传 callingId → 取消当前 Stub 发起的所有请求。
destroy(): void
  • 用完之后销毁 UrpcStub 实例,释放关联资源。

三、完整调用流程拆解(从定义消息到销毁 Stub)

1. 导入模块

import { hilog } from "@kit.PerformanceAnalysisKit";
import { urpc } from "@kit.RemoteCommunicationKit";
import { BusinessError } from '@kit.BasicServicesKit';
  • urpc:URPC 主角;
  • hilog:日志输出;
  • BusinessError:异常统一用这个封装。

2. 定义 Request / Response 消息类

这里基于 urpc.FlowbufElement<T> 来描述一个字段:

// 请求消息
export class MediaTaskRequestMessage {
  RequestMessage: urpc.FlowbufElement<string>;

  constructor() {
    this.RequestMessage = { type: 'STRING', value: "", name: "" };
  }

  setRequestMessage(RequestMessage: string) {
    this.RequestMessage.value = RequestMessage;
  }

  getRequestMessage(): string {
    return this.RequestMessage.value;
  }
}

// 响应消息
export class MediaTaskResponseMessage {
  ResponseMessage: urpc.FlowbufElement<string>;

  constructor() {
    this.ResponseMessage = { type: 'STRING', value: "", name: "" };
  }

  setResponseMessage(ResponseMessage: string) {
    this.ResponseMessage.value = ResponseMessage;
  }

  getResponseMessage(): string {
    return this.ResponseMessage.value;
  }
}

要点:

  • 每个字段用 FlowbufElement<T> 包裹,指定:
    • type:类型(如 'STRING');
    • value:实际值;
    • name:字段名(可在协议里用)。
  • setXxx / getXxx 封装好,后面调用更顺手。

3. 创建 Request / Response 实例

let request = new MediaTaskRequestMessage();
let response = new MediaTaskResponseMessage();

// 比如这里设置请求内容
request.setRequestMessage("hello urpc");
  • 请求对象 = 要发给远端的参数;
  • 响应对象 = 用来接收返回数据。

4. 配置连接信息 & 创建 UrpcStub

let node: urpc.IpAndPort = {
  ip: '127.0.0.1',
  port: 8000
};

let connect: urpc.UrpcConnectConfiguration = {
  node: node,
  protocol: 'eat', // 协议字符串,需与服务端约定一致
};

let config: urpc.UrpcInitConfiguration = {
  timeout: 3000, // 初始化超时时间
  mode: 'client', // 当前为客户端模式
  connect: connect
};

// 允许调用的远程函数列表
const funcList: string[] = ["uploadFile"];

// 创建 urpcStub(注意,返回的是 Promise)
let urpcStub = urpc.urpcStubCreate(config, funcList);

要点:

  • node 指明 远端 IP + 端口
  • protocol 需要与你的 URPC 服务端实现保持一致(如 'eat');
  • funcList 里是你允许客户端调用的 远程方法名列表,比如 "uploadFile"
  • urpcStubCreate 返回的是一个 Promise,所以后续要 .then(...)

5. 使用 call 发送 URPC 请求

urpcStub.then(async (stub: urpc.UrpcStub) => {
  let upload_config: urpc.CallingOption = {
    priority: 0  // 优先级,数字越小可能表示越高优先
  };

  let urpcPromise = stub.call("uploadFile", request, response, upload_config);

  urpcPromise.promise
    .then((resp: object) => {
      hilog.info(0x000, "urpc", "resp: %{public}s", resp);
      // 一般会从 response 对象里再用 getResponseMessage() 拿业务数据
    })
    .catch((err: BusinessError) => {
      hilog.error(0x000, "urpc", "the error code is %d", err.code);
    });
}).catch((error: BusinessError) => {
  hilog.error(0x000, "urpc", "urpc call failed, error code is %d", error.code);
});

这里有两个“Promise 层”:

  1. urpcStub 自身是 Promise<UrpcStub>
  2. stub.call(...) 返回的是 UrpcPromise,里面还有一个 .promise 真正表示这次远程调用的完成/失败。

CallingOption 里可以配置:

  • priority:请求优先级(数值约定视服务端实现而定)。

6. 使用 cancel 取消请求(可选)

有时候发出请求后发现 不需要结果了(页面退出、用户取消、超时兜底逻辑等),就可以手动取消:

urpcStub.then(async (stub: urpc.UrpcStub) => {
  let upload_config: urpc.CallingOption = {
    priority: 0
  };
  let urpcPromise = stub.call("uploadFile", request, response, upload_config);

  // 取消指定 callingId 对应的这一次调用
  stub.cancel(urpcPromise.callingId);
}).catch((error: BusinessError) => {
  hilog.error(0x000, "urpc", "urpc cancel failed, error code is %d", error.code);
});
  • 传入单个 callingId → 取消该次请求;
  • 传入 number[] → 一次性取消多个;
  • 不传参数 → 取消当前 Stub 下所有正在进行的调用。

7. 使用 destroy 释放资源(必做收尾)

当这个 Stub 不再需要继续发请求时,要记得销毁它:

urpcStub.then(async (stub: urpc.UrpcStub) => {
  stub.destroy();
}).catch((error: BusinessError) => {
  hilog.error(0x000, "urpc", "urpc destroy failed, error code is %d", error.code);
});
  • 释放连接、内部缓冲等资源;
  • 一般可以在:
    • 页面销毁时;
    • 模块退出时;
    • 或应用退出前集中调用。

四、实战 Tips & 常踩坑点

  1. 服务端方法名必须匹配
    • funcList 里的 "uploadFile" 必须跟服务端暴露的远程函数名字一致;
    • 大小写也要一致,否则会直接调用失败。
  2. 入参/出参类要和服务端约定好结构
    • MediaTaskRequestMessageMediaTaskResponseMessage 的字段类型(FlowbufElement 类型、字段 name 等)要和服务端协议对齐;
    • 否则就算网络调用成功,序列化/反序列化阶段也可能出错或拿不到正确数据。
  3. 注意超时配置
    • UrpcInitConfiguration.timeout 只是初始化 Stub 时的超时;
    • 具体调用过程中的超时策略还可以在协议层或服务端进一步控制。
  4. cancel 不等于立即“秒停”
    • cancel 是“请求取消语义”,底层是否已经发完 / 服务器是否已经处理完,会根据实现有所差异;
    • 但是从客户端业务角度,可以把它当作“我不再关心这个结果了”。
  5. destroy 是真正生命周期的终点
    • 如果后续还要继续使用 URPC,建议重新走一遍 urpcStubCreate
    • 不要销毁后继续复用同一个 stub 引用。

五、考点速记版(考试/出题小抄)

  • 能力名称: 使用 URPC 进行远程程序调用
  • 设备支持: Phone / 2in1 / Tablet / Wearable,5.1.1(19) 起新增 TV
  • 核心接口:
    • call(funcName, request, returnValue, config?): UrpcPromise
    • cancel(callingId?: number | number[]): void
    • destroy(): void
  • 典型调用流程:
    1. 定义 Request / Response 类(基于 urpc.FlowbufElement<T>
    2. 配置 IpAndPortUrpcConnectConfigurationUrpcInitConfiguration
    3. urpc.urpcStubCreate(config, funcList) 拿到 urpcStub(Promise)
    4. stub.call("uploadFile", request, response, { priority: 0 })
    5. urpcPromise.promise.then / catch 处理结果
    6. 需要时 stub.cancel(callingId) 取消请求
    7. 结束时 stub.destroy() 释放资源
posted @ 2025-12-13 20:23  遇到困难睡大觉哈哈  阅读(3)  评论(0)    收藏  举报