HarmonyOS鸿蒙Next中rcp网络请求session复用方案
HarmonyOS鸿蒙Next中rcp网络请求session复用方案
rcp 网络请求 session复用方案
背景介绍
官方的只提到了rcp的session可以复用,但如何复用没有相关的介绍,论坛里搜索了下,也没有相关的方案
于是自己写了一个池化的方案,希望各位大佬们指正,提出优化方案
需求
- 自己项目的项目需要获取网络图片,需要特定的请求头进行获取,官方的Image组件无法定制请求头相关参数,满足不了。
- 第三方组件 ImageKnife (看了代码,底层试用的是http模块) 可以定制请求头,由于项目采用V2版本状态控制进行开发,ImageKnife 对v2的支持还不完善。
问题和方案代码
主要用于网络图片下载,基于自己对池化技术的理解,用拦截器实现了缓存功能。 本来打算加入异步锁来避免公共数据的并发操作,但假如之后感觉速度下降了,等之后确实需要加入在加吧。
存在的问题
- 在懒加载列表中控制台会报一堆如下的error,大概跟回调函数有关吧,应该是由于组件在懒加载时被回收了,导致回调调用出错,希望哪位帮忙看下这是因为什么问题导致的
12-25 16:37:27.485 2491-2491 C03f01/NAPI com.xxx.xxxx E [(ark_crash_holder.cpp:68)(UpdateCallbackPtr)] Failed to update callback info: 140070987694160
- 任务队列使用LinkedList进行实现,但是不知道remove是根据什么进行判断相等的,每次调用removeTask都是方法返回的都是false。本人是java方向的,java可以重写equals方法和hash实现,在ArkTs里不知是如何判断的,希望各位指教
方案代码
import { rcp } from "@kit.RemoteCommunicationKit";
import { AppConfig } from "../AppConfig";
import { TokenInterceptor } from "../interceptor/TokenInterceptor";
import { LinkedList, url } from "@kit.ArkTS";
import { image } from "@kit.ImageKit";
import { PixelCache } from "../cache/PixelMapCache";
import { BusinessError, zlib } from "@kit.BasicServicesKit";
import { fileIo } from "@kit.CoreFileKit";
/**
* 定义下载任务接口
*/
export interface ImageTask {
url: string; // 图片URL
callback: (pic: image.PixelMap) => void; // 下载完成后的回调函数
}
/**
* 下载工具
* session最多可创建16个
*/
export class DownloadUtil {
// 最大并发连接数
private static readonly maxConnections: number = 10;
// 会话池大小
private static readonly poolSize: number = 2;
// 当前使用的连接数
private static inUseConnections: number = 0;
// 会话池
private static sessionPool: Array<rcp.Session> = [];
// 等待队列
private static waitList: LinkedList<ImageTask> = new LinkedList();
/**
* 初始化请求
*/
public static init(): void {
// 预先创建一定数量的session放入池中
for (let i = 0; i < DownloadUtil.poolSize; i++) {
DownloadUtil.sessionPool.push(DownloadUtil.createSession());
}
}
/**
* 创建一个新的Session
* @returns {rcp.Session} 新的Session实例
*/
private static createSession(): rcp.Session {
// 创建Session并配置基础地址、请求头、拦截器等
return rcp.createSession({
baseAddress: AppConfig.BASE_API_URL,
headers: {
'User-Agent': AppConfig.USER_AGENT,
"Accept-language": AppConfig.ACCEPT_LANGUAGE,
'App-OS': AppConfig.APP_OS,
'App-OS-Version': AppConfig.APP_OS_VERSION,
'App-Version': AppConfig.APP_VERSION,
'Host': AppConfig.HOST,
"Content-type": 'application/x-www-form-urlencoded;charset=UTF-8',
'Referer': AppConfig.BASE_API_URL,
},
interceptors: [new TokenInterceptor()],
requestConfiguration: {
processing: {
validateResponse: (response): boolean => response.statusCode === 200
},
dns: {
dnsOverHttps: {
url: AppConfig.DOH
}
},
security: {
remoteValidation: (context) => {
console.info('context', context);
return true;
}
}
}
});
}
/**
* 获取一个Session实例
* @returns {rcp.Session | null} Session实例或null
*/
private static getSession(): rcp.Session | null {
// 优先从会话池中获取Session
if (DownloadUtil.sessionPool.length > 0) {
return DownloadUtil.sessionPool.shift()!;
// 如果当前使用的连接数未达到最大限制,则创建新的Session
} else if (DownloadUtil.inUseConnections < DownloadUtil.maxConnections) {
DownloadUtil.inUseConnections++;
return DownloadUtil.createSession();
} else {
// 达到最大限制则返回null
return null;
}
}
/**
* 销毁所有Session实例
*/
public static destroy(): void {
// 遍历会话池,取消并关闭所有Session
DownloadUtil.sessionPool.forEach(session => {
session.cancel();
session.close();
});
PixelCache.clear();
DownloadUtil.sessionPool = [];
DownloadUtil.inUseConnections = 0;
}
/**
* 下载图片
* @param {ImageTask} task 图片下载任务
*/
public static async downloadImage(task: ImageTask): Promise<void> {
let cache = PixelCache.getCache(task.url);
if (cache) {
task.callback?(cache)
return
}
const session = DownloadUtil.getSession();
if (session) {
try {
// 发起GET请求下载图片
const response = await session.get(task.url);
// 创建PixelMap并调用回调函数
const imageSource = image.createImageSource(response.body)
const pixelMap = imageSource.createPixelMapSync()
PixelCache.setCache(task.url, pixelMap)
task.callback?(pixelMap);
} catch (e) {
console.error(`err: err code is ${e.code}, err message is ${JSON.stringify(e)}`);
} finally {
// 释放Session并处理下一个任务
DownloadUtil.releaseSession(session);
DownloadUtil.processNextTask();
}
} else {
// 如果没有获取到Session,将任务加入等待队列
DownloadUtil.waitList.add(task);
}
}
/**
* 下载ugoira文件
*
* 本函数旨在从给定的源URL下载ugoira(动画插图)文件,并将其保存到临时目录中,然后解压缩到缓存目录
* 它首先解析URL以获取文件名和目录路径,然后检查并创建必要的目录结构如果文件尚未存在,
* 它将下载文件并将其解压缩到指定的缓存目录中
*
* @param src ugoira文件的URL如果为空或无效,则函数将直接返回
* @param callback 下载完成后的回调函数,参数为true表示下载成功,false表示下载失败
*/
public static downloadUgoiraFile(src:string,callback?:(success:boolean)=>void){
// 检查src参数是否为空或无效
if (!src || src.length === 0) {
callback?(false)
return
}
try {
// 解析URL以获取源信息
const source = url.URL.parseURL(src)
// 提取文件名
const fileName = src.split('/').pop()
// 提取文件目录名
const fileDir = fileName?.split('.').shift()
// 定义缓存目录
const cacheDir = `${getContext().cacheDir}/ugoira/${fileDir}`
if (fileIo.accessSync(cacheDir, fileIo.AccessModeType.EXIST)){
callback?(true)
return
}
// 定义临时文件目录
const tempDir = `${getContext().tempDir}/ugoira`
// 检查并创建临时目录
if (!fileIo.accessSync(tempDir, fileIo.AccessModeType.EXIST)) {
fileIo.mkdirSync(tempDir,true)
}
// 定义文件路径
const filePath = `${tempDir}/${fileName}`
// 打开或创建文件
const file = fileIo.openSync(filePath,fileIo.OpenMode.CREATE|fileIo.OpenMode.READ_WRITE)
// 获取下载会话
const session = DownloadUtil.getSession()
// 下载文件到本地
session?.downloadToFile(source,{
kind:'file',
file:file.fd,
keepLocal:true
}).then(response=>{
console.log('response',response)
// 检查并创建缓存目录
if (!fileIo.accessSync(cacheDir, fileIo.AccessModeType.EXIST)) {
fileIo.mkdirSync(cacheDir,true)
}
// 解压缩文件到缓存目录
zlib.decompressFile(filePath,cacheDir).then(()=>{
callback?(true)
console.log('decompressFile success')
}).catch((e:BusinessError)=>{
console.log('err',e)
callback?(false)
}).finally(()=>{
// 关闭文件并删除临时文件
fileIo.close(file.fd).finally(()=>fileIo.unlink(filePath))
})
}).catch((e:BusinessError)=>{
callback?(false)
console.log('err',e)
})
} catch (e) {
callback?(false)
console.log('err',e)
}
}
/**
* 释放Session
* @param {rcp.Session} session 要释放的Session实例
*/
private static releaseSession(session: rcp.Session): void {
// 如果会话池未满,则将Session放回池中
if (DownloadUtil.sessionPool.length < DownloadUtil.poolSize) {
DownloadUtil.sessionPool.push(session);
} else {
// 否则关闭Session并减少当前使用的连接数
session.close();
DownloadUtil.inUseConnections--;
}
}
/**
* 处理下一个等待中的任务
*/
private static processNextTask(): void {
// 从等待队列中获取下一个任务并下载图片
const nextTask = DownloadUtil.waitList.removeFirst();
if (nextTask) {
DownloadUtil.downloadImage(nextTask);
}
}
public static removeTask(task:ImageTask){
const success = DownloadUtil.waitList.remove(task);
console.log('removeTask',success)
}
}
更多关于HarmonyOS鸿蒙Next中rcp网络请求session复用方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,rcp网络请求的session复用方案主要依赖于HttpSession
对象的管理和复用机制。HttpSession
是用于维护客户端与服务器之间会话状态的对象,通过复用HttpSession
可以减少网络请求的开销,提升性能。
在鸿蒙Next中,HttpSession
的复用可以通过以下方式实现:
-
Session管理:通过
HttpSessionManager
来管理HttpSession
的生命周期。HttpSessionManager
负责创建、获取和销毁HttpSession
对象。在同一个会话中,多个请求可以共享同一个HttpSession
对象。 -
Session ID传递:客户端在首次请求时,服务器会生成一个唯一的
Session ID
,并将其通过Set-Cookie
头返回给客户端。客户端在后续请求中通过Cookie
头将Session ID
传递给服务器,服务器根据Session ID
找到对应的HttpSession
对象,实现会话的复用。 -
Session超时处理:
HttpSession
对象有一个超时时间,超过该时间未使用的HttpSession
会被自动销毁。可以通过HttpSession.setMaxInactiveInterval()
方法设置超时时间,确保会话的合理复用和资源释放。 -
线程安全:
HttpSession
对象是线程安全的,多个请求可以同时访问同一个HttpSession
对象,但需要注意对共享数据的同步处理,避免并发问题。 -
Session存储:
HttpSession
对象可以存储在内存中,也可以持久化到外部存储中。在鸿蒙Next中,默认情况下HttpSession
存储在内存中,但可以通过配置将其持久化到文件或数据库中,以支持分布式环境下的会话共享。
通过以上机制,鸿蒙Next中的rcp网络请求可以实现HttpSession
的复用,减少网络请求的开销,提升系统的性能和可扩展性。
更多关于HarmonyOS鸿蒙Next中rcp网络请求session复用方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,实现RCP网络请求的Session复用,可以通过以下方案:
- Session管理:使用
HttpSession
对象管理会话,确保同一会话的请求可以复用Session。 - Cookie处理:在请求头中携带Session ID的Cookie,服务器通过Cookie识别并复用Session。
- 连接池:利用
HttpURLConnection
或OkHttp
的连接池机制,复用TCP连接,减少建立连接的开销。 - 单例模式:设计单例的HTTP客户端,确保所有请求通过同一客户端发出,共享Session和连接池。
通过这些方法,可以有效提升网络请求的效率和性能。