鸿蒙核心技术##运动开发## Remote Communication Kit(远场通信服务)

在之前的文章中,我们详细介绍了如何封装一个功能完备的 RCP 网络库,并探讨了其核心功能和高级特性。在本篇中,我们将展示如何在鸿蒙运动项目中使用这个网络库来实现具体的网络请求功能。

前言

在鸿蒙运动项目中,网络请求是实现功能的关键环节之一。无论是获取运动数据、同步用户信息,还是加载运动视频资源,都需要一个稳定、高效且易于使用的网络库。在本篇中,我们将通过实际代码样例,展示如何使用封装好的 RCP 网络库实现这些功能。

一、封装异常处理

在实际开发中,异常处理是网络请求中不可或缺的一部分。通过自定义异常类,我们可以更好地管理网络请求中可能出现的各种错误。

(一)自定义异常类

定义了一个 ApiException 类,用于封装 API 请求中的错误信息。

export class ApiException extends Error {
apiCode?: number;
apiMessage?: string; constructor(apiCode?: number, apiMessage?: string) {
super();
this.name = "ApiException";
this.apiCode = apiCode;
this.apiMessage = apiMessage;
}
}

核心点解析

  1. 自定义属性apiCodeapiMessage 用于存储 API 返回的错误代码和错误信息。
  2. 构造函数:通过构造函数初始化异常对象,并设置错误代码和错误信息。

二、封装网络请求

为了简化网络请求的实现,你封装了一个 ApiRequest 类,用于统一管理网络请求和错误处理。

(一)单例模式

ApiRequest 类使用了单例模式,确保全局只有一个实例。


import { DataResult, ErrorCodes, ErrorData, LibToast, NetworkException, SuccessData } from "lib_base";
import { RcpNetworkService } from "lib_base/src/main/ets/utils/rcpnet/RcpService";
import { ApiResult } from "../data/models/ApiResult";
import { ApiException } from "./ApiException"; export interface IApiRequestHandleBusinessError{
handleBusinessError(apiException: ApiException): boolean
} export class ApiRequest{ private static instance: ApiRequest static getInstance (): ApiRequest {
if (!ApiRequest.instance) {
ApiRequest.instance = new ApiRequest()
}
return ApiRequest.instance
} private _net?: RcpNetworkService | undefined;
private _i_api_equest_handle_business_error?: IApiRequestHandleBusinessError | undefined; public set i_api_equest_handle_business_error(value: IApiRequestHandleBusinessError | undefined) {
this._i_api_equest_handle_business_error = value;
} public set net(value: RcpNetworkService | undefined) {
this._net = value;
} public getService() : RcpNetworkService{
return this._net!;
} }

核心点解析

  1. 单例模式:通过 getInstance 方法确保 ApiRequest 的全局唯一性。
  2. 依赖注入:通过 set 方法注入 RcpNetworkServiceIApiRequestHandleBusinessError,增强类的灵活性。

(二)统一请求方法

ApiRequest 类提供了一个统一的请求方法 remoteApi,用于处理网络请求并封装返回结果。

export interface IApiRequestHandleBusinessError {
handleBusinessError(apiException: ApiException): boolean;
} export class ApiRequest { public static async remoteApi<T>(api:()=>Promise<ApiResult<T>>): Promise<DataResult<ApiResult<T>|null>>{
try{
const data = await api()
if(data.success && data.success){
let bean = new SuccessData<ApiResult<T>>(data)
return bean;
} else {
let bean = ErrorData.fromError(new ApiException(data.code,data.msg) as Error,data.code?.toString(),data.msg)
return bean;
}
}catch (e) {
if(e instanceof NetworkException){
let bean = ErrorData.fromError(e)
//请求被框架取消,客户端不进入异常处理,避免业务弹窗
if(e.code !== ErrorCodes.REQUEST_CANCEL){
LibToast.show(e.message)
}
return bean;
}
throw e as Error
} }
}

核心点解析

  1. 请求执行:通过传入的 api 函数执行具体的网络请求。
  2. 成功处理:如果请求成功,返回 SuccessData 对象。
  3. 失败处理:如果请求失败,根据错误类型返回 ErrorData 对象。
  4. 网络异常处理:捕获 NetworkException,并根据错误代码决定是否显示提示信息。

(三)业务逻辑处理

ApiRequest 类还提供了一个 service 方法,用于处理业务逻辑和错误。

export class ApiRequest {
// ... 其他代码 ... public static async service<T>(source:()=>Promise<DataResult<ApiResult<T>|null>>,
request : (data:DataResult<T|null>)=>void,
apiError : (apiError:ErrorData)=>void,
netError? : (netError:ErrorData)=>void,
){
let data = await source();
if(data instanceof SuccessData){
request(data.data)
}else {
let error = data as ErrorData
if(error.error&&error.error instanceof ApiException){
//业务异常
if(ApiRequest.instance._i_api_equest_handle_business_error){
ApiRequest.instance._i_api_equest_handle_business_error.handleBusinessError(error.error)
}
apiError(error)
}else {
//网络异常
if(netError){
netError(error)
}
}
}
}
}

核心点解析

  1. 请求执行:通过 source 函数执行网络请求。
  2. 成功处理:如果请求成功,调用 request 回调函数处理数据。
  3. 错误处理:根据错误类型调用 apiErrornetError 回调函数。
  4. 业务逻辑分离:通过 IApiRequestHandleBusinessError 接口处理业务逻辑错误。

三、封装公共请求头

在实际开发中,许多请求都需要携带公共请求头,例如设备信息、用户令牌等。你通过 CommonHeaderInterceptor 类实现了公共请求头的封装。

export async function getCommonHeaders(): Promise<Record<string, string>> {
return {
"device": LibDevice.getDeviceInfo(),
"machineCode": await USystem.getUniqueKey(),
"terminalType": "1", // 示例终端类型
"timestamp": new Date().toISOString(),
"versionNumber": LibDevice.getAppVersionCode().toString(),
"osId": Platform.appPlatform, // 示例 osId
"versionCode": LibDevice.getAppVersionName(),
"token": AccountManager.getToken()
};
} export class CommonHeaderInterceptor implements rcp.Interceptor {
async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
const commonHeaders = await getCommonHeaders();
const keys = Object.keys(commonHeaders);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (!context.request.headers) {
context.request.headers = {};
}
context.request.headers[key] = commonHeaders[key];
}
return next.handle(context);
}
}

核心点解析

  1. 公共头信息:通过 getCommonHeaders 函数获取公共头信息。
  2. 拦截器实现:在 intercept 方法中,将公共头信息添加到请求头中。
  3. 动态添加:通过循环动态添加公共头信息,确保每个请求都携带必要的信息。

四、封装响应转换器

为了更好地处理响应数据,封装一个 CommonTextResponseConverter 类,用于处理响应内容。

export class CommonTextResponseConverter extends TextResponseConverter {
convert(response: rcp.Response): string | object | null {
if (response.toJSON()) {
return response.toJSON();
} else {
return super.convert(response);
}
}
}

核心点解析

  1. JSON 转换:优先尝试将响应内容转换为 JSON 格式。
  2. 回退处理:如果无法转换为 JSON,调用父类的 convert 方法处理响应内容。

五、实际应用案例:获取运动数据

假设我们需要从服务器获取用户的运动数据,例如运动记录、运动计划等。我们将通过封装好的 RCP 网络库来实现这一功能。

(一)封装请求方法

export async function getAllLookSubjectList(params: Record<"parentId", string>, requestKeyFun?: (str: string) => void): Promise<ApiResult<Subject[]>> {
return ApiRequest.getInstance().getService().request<ApiResult<Subject[]>>>({
act: AllLOOK_SUBJECT_LIST,
method: RequestMethod.POST,
contentType: RcpContentType.JSON,
content: params
}, requestKeyFun);
}

核心点解析

  1. 请求配置:通过 RequestOptions 配置请求的基本信息,如 API 路径、请求方法和内容类型。
  2. 请求发送:使用 ApiRequest.getInstance().getService().request 方法发送请求。
  3. 回调函数:通过 requestKeyFun 提供请求键值,用于取消请求等操作。

(二)调用请求方法

在实际的页面或组件中,我们可以调用封装好的请求方法来获取运动数据。

aboutToAppear(): void {
super.aboutToAppear();
this.showLoading();
ApiRequest.service<Subject[]>(() => {
return LookResponsitory.getInstance().getAllLookSubjectList("1", (requestKey) => {
if (requestKey) {
this.addRequestId(requestKey);
}
});
}, (data) => {
this.hideLoading();
if (data.data && data.data.length > 0) {
this.tabs = data.data;
} else {
this.setDefalutTab();
}
}, (apiError) => {
this.hideLoading();
this.setDefalutTab();
// 业务异常
// LibToast.show(apiError.message ?? "获取异常");
}, () => {
this.hideLoading();
this.setDefalutTab();
// 网络等异常
// LibToast.show("网络异常");
});
}

核心点解析

  1. 请求执行:通过 ApiRequest.service 方法执行网络请求。
  2. 成功处理:如果请求成功,处理返回的数据。
  3. 错误处理:根据错误类型调用相应的回调函数处理错误。
  4. 加载状态:在请求开始时显示加载状态,在请求结束时隐藏加载状态。

六、总结

通过本篇的实战案例,我们展示了如何使用封装好的 RCP 网络库实现具体的网络请求功能。通过定义异常类、封装请求方法、处理公共请求头和响应转换器,以及实现具体的请求逻辑,我们能够高效地完成网络请求任务。封装好的网络库不仅提供了基本的请求和响应处理功能,还具备错误处理、日志记录、会话管理和网络状态检测等高级特性,能够满足大多数网络请求场景的需求。

鸿蒙运动项目开发:封装超级好用的 RCP 网络库(下)—— 实战应用的更多相关文章

  1. 基于c++11新标准开发一个支持多线程高并发的网络库

    背景 新的c++11标准出后,c++语法得到了非常多的扩展,比起以往不论什么时候都要灵活和高效,提高了程序编码的效率,为软件开发者节省了不少的时间. 之前我也写过基于ACE的网络server框架,但A ...

  2. Android项目开发全程(三)-- 项目的前期搭建、网络请求封装是怎样实现的

    在前两篇博文中已经做了铺垫,下面咱们就可以用前面介绍过的内容开始做一个小项目了(项目中会用到Afinal框架,不会用Afinal的童鞋可以先看一下上一篇博文),正所谓麻雀虽小,五脏俱全,这在里我会尽量 ...

  3. 使用Vue2+webpack+Es6快速开发一个移动端项目,封装属于自己的jsonpAPI和手势响应式组件

    导语 最近看到不少使用vue制作的音乐播放器,挺好玩的,本来工作中也经常使用Vue,一起交流学习,好的话点个star哦 本项目特点如下 : 1. 原生js封装自己的跨域请求函数,支持promise调用 ...

  4. Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充

    本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,me ...

  5. SPA项目开发--左侧树加首页导航

    SPA项目开发--左侧树以及首页导航 1. Mock.js 前后端分离之后,前端迫切需要一种机制,不再需要依赖后端接口开发,而今天的主角mockjs就可以做到这一点    Mock.js是一个模拟数据 ...

  6. [转]基于Starling移动项目开发准备工作

    最近自己趁业余时间做的flash小游戏已经开发得差不多了,准备再完善下ui及数值后,投放到国外flash游戏站.期间也萌生想法,想把游戏拓展到手机平台.这两天尝试了下,除去要接入ane接口的工作,小游 ...

  7. 企业项目开发--分布式缓存Redis

    第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis ...

  8. git项目开发版本控制实践

    linux和bsd: 第一, bsd, berkeley software distribution, 伯克利软件套装, 是最开始的unix是开放的, 然后berkeley对unix进行了修改, 形成 ...

  9. 第九章 企业项目开发--分布式缓存Redis(1)

    注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis 1.1.为什么用分布式缓存(或者说本地缓存存在的问题 ...

  10. 第八章 企业项目开发--分布式缓存memcached

    注意:本节代码基于<第七章 企业项目开发--本地缓存guava cache> 1.本地缓存的问题 本地缓存速度一开始高于分布式缓存,但是随着其缓存数量的增加,所占内存越来越大,系统运行内存 ...

随机推荐

  1. 【Python】基础操作

    指定解释器的运行环境 有时候我们会遇见报错 SyntaxError: Non-ASCII character '\xe4' in file E:/PycharmProjects/LEDdisplay2 ...

  2. 【Python】配置pip使用国内镜像源

    配置pip使用国内镜像源 零.问题 使用pip安装插件时总是很慢,咋解决呢? 壹.解决 在桌面上你的文件夹内新建pip目录,一般路径如下:C:\Users\{$你的用户名},比如我的用户名是Minuy ...

  3. Dify开发必备:分享8个官方文档不曾解释的关键技巧

    Dify 是一个帮助你快速搭建 AI 应用的工具,其定位类似Coze.但相比Coze--Dify是免费的.开源的,人人都可以用.哪怕你不懂编程,也能用它参与到 AI 应用的设计和使用中.总之,如果你是 ...

  4. FastAPI 核心安全功能与模板渲染的完整示:登录、CSRF、JWT、会话、认证和缓存功能

    以下是一个整合 FastAPI 核心安全功能与模板渲染的完整示例,基于多个技术文档的最佳实践,包含登录.CSRF.JWT.会话.认证和缓存功能: from datetime import dateti ...

  5. 一文搞懂Dockerfile

    Dockerfile官网 https://docs.docker.com/reference/dockerfile/ 什么是Dockerfile? Dockerfile 是一个文本文件,其内包含了一条 ...

  6. 探秘Transformer系列之(27)--- MQA & GQA

    探秘Transformer系列之(27)--- MQA & GQA 目录 探秘Transformer系列之(27)--- MQA & GQA 0x00 概述 0x01 MHA 1.1 ...

  7. 🧠 Model Context Protocol(MCP)详解:AI 编程新时代的“USB 接口

    目标读者:具备一定编程基础,但尚未涉足 AI 编程的开发者 本文目的:帮助你理解 MCP 的核心概念.技术优势.运作机制,并指导你如何使用 MCP 构建智能体项目. 什么是 MCP? MCP,全称 M ...

  8. python获取日期(今日、昨日),及格式化日期的方法(datetime)

    在写爬虫的过程中发现,很多网站的信息均以当天日期为链接组成部分,但是格式又不相同 有些是20210328,有些是2021-03-28,还有些2021/03/28 如果一次爬取来源不同网站的信息,需要对 ...

  9. 网鼎杯-nmap

    Web 0x01 nmap 查看源码提示: 研究了半天分隔符绕过,后来放弃了,根据题目提示(127.0.01,不用扫外网),尝试扫描/flag并尝试将其输出到1.txt中 -iL /flag -oN ...

  10. SpringBoot——使用http2

    使用http2 许多浏览器,包括Edge,仅在TLS(即HTTPS)情况下支持HTTP/2.即使服务器端配置为无TLS支持的HTTP/2,浏览器可能仍将回退到HTTP/1.1.所以我们需要有一个证书来 ...