From 92fd9cdd0b2b6dedc680e554af0025e437993aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Wed, 21 Jan 2026 14:56:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(http):=20=E4=BF=AE=E5=A4=8DHttpHelper?= =?UTF-8?q?=E5=B9=B6=E5=8F=91=E5=88=97=E8=A1=A8=E6=93=8D=E4=BD=9C=E5=92=8C?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=A4=84=E7=90=86=E5=99=A8=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为concurrentList的clear、add和remove操作添加try-catch异常处理 - 将httpHandlerList的清理逻辑移至finally块确保始终执行 - 修复setHandler方法中重复的存储操作并添加异常处理 - 移除重复的httpHandlerList清理代码避免重复操作 - 为cancel方法添加异常处理确保请求正确取消 --- CHANGELOG.md | 5 +- UTILS.md | 211 ------------------------------- src/main/ets/http/HttpHelper.ets | 154 ++++++++++++---------- 3 files changed, 89 insertions(+), 281 deletions(-) delete mode 100644 UTILS.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 325fd0a..8d3e444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # [v1.0.12] 2025.xx.xx -> - 添加一个图片组件`AutoImage`,高度固定,宽度只适应 -> - 'RefreshView'组件,添加一个`canLoadMore`字段,主动控制加载更多 +> - 添加一个图片组件`AutoImage`,高度固定,宽度自适应 +> - `RefreshView`组件,添加一个`canLoadMore`字段,主动控制加载更多 +> - 替换一些过期api > # [v1.0.11] 2025.09.02 diff --git a/UTILS.md b/UTILS.md deleted file mode 100644 index bc52926..0000000 --- a/UTILS.md +++ /dev/null @@ -1,211 +0,0 @@ -- # [@State装饰器:组件内状态](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-0000001774279614) -> @State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。 -> -> 在状态变量相关装饰器中,@State是最基础的,使变量拥有状态属性的装饰器,它也是大部分状态变量的数据源。 - -> @State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。 -> -> @State装饰的变量拥有以下特点: -> -> @State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。 -> -> @State装饰的变量生命周期与其所属自定义组件的生命周期相同。 -> - -- # [@Prop装饰器:父子单向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-prop-0000001774119942) -> @Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。 - ->@Prop装饰的变量和父组件建立单向的同步关系: -> ->@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。 -> ->当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。 -> -- # [@Link装饰器:父子双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link-0000001820999565) - -> 子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 -> -> @Link装饰的变量与其父组件中的数据源共享相同的值。 -> -> @Link装饰器不能在@Entry装饰的自定义组件中使用。 - -- # [@Provide装饰器和@Consume装饰器:与后代组件双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-provide-and-consume-0000001820879589) - -> @Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。 -> -> 其中@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。 - -> @Provide/@Consume装饰的状态变量有以下特性: -> -> @Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。 -> -> 后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。 -> -> @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。 - -```typescript -// 通过相同的变量名绑定 -@Provide a: number = 0; -@Consume a: number; - -// 通过相同的变量别名绑定 -@Provide('a') b: number = 0; -@Consume('a') c: number; -@Provide和@Consume通过相同的变量名或者相同的变量别名绑定时,@Provide装饰的变量和@Consume装饰的变量是一对多的关系。不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的@Provide装饰的变量,@Provide的属性名或别名需要唯一且确定,如果声明多个同名或者同别名的@Provide装饰的变量,会发生运行时报错。 - -``` - -- # [@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink-0000001774279618) -> 上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。 - -> @ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步: -> -> 被@Observed装饰的类,可以被观察到属性的变化; -> -> 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。 -> -> 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。 - -> 使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。 -> -> @ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。 - - - - - -- # [@Watch装饰器:状态变量更改通知](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-watch-0000001774119954) -> @Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。 - -> @Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。 - -```typescript -@Component -struct TotalView { - @Prop @Watch('onCountUpdated') count: number = 0; - @State total: number = 0; - // @Watch 回调 - onCountUpdated(propName: string): void { - this.total += this.count; - } - - build() { - Text(`Total: ${this.total}`) - } -} - -@Entry -@Component -struct CountModifier { - @State count: number = 0; - - build() { - Column() { - Button('add to basket') - .onClick(() => { - this.count++ - }) - TotalView({ count: this.count }) - } - } -} -``` - - - -- # [$$语法:内置组件双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-two-way-sync-0000001820999577) - -```typescript -// xxx.ets -@Entry -@Component -struct TextInputExample { - @State text: string = '' - controller: TextInputController = new TextInputController() - - build() { - Column({ space: 20 }) { - Text(this.text) - TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller }) - .placeholderColor(Color.Grey) - .placeholderFont({ size: 14, weight: 400 }) - .caretColor(Color.Blue) - .width(300) - }.width('100%').height('100%').justifyContent(FlexAlign.Center) - } -} -``` - - - -- # [@Track装饰器:class对象属性级更新](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-track-0000001820879601) - -> @Track应用于class对象的属性级更新。@Track装饰的属性变化时,只会触发该属性关联的UI更新。 - -> @Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;而未被标记的属性不能在UI中使用。 - -```typescript -class LogTrack { - @Track str1: string; - @Track str2: string; - - constructor(str1: string) { - this.str1 = str1; - this.str2 = 'World'; - } -} - -class LogNotTrack { - str1: string; - str2: string; - - constructor(str1: string) { - this.str1 = str1; - this.str2 = '世界'; - } -} - -@Entry -@Component -struct AddLog { - @State logTrack: LogTrack = new LogTrack('Hello'); - @State logNotTrack: LogNotTrack = new LogNotTrack('你好'); - - isRender(index: number) { - console.log(`Text ${index} is rendered`); - return 50; - } - - build() { - Row() { - Column() { - Text(this.logTrack.str1) // UINode1 - .fontSize(this.isRender(1)) - .fontWeight(FontWeight.Bold) - Text(this.logTrack.str2) // UINode2 - .fontSize(this.isRender(2)) - .fontWeight(FontWeight.Bold) - Button('change logTrack.str1') - .onClick(() => { - this.logTrack.str1 = 'Bye'; - }) - Text(this.logNotTrack.str1) // UINode3 - .fontSize(this.isRender(3)) - .fontWeight(FontWeight.Bold) - Text(this.logNotTrack.str2) // UINode4 - .fontSize(this.isRender(4)) - .fontWeight(FontWeight.Bold) - Button('change logNotTrack.str1') - .onClick(() => { - this.logNotTrack.str1 = '再见'; - }) - } - .width('100%') - } - .height('100%') - } -} -``` - - - diff --git a/src/main/ets/http/HttpHelper.ets b/src/main/ets/http/HttpHelper.ets index e58b775..4e44107 100644 --- a/src/main/ets/http/HttpHelper.ets +++ b/src/main/ets/http/HttpHelper.ets @@ -29,7 +29,10 @@ export class HttpHelper { this.httpHandlerList = new HashMap(); SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) - this.concurrentList.clear() + try { + this.concurrentList.clear() + } catch (error) { + } } /** @@ -40,27 +43,39 @@ export class HttpHelper { if (!apiNo) { return } - if (this.concurrentList.getIndexOf(apiNo) === -1) { - this.concurrentList.add(apiNo) + try { + if (this.concurrentList.getIndexOf(apiNo) === -1) { + this.concurrentList.add(apiNo) + } + } catch (error) { } } public addConcurrents(apiNo: string[]) { for (let apiNoElement of apiNo) { - if (this.concurrentList.getIndexOf(apiNoElement) === -1) { - this.concurrentList.add(apiNoElement) + try { + if (this.concurrentList.getIndexOf(apiNoElement) === -1) { + this.concurrentList.add(apiNoElement) + } + } catch (error) { } } } public removeConcurrent(apiNo: string) { - if (this.concurrentList.getIndexOf(apiNo) !== -1) { - this.concurrentList.remove(apiNo) + try { + if (this.concurrentList.getIndexOf(apiNo) !== -1) { + this.concurrentList.remove(apiNo) + } + } catch (error) { } } public removeConcurrents(apiNo: string[]) { for (let apiNoElement of apiNo) { - if (this.concurrentList.getIndexOf(apiNoElement) !== -1) { - this.concurrentList.remove(apiNoElement) + try { + if (this.concurrentList.getIndexOf(apiNoElement) !== -1) { + this.concurrentList.remove(apiNoElement) + } + } catch (error) { } } } @@ -100,12 +115,6 @@ export class HttpHelper { LogHelper.print(data) } - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (data.responseCode === 200) { resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) } else { @@ -116,12 +125,6 @@ export class HttpHelper { } }).catch((err: Error) => { LogHelper.error(JSON.stringify({ err: err, url: params.url, })) - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (err.message === 'Failed writing received data to disk/application') { const error: Error = new Error() error.name = 'cancel' @@ -130,26 +133,39 @@ export class HttpHelper { } else { reject(err) } + }).finally(() => { + try { + if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { + this.httpHandlerList.remove(apiNo ?? params.url) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) + } + } catch (error) { + } }); }); } setHandler(key: string, httpRequest: http.HttpRequest) { - if (this.concurrentList.getIndexOf(key) === -1 && - this.httpHandlerList.hasKey(key)) { - this.httpHandlerList.get(key).destroy() - this.httpHandlerList.remove(key) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + try { + if (this.concurrentList.getIndexOf(key) === -1 && + this.httpHandlerList.hasKey(key)) { + this.httpHandlerList.get(key).destroy() + this.httpHandlerList.remove(key) + } + } catch (error) { } - if (this.concurrentList.getIndexOf(key) === -1) { - this.httpHandlerList.set(key, httpRequest) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + try { + if (this.concurrentList.getIndexOf(key) === -1) { + this.httpHandlerList.set(key, httpRequest) + } + } catch (error) { } + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } /** @@ -187,12 +203,6 @@ export class HttpHelper { LogHelper.print(data) } - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (data.responseCode === 200) { resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) } else { @@ -203,12 +213,6 @@ export class HttpHelper { } }).catch((err: Error) => { LogHelper.error(JSON.stringify({ err: err, url: params.url, })) - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (err.message === 'Failed writing received data to disk/application') { const error: Error = new Error() error.name = 'cancel' @@ -217,6 +221,16 @@ export class HttpHelper { } else { reject(err) } + }).finally(() => { + try { + if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { + this.httpHandlerList.remove(apiNo ?? params.url) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) + } + } catch (error) { + } }); }); @@ -257,12 +271,6 @@ export class HttpHelper { LogHelper.print(data) } - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (data.responseCode === 200) { if (typeof data.result === 'string') { @@ -278,12 +286,6 @@ export class HttpHelper { } }).catch((err: Error) => { LogHelper.error(JSON.stringify({ err: err, url: params.url, })) - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) - } if (err.message === 'Failed writing received data to disk/application') { const error: Error = new Error() error.name = 'cancel' @@ -292,6 +294,16 @@ export class HttpHelper { } else { reject(err) } + }).finally(() => { + try { + if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { + this.httpHandlerList.remove(apiNo ?? params.url) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) + } + } catch (error) { + } }); }); @@ -354,11 +366,14 @@ export class HttpHelper { } }).finally(() => { httpRequest.off("dataSendProgress"); - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + try { + if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { + this.httpHandlerList.remove(apiNo ?? params.url) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) + } + } catch (error) { } }); }); @@ -373,12 +388,15 @@ export class HttpHelper { } cancel(apiNo: string) { - if (this.httpHandlerList.hasKey(apiNo)) { - this.httpHandlerList.get(apiNo).destroy() - this.httpHandlerList.remove(apiNo) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + try { + if (this.httpHandlerList.hasKey(apiNo)) { + this.httpHandlerList.get(apiNo).destroy() + this.httpHandlerList.remove(apiNo) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) + } + } catch (error) { } }