鸿蒙核心技术##运动开发## 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. 交换机批量配置生成器(SecureCRT vbs脚本)

    交换机批量配置生成器(SecureCRT vbs脚本) QQ交流群:(4817315) 一.工具介绍 本工具主要是针对简化网络工程师重复繁琐的工作而开发.工具只是将重复工作通过自己配置生成脚本代码来执 ...

  2. Win环境下的批处理命令和JScript脚本结合使用笔记

    最近工作有接触到.bat 批处理命令,在Win环境下编写的时候基于以前的编码习惯,觉得批处理语法可读性较差,于是学习了解了一下结合JScript的用法,特此记录. 什么是JScript JScript ...

  3. DeepSeek引发的AI发展路径思考

    DeepSeek引发的AI发展路径思考 参考文章来源于科技导报 ,作者李国杰院士 | 哈工大 DeepSeek 技术前沿与应用讲座 1. DeepSeek 的科技突破 7 天之内 DeepSeek 的 ...

  4. ubuntu 22.04安装NFS

    一.概述 1. 定义 NFS(Network File System)是一种分布式文件系统协议,最初由 Sun Microsystems 开发,并于1984年发布.它允许不同主机通过网络共享文件和目录 ...

  5. day12“函数”进阶学习让你更上一层楼

    函数进阶 多函数程序执⾏流程 拆包 递归 可变和不可变类型 多函数程序执⾏流程 共用全局变量 # 1. 定义全局变量 num = 0 def test1(): global num # 修改全局变量 ...

  6. soapUI接口关联教程

    1.新建项目结构 步骤如下: 打开soapUI Pro,新建工作空间(点击File-New Workspace) 选中Test右键点击New Project 选中AlloTest右键点击New Tes ...

  7. DIY记录之 USBasp

    序 笔者在上网浏览时发现了这个项目[0].刚好另一个DIY会需要对Attiny85进行烧写,并且感觉自己做一个USBasp来干这个事比较有趣,于是就买材料打板子准备DIY一个USBasp.这篇随笔是用 ...

  8. 看过源码吗?说下 Spring 由哪些重要的模块组成?

    是的,Spring源码可以深入分析,Spring框架是一个庞大的生态系统,包含多个模块,每个模块都为不同的功能提供支持.以下是Spring的主要模块及其职责: 1. Core Container(核心 ...

  9. Java 中常见的垃圾收集器有哪些?

    Java 中常见的垃圾收集器 Java 提供了多种垃圾收集器(Garbage Collector, GC),每种收集器针对不同的应用场景和需求进行了优化.以下是常见的垃圾收集器及其特点. 1. Ser ...

  10. K8s Pod 控制器介绍及应用示例

    Kubernetes 官方文档:Pod 控制器 Pod控制器介绍 Pod是kubernetes的最小管理单元,在kubernetes中,按照pod的创建方式可以将其分为两类: 自主式pod:kuber ...