HarmonyOS NEXT应用开发实战:十二、远场通信RCP简单好用的模块化封装

在进行HarmonyOS的应用开发中,我们常常需要进行网络通信。然而,原始的远场通信(RCP)使用方式较为繁琐,让人感到不够便捷。作为一位前期从事小程序开发的开发者,我深受小程序网络访问的简单性和便利性的吸引。因此,我决定在HarmonyOS中打造一个高效的网络组件,简化网络请求的使用方式。

原始使用方式的复杂性

首先,让我们看一下原始的RCP使用方式:

// 定义请求头
let headers: rcp.RequestHeaders = {'accept': 'application/json'
};// 定义要修改的内容
let modifiedContent: UserInfo = {'userName': 'xxxxxx'
};const securityConfig: rcp.SecurityConfiguration = {tlsOptions: {tlsVersion: 'TlsV1.3'}
};// 创建通信会话对象
const session = rcp.createSession({ requestConfiguration: { security: securityConfig } });// 定义请求对象
let req = new rcp.Request('http://example.com/fetch', 'PATCH', headers, modifiedContent);// 发起请求
session.fetch(req).then((response) => {Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

从上面的代码来看,整个流程涉及多步骤的配置,包括请求头、请求体的定义,以及会话的创建。这样的流程虽然功能齐全,但对于开发者来说,无疑增加了工作量。

模块化封装后的优势

为了提升开发效率,我着手对RCP进行模块化封装。以下是封装后的核心代码:

import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';type AnyObject = Record<string | number | symbol, any>;
type HttpPromise<T> = Promise<HttpResponse<T>>;export interface RequestTask {abort: () => void;offHeadersReceived: () => void;onHeadersReceived: () => void;
}type Tasks = RequestTask;export interface HttpRequestConfig<T = Tasks> {/** 请求基地址 */baseURL?: string;/** 请求服务器接口地址 */url?: string;/** 请求查询参数,自动拼接为查询字符串 */params?: AnyObject;/** 请求体参数 */data?: AnyObject;/** 文件对应的 key */name?: string;/** HTTP 请求中其他额外的 form data */formData?: AnyObject;/** 要上传或下载的文件资源的路径。 */filePath?: string;/** 请求头信息 */header?: AnyObject;/** 请求方式 */method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD" | (string & NonNullable<unknown>);/** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */dataType?: string;/** 设置响应的数据类型 */responseType?: "text" | "arraybuffer";/** 自定义参数 */custom?: AnyObject;/** 超时时间 */timeout?: number;/** DNS解析时优先使用ipv4*/firstIpv4?: boolean;/** 验证 ssl 证书  */sslVerify?: boolean;/** 跨域请求时是否携带凭证(cookies) */withCredentials?: boolean;/** 返回当前请求的task, options。请勿在此处修改options。 */getTask?: (task: T, options: HttpRequestConfig<T>) => void;/**  全局自定义验证器 */validateStatus?: (statusCode: number) => boolean | void;
}export interface HttpResponse<T = any> {config?: HttpRequestConfig;statusCode: number;data: T;errMsg: string;cookies: Array<string>;header: AnyObject;
}export interface HttpUploadResponse<T = any> {config: HttpRequestConfig;statusCode: number;data: T;errMsg: string;
}
export interface HttpDownloadResponse extends HttpResponse {tempFilePath: string;
}export abstract class HttpRequestAbstract {session: rcp.Session;config: HttpRequestConfig;constructor(public conf: HttpRequestConfig) {this.config = {...conf,validateStatus: conf.validateStatus || ((status) => status >= 200 && status < 300)};const sessionConfig: rcp.SessionConfiguration = {baseAddress: conf.baseURL,headers: conf.header,requestConfiguration: {security: {tlsOptions: {tlsVersion: 'TlsV1.3'}}}}this.session = rcp.createSession(sessionConfig);}request<T = any>(config: HttpRequestConfig<RequestTask>): HttpPromise<T> {return new Promise((resolve, reject) => {//const fullUrl = buildURL(buildFullPath(this.config.baseURL, config.url), config.params);const { method = "GET", url, header, data,params } = config;const fullUrl =`${this.config.baseURL}${url}`;const req = new rcp.Request(fullUrl, method, header, data);this.session.fetch(req).then((response) => {const responseData = response.toJSON();let resp = {config,data: responseData as T,statusCode: response.statusCode,errMsg: response.reasonPhrase,cookies:response.cookies?.map(cookie => `${cookie.name}=${cookie.value}`),header:response.headers};resolve(resp);}).catch((err: BusinessError) => {reject(err);});});}// GET 请求get<T = any>(url: string, config?: HttpRequestConfig<RequestTask>): HttpPromise<T> {return this.request({ ...config, url, method: 'GET' });}// POST 请求post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<RequestTask>): HttpPromise<T> {return this.request({ ...config, url, data, method: 'POST' });}// PUT 请求put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<RequestTask>): HttpPromise<T> {return this.request({ ...config, url, data, method: 'PUT' });}// DELETE 请求delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<RequestTask>): HttpPromise<T> {return this.request({ ...config, url, data, method: 'DELETE' });}// 其他 HTTP 请求方法可以类似实现,如 head、options、trace 等// 上传upload<T = any>(url: string, config?: HttpRequestConfig<RequestTask>): Promise<HttpUploadResponse>{return new Promise((resolve, reject) => {let fileDir = config.filePath; // 请根据自身业务定义此路径let uploadFromFile : rcp.UploadFromFile = {fileOrPath : fileDir}this.session.uploadFromFile(url, uploadFromFile).then((response) => {console.info(`Succeeded in getting the response ${response}`);let resp = {config,data: response.toJSON() as T,statusCode: response.statusCode,errMsg: response.reasonPhrase};resolve(resp)}).catch((err: BusinessError) => {console.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);reject(err)});})}// 下载download(url: string, config?: HttpRequestConfig<RequestTask>): Promise<HttpDownloadResponse>{return new Promise((resolve, reject) => {let downloadToFile: rcp.DownloadToFile = {kind: 'folder',path: config.filePath //请根据自身业务选择合适的路径} as rcp.DownloadToFilethis.session.downloadToFile(url, downloadToFile).then((response) => {console.info(`Succeeded in getting the response ${response}`);let resp = {config,data: response.toJSON(),statusCode: response.statusCode,errMsg: response.reasonPhrase,tempFilePath:config.filePath,header:response.headers,cookies:response.cookies?.map(cookie => `${cookie.name}=${cookie.value}`),};resolve(resp)}).catch((err: BusinessError) => {console.error(`DownloadToFile failed, the error message is ${JSON.stringify(err)}`);reject(err)});})}
}

通过这样的封装,我们将网络请求的逻辑进行了抽象,简化了外部调用的复杂性。开发者只需专注于请求的参数,而无需关心底层的实现。

封装后的简单使用

在封装完成后,发起网络请求的代码大幅度简化,例如:
先封装个http.ts工具

//utils/http.ts
import HttpRequest, { HttpRequestConfig, HttpResponse } from './core';const config:HttpRequestConfig = {baseURL: "http://175.178.126.10:8000/",validateStatus: (status) => {return status >= 200 && status < 300;}
}export const httpClient = new HttpRequest(config);export const setRequestConfig = () => {// 请求拦截httpClient.requestInterceptor.onFulfilled = (config?: HttpRequestConfig) =>{// 返回一个符合 HttpRequestConfig 类型的对象console.debug('请求拦截')return {}}httpClient.responseInterceptor.onFulfilled = (response?: HttpResponse) =>{// 返回一个符合 HttpResponse 类型的对象console.debug('响应拦截')return {}as HttpResponse}
}
export default httpClient;

网络访问的API接口实现:

// homeapi.ts
import { BaseResponse,SwiperData,HotMovieReq,MovieRespData } from '../bean/ApiTypes';
import { httpClient,setRequestConfig } from '../../utils/http';// 调用setRequestConfig函数进行请求配置
setRequestConfig();const http = httpClient;// 获取轮播图api接口
export const getSwiperList = (): Promise<BaseResponse<SwiperData>> => http.get('/swiperdata');// 获取热门影视接口
export const getHotMovie = (req: HotMovieReq): Promise<BaseResponse<MovieRespData>> => http.post('/hotmovie', req);// 获取知乎列表页api接口
export const getZhiHuNews = (date:string): Promise<BaseResponse<ZhiNewsRespData>> => http.get('/zhihunews/'+date);// 获取知乎详情页api接口
export const getZhiHuDetail = (id:string): Promise<BaseResponse<ZhiDetailRespData>> => http.get('/zhihudetail/'+id);

这一改动使得接口调用变得极其简单,清晰易读,减少了出错的可能性。一行代码即可写完一个接口,清晰直观。以上的四行代码,写完了四个接口,极大提高了开发效率。

当然,接口的请求包和响应包格式需要定义,这必不可少。以下是bean/ApiTypes.ts的接口类型定义。

type AnyObject = Record<string | number | symbol, any>
export interface BaseResponse<T>{statusCode: number;errMsg:string;header?: AnyObject;data:T;
}export interface ErrorResp {code: number;message: string;data: [];
}// 轮播图响应数据
export interface SwiperItem{id:string;imageUrl:string;title:string;url:string;description:string;
}
export interface SwiperData {code: number;message: string;data: Array<SwiperItem>;
}// 热门影视请求数据
export interface HotMovieReq {start: number;count: number;city:string;
}
// 热门影视响应数据
interface MovieItem{id:string;cover:string;title:string;gener:string;rate:number;
}
export interface MovieRespData {code: number;message: string;data: Array<MovieItem>;count: number;start: number;total: number;title: string;
}//==============================知乎日报
export type ZhiNewsItem ={id:string;image:string;title:string;url:string;hint:string;date: string;
}
export interface ZhiNewsRespData {code: number;message: string;stories: Array<ZhiNewsItem>;top_stories: Array<ZhiNewsItem>;date: string;
}type ZhiDetailItem={types:string;value:string;
}
export interface ZhiDetailRespData {code: number;message: string;content: Array<ZhiDetailItem>;title: string;author: string;bio: string;avatar: string;image: string;more: string;}

总结

通过对HarmonyOS中远场通信RCP的模块化封装,我们不仅优化了网络请求的流程,还提升了代码的可读性和可维护性。希望这篇文章能够帮助你在HarmonyOS应用开发中更高效地使用网络组件,享受更便捷的开发体验。

开源地址: https://gitee.com/yyz116/request

写在最后

最后,推荐下笔者的业余开源app影视项目“爱影家”,推荐分享给与我一样喜欢免费观影的朋友。
注:因涉及免费观影,该项目仅限于学习研究使用!请勿用于其他用途!

开源地址:爱影家app开源项目介绍及源码

https://gitee.com/yyz116/imovie

其他资源

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/remote-communication-interceptor-V13
https://developer.huawei.com/consumer/cn/doc/best-practices-V5/bpta-rcp-based-network-request-V5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/61301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app快速入门(十)--常用内置组件(下)

本文介绍uni-app的textarea多行文本框组件、web-view组件、image图片组件、switch开关组件、audio音频组件、video视频组件。 一、textarea多行文本框组件 textarea组件在HTML 中相信大家非常熟悉&#xff0c;组件的官方介绍见&#xff1a; textarea | uni-app官网uni-app,un…

Tomcat 如何管理 Session

Tomcat 如何管理 Session 我们知道&#xff0c;Tomcat 中每一个 Context 容器对应一个 Web 应用&#xff0c;而 Web 应用之间的 Session 应该是独立的&#xff0c;因此 Session 的管理肯定是 Context 级的&#xff0c;也就是一个 Context 一定关联多个 Session。 Tomcat 中主…

Flink vs Spark

Flink vs Spark Flink和Spark都是大数据处理领域的热门分布式计算框架&#xff0c;它们有各自的特点和优势&#xff0c;适用于不同的场景。本文对两者进行对比。 一、技术理念与架构 Flink&#xff1a; 基于事件驱动&#xff0c;面向流的处理框架。支持真正的流计算&#xff0c…

鸿蒙NEXT开发-用户通知服务的封装和文件下载通知

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

01 IP路由基础

一、路由器是怎么转发数据包 • 当数据包到达路由器之后&#xff0c;根据数据包的目的 IP 地址&#xff0c;查找 路由表&#xff0c;并根据路由表中相应的路由所指示出接口还有下一跳 指导数据包在网络中的转发。 • 如果路由器路由表没有路由怎么办&#xff1f; -------- 将数…

Android studio 呼叫盒app

一、权限文件 0.gradle切换国内源 #Fri Nov 08 15:46:05 CST 2024 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-8.4-bin.zip zipStoreBaseGRADLE_USER_HOME zipStorePathwrapper/dists1…

[Admin] Dashboard Filter for Mix Report Types

Background RevOps team has built a dashboard for sales team to track team members’ performance, but they’re blocked by how to provide a manager view based on sales’ hierarchy. Therefore, they seek for dev team’s help to clear their blocker. From foll…

网络技术-路由协议

路由协议是网络中确保数据包能够有效地从源节点传递到目的节点的重要机制。以下是常见的几种路由协议&#xff1a; 一、根据算法分类 1.距离向量路由协议&#xff08;Distance Vector Routing Protocol&#xff09; RIP&#xff08;Routing Information Protocol&#xff09;&…

2024年人工智能技术赋能网络安全应用测试:广东盈世在钓鱼邮件识别场景荣获第三名!

近期&#xff0c;2024年国家网络安全宣传周“网络安全技术高峰论坛主论坛暨粤港澳大湾区网络安全大会”在广州成功举办。会上&#xff0c;国家计算机网络应急技术处理协调中心公布了“2024年人工智能技术赋能网络安全应用测试结果”。结果显示&#xff0c;广东盈世计算机科技有…

Java进阶四-异常,File

异常 概念&#xff1a;代表程序出现的问题。 目的&#xff1a;程序出现了异常我们应该如何处理。 最高父类&#xff1a;Exception 异常分为两类 编译时异常&#xff1a;没有继承RuntimeException的异常,直接继承与Exception,编译阶段就会错误提示。运行时异常:RuntimeExc…

Gin 框架中的路由

1、路由概述 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。 RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路 由的时候建议参考 …

ERROR TypeError: AutoImport is not a function

TypeError: AutoImport is not a function 原因&#xff1a;unplugin-auto-import 插件版本问题 Vue3基于Webpack&#xff0c;在vue.config.js中配置 当unplugin-vue-components版本小于0.26.0时&#xff0c;使用以下写法 const { defineConfig } require("vue/cli-se…

Elasticsearch:更好的二进制量化(BBQ)对比乘积量化(PQ)

作者&#xff1a;来自 Elastic Benjamin Trent 为什么我们选择花时间研究更好的二进制量化而不是在 Lucene 和 Elasticsearch 中进行生产量化。 我们一直在逐步使 Elasticsearch 和 Lucene 的向量搜索变得更快、更实惠。我们的主要重点不仅是通过 SIMD 提高搜索速度&#xff0…

检查课程是否有效

文章目录 概要整体架构流程技术细节小结 概要 这是一个微服务内部接口&#xff0c;当用户学习课程时&#xff0c;可能需要播放课程视频。此时提供视频播放功能的媒资系统就需要校验用户是否有播放视频的资格。所以&#xff0c;开发媒资服务&#xff08;tj-media&#xff09;的…

红外遥控报警器设计(模电课设)

一、设计要求 利用NE555p芯片设计制作报警器。要求当有人遮挡红外光时发出报警信号&#xff0c;无人遮挡红外光时报警器不工作&#xff0c;即不发声。 二、元器件 555芯片&#xff1a;NE555P 集成运放&#xff1a;LM358 三级管&#xff1a;2N1711 蜂鸣器&#xff1a;HY-30…

英语fault和false的区别

"fault" 和 "false" 在英语中虽然都与错误或问题有关&#xff0c;但它们的含义和用法有很大的不同。下面详细解释这两个词的区别&#xff1a; 1. Fault 定义&#xff1a;错误、缺陷、责任、故障。特点&#xff1a; 错误或缺陷&#xff1a;指某物或某事存…

Spring MVC——针对实习面试

目录 Spring MVC什么是Spring MVC&#xff1f;简单介绍下你对Spring MVC的理解&#xff1f;Spring MVC的优点有哪些&#xff1f;Spring MVC的主要组件有哪些&#xff1f;Spring MVC的工作原理或流程是怎样的&#xff1f;Spring MVC常用注解有哪些&#xff1f; Spring MVC 什么是…

大连理工大学概率上机作业免费下载

大连理工大学概率论与数理统计上机资源 本资源库收录了大连理工大学概率论与数理统计课程的上机作业范例代码&#xff0c;旨在通过实际操作加深学生对概率统计概念的理解&#xff0c;帮助学生更好地理解和掌握知识点。 作业内容概览 第一题&#xff1a;随机变量关系探索 数…

如何通过对敏捷实践的调整,帮助远程团队提升研发效能?

首先明确一点&#xff0c;最敏捷的做法就是不要远程团队或分布式团队&#xff0c;远程一定比不上面对面同一地点的模式&#xff0c;毕竟环境不同&#xff0c;就不要期望远程团队和本地团队具备相同的效能&#xff0c;甚至期望更高。 那么&#xff0c;无论何种原因&#xff0c;…

机器学习(贝叶斯算法,决策树)

朴素贝叶斯分类 贝叶斯分类理论 假设现有两个数据集&#xff0c;分为两类 我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中红色圆点表示的类别)的概率&#xff0c;用p2(x,y)表示数据点(x,y)属于类别2(图中蓝色三角形表示的类别)的概率&#xff0c;那么对于一个新数据点(x,y)…