第四章:鸿蒙Tab实战 - 全局工具类架构设计:单例模式的生命周期管理
全局工具类如何设计?单例模式的生命周期管理、初始化分离、响应式更新,这些基础设施层面的架构设计,如何支撑整个项目?
一、问题场景:全局工具类的架构挑战
(本节约3分钟,快速了解问题即可)
1.1 这是几乎所有项目都会遇到的问题
在HarmonyOS应用开发中,几乎每个项目都需要设计全局工具类,比如:
- 尺寸适配工具:设计稿px值转换为实际设备的vp值(几乎所有项目都需要)
- 主题管理工具:全局主题切换、颜色管理(大多数项目需要)
- 网络请求工具:全局请求拦截、错误处理(几乎所有项目都需要)
- 日志工具:全局日志收集、上报(大多数项目需要)
- 本地存储工具:全局数据持久化(几乎所有项目都需要)
- 权限管理工具:全局权限申请、管理(大多数项目需要)
这些全局工具类面临共同的架构挑战,如果你设计过全局工具类,很可能遇到过这些问题:
挑战1:生命周期管理
- 单例模式如何正确处理生命周期?
- 组件销毁时,单例如何清理资源?
- 如何避免内存泄漏?
挑战2:初始化分离
- 哪些初始化可以在构造函数中完成?
- 哪些初始化需要等待外部依赖(如UIContext)?
- 如何设计初始化流程,避免循环依赖?
挑战3:响应式更新
- 全局工具类的状态变化,如何通知到UI组件?
- 如何与HarmonyOS的响应式系统集成?
- 窗口变化、屏幕旋转等系统事件,如何响应?
挑战4:使用体验
- 如何让全局工具类使用简单,无需每个页面都传入实例?
- 如何让工具类自动响应系统变化,无需手动刷新?
1.2 传统方案的痛点
方案1:简单单例模式
// ❌ 问题:生命周期管理缺失,容易内存泄漏
class SimpleSingleton {
private static instance: SimpleSingleton | null = null;
static getInstance(): SimpleSingleton {
if (!SimpleSingleton.instance) {
SimpleSingleton.instance = new SimpleSingleton();
}
return SimpleSingleton.instance;
}
// 问题:没有destroy方法,资源无法清理
// 问题:没有生命周期标记,销毁后仍可使用
}
方案2:每个页面传入实例
// ❌ 问题:使用繁琐,每个页面都要传入实例
@ComponentV2
export struct MyPage {
@Param tool: GlobalTool = new GlobalTool() // 每个页面都要创建实例
build() {
Row()
.width(this.tool.getSize(160)) // 使用繁琐
}
}
方案3:手动刷新UI
// ❌ 问题:系统变化时,需要手动刷新UI
onWindowResize() {
// 窗口变化时,需要手动重新计算并刷新UI
this.width = this.tool.getSize(160);
// 需要手动触发UI刷新
}
二、实战案例:完整的全局工具类设计
我们以尺寸适配工具为例,展示如何设计一个完整的全局工具类,解决生命周期管理、初始化分离、响应式更新等架构挑战。
2.1 整体设计思路
为什么选择尺寸适配工具作为案例?
- 问题普遍性:几乎所有HarmonyOS项目都需要处理设计稿适配问题
- 架构完整性:尺寸适配工具涵盖了全局工具类的所有架构挑战(生命周期、初始化、响应式更新)
- 设计思路可迁移:这套设计思路可以应用到其他全局工具类(主题管理、网络请求、日志工具等)
如果你正在设计其他全局工具类,这套设计思路同样适用。
全局工具类的5个核心功能模块:
- 全局数据共享:避免重复耗时操作
- 生命周期管理:避免内存泄漏
- 监听器管理:响应系统变化
- 缓存机制:提升性能
- 全局函数封装:提升使用体验
下面我们逐一展示每个功能模块的设计思路和实现。
2.2 功能模块1:全局数据共享(避免重复耗时操作)
问题:在HarmonyOS开发中,获取Display对象、屏幕尺寸等操作是耗时操作(display.getDefaultDisplaySync()),如果每个组件都单独调用,会造成性能浪费。
解决方案:通过工具类统一获取、监听变化,全局共享数据。
实现:
@ObservedV2
class DisplayManager {
private _uiCtx?: UIContext;
private _params?: object;
private _sharedDisplay: display.Display = display.getDefaultDisplaySync();
@Trace private _displayWidthVp: number = 0; // 屏幕宽度
@Trace private _displayHeightVp: number = 0; // 屏幕高度
@Trace private _windowWidthVp: number = 0; // 窗口宽度
@Trace private _windowHeightVp: number = 0; // 窗口高度
// 全局共享的Display对象(避免重复调用getDefaultDisplaySync)
public get sharedDisplay(): display.Display {
return this._sharedDisplay;
}
// 全局共享的屏幕尺寸(避免重复计算)
public get displayWidthVp(): number {
return this._displayWidthVp;
}
public get displayHeightVp(): number {
return this._displayHeightVp;
}
// 全局共享的窗口尺寸(避免重复计算)
public get windowWidthVp(): number {
return this._windowWidthVp;
}
public get windowHeightVp(): number {
return this._windowHeightVp;
}
// 全局共享的状态栏、导航栏高度(避免重复计算)
public get statusBarHeightVp(): number {
if (!this._uiCtx) return 0;
return this._uiCtx.px2vp(WindowUtil.getStatusBarHeight());
}
public get navBarHeightVp(): number {
if (!this._uiCtx) return 0;
return this._uiCtx.px2vp(WindowUtil.getNavigationBarHeight());
}
// 初始化时获取Display对象(只调用一次)
private initDisplaySize() {
this._sharedDisplay = display.getDefaultDisplaySync(); // 耗时操作,只调用一次
this._displayWidthVp = this._uiCtx?.px2vp(this._sharedDisplay.width) || 0;
this._displayHeightVp = this._uiCtx?.px2vp(this._sharedDisplay.height) || 0;
}
// 监听Display变化的回调逻辑见下文生命周期管理部分,避免重复定义
}
使用示例:
// ✅ 直接使用全局共享的数据,无需重复调用耗时接口
const display = displayManager.sharedDisplay; // 避免重复调用getDefaultDisplaySync()
const screenWidth = displayManager.displayWidthVp; // 避免重复计算
const windowWidth = displayManager.windowWidthVp; // 避免重复计算
const statusBarHeight = displayManager.statusBarHeightVp; // 避免重复计算
核心价值:
- 避免重复耗时操作:Display对象、屏幕尺寸等只获取一次,全局共享
- 自动更新:监听系统变化,自动更新共享数据
- 统一管理:所有组件使用同一份数据,保证一致性
2.3 功能模块2:生命周期管理(避免内存泄漏)
问题:组件销毁时,单例的监听器、缓存等资源无法清理,导致内存泄漏。
解决方案:使用生命周期标记(_isDestroyed)和destroy()方法统一清理资源。
实现:
@ObservedV2
class DisplayManager {
private static instance: DisplayManager | null = null;
private _isDestroyed: boolean = false; // 生命周期标记
private listeners: Set<(display?: display.Display) => void> = new Set();
private displayChangeCallback: (() => void) | null = null;
static getInstance(): DisplayManager {
if (DisplayManager.instance && !DisplayManager.instance._isDestroyed) {
return DisplayManager.instance;
}
DisplayManager.instance = new DisplayManager();
return DisplayManager.instance;
}
private constructor() {
this._isDestroyed = false;
this.initDisplaySize();
this.setupDisplayListener();
}
destroy(): void {
if (this._isDestroyed) {
return;
}
this._isDestroyed = true;
// 清理监听器
if (this.displayChangeCallback) {
display.off('change', this.displayChangeCallback);
}
this.listeners.clear();
// 清理缓存
this.clearLayoutCache('destroy');
this.displayChangeCallback = null;
}
// 在回调中检查生命周期标记(实现见监听器管理章节的同名方法)
}
使用示例:
// 在组件销毁时调用
aboutToDisappear() {
displayManager.destroy(); // 清理资源
}
核心价值:
- 避免内存泄漏:组件销毁时自动清理监听器、缓存等资源
- 防止回调执行:通过
_isDestroyed标记防止销毁后回调执行 - 支持重新初始化:销毁后可以重新初始化,适应组件重建场景
2.4 功能模块3:监听器管理(响应系统变化)
问题:多个组件需要监听系统变化(屏幕旋转、窗口变化等),如何统一管理监听器?
解决方案:使用Set管理多个监听器,支持动态添加/移除,统一通知。
实现:
@ObservedV2
class DisplayManager {
private listeners: Set<(display?: display.Display) => void> = new Set();
// 添加监听器
public addListener(callback: (display?: display.Display) => void): void {
this.listeners.add(callback);
}
// 移除监听器
public removeListener(callback: (display?: display.Display) => void): void {
this.listeners.delete(callback);
}
// 通知所有监听者
private notifyListeners(): void {
this.listeners.forEach(callback => {
try {
callback(this._sharedDisplay);
} catch (error) {
Logger.error('DisplayManager', 'Error in display listener:', error);
}
});
}
// 在Display变化时通知所有监听者
private displayChangeCallback = () => {
if (this._isDestroyed) return;
this.initDisplaySize();
this.clearLayoutCache('display change');
this.notifyListeners(); // 通知所有监听者
};
}
使用示例:
// 组件A监听屏幕变化
displayManager.addListener((display) => {
// 组件A的响应逻辑
this.updateLayout(display);
});
// 组件B也监听屏幕变化
displayManager.addListener((display) => {
// 组件B的响应逻辑
this.refreshUI(display);
});
// 组件销毁时移除监听器
aboutToDisappear() {
displayManager.removeListener(this.handleDisplayChange);
}
核心价值:
- 统一管理:多个组件可以监听同一事件,统一管理
- 动态管理:支持动态添加/移除监听器
- 自动通知:系统变化时自动通知所有监听者
2.5 功能模块4:缓存机制(提升性能)
问题:尺寸适配计算会被频繁调用,相同参数的计算重复执行,影响性能。
解决方案:使用Map缓存计算结果,避免重复计算。
实现:
@ObservedV2
class DisplayManager {
private _layoutCache: Map<string, string | number> = new Map();
// 缓存读取
layoutCacheGet<T>(key: string): T | undefined {
return this._layoutCache.get(key) as T | undefined;
}
// 缓存写入
layoutCacheSet(key: string, value: string | number): void {
this._layoutCache.set(key, value);
}
// 清理缓存
clearLayoutCache(reason?: string): void {
this._layoutCache.clear();
Logger.info('DisplayManager', `清理布局缓存,原因: ${reason || 'unknown'}`);
}
// 在窗口变化时自动清理缓存
private listenToWindow() {
const delegate: WindowDelegate = {
onContainerResized: (canSplit: boolean, size: window.Size, options: WindowOptions) => {
this._windowWidthVp = size.width;
this._windowHeightVp = size.height;
this.clearLayoutCache('window change'); // 窗口变化时清理缓存
emitter.emit('windowSizeChanged'); // 发送窗口变化事件
},
};
// 注册窗口变化监听(具体实现根据项目路由系统调整)
windowManager.registerDelegate(delegate, this._params);
}
}
使用示例:
// 在vpSizer方法中使用缓存
public vpSizer(designPx?: number | null, baseDesignFullWidth: number = 750): number {
// 生成缓存键
const cacheKey = `${this.windowWidthVp}_${designPx}_${baseDesignFullWidth}`;
// 先查缓存
const cached = this.layoutCacheGet<number>(cacheKey);
if (cached !== undefined) {
return cached; // 缓存命中,直接返回
}
// 缓存未命中,计算并写入缓存
const containerWidth = this.windowWidthVp;
const n = baseDesignFullWidth / 375; // 设计稿基准比例(750/375=2)
const stretch = containerWidth >= 600 ? 1.2 : 1; // 大屏设备拉伸系数
const result = designPx * stretch / n; // 根据不同的业务和设计统一对全局的尺寸进行计算
this.layoutCacheSet(cacheKey, result);
return result;
}
核心价值:
- 性能提升:相同参数的计算只执行一次,后续直接返回缓存值
- 自动清理:窗口变化时自动清理缓存,避免脏数据
- 内存可控:缓存大小可控,避免内存无限增长
缓存优化建议(详见第五节):
- 缓存键使用字符串拼接,避免JSON序列化开销
- 可设置最大缓存数量,避免内存无限增长
- 可设置缓存过期时间,自动清理过期缓存
2.6 功能模块5:全局函数封装(提升使用体验)
问题:每个页面都要传入实例,使用繁琐。如何让全局工具类使用简单?
解决方案:导出全局函数,底层使用单例模式,保证全局唯一性。
实现:
// 全局单例
export const displayManager = DisplayManager.getInstance();
// 便捷导出函数
export function sz(designPx?: number | null, baseDesignFullWidth: number = 750) {
if (!designPx) {
return 0;
}
return displayManager.vpSizer(designPx, baseDesignFullWidth);
}
export const szM = (designPx?: number | null, baseDesignFullWidth: number = 750) => {
if (!designPx) {
return LengthMetrics.vp(0);
}
const vp = sz(designPx);
return LengthMetrics.vp(vp); // 兼容镜像等
}
使用示例:
// ✅ 一行代码完成适配,无需传入实例
Row()
.width(sz(160)) // 设计稿160px,自动适配
.height(sz(72)) // 设计稿72px,自动适配
// 或使用szM(返回LengthMetrics,兼容镜像等)
Row()
.width(szM(160))
.height(szM(72))
核心价值:
- 使用简单:一行代码完成适配,无需传入实例
- 单例保证:底层使用单例模式,保证全局唯一性
- 高频调用友好:在UI代码中频繁使用,全局函数减少代码量
2.7 核心功能:尺寸适配算法(vpSizer方法)
这是尺寸适配工具的核心业务逻辑,整合了前面5个功能模块。
适配公式:
result = designPx * stretch / n
其中:
- n = baseDesignFullWidth / 375 // 设计稿基准比例(750/375=2)
- stretch = containerWidth >= 600 ? 1.2 : 1 // 大屏设备拉伸系数
适配策略:
- < 600vp:保持原始比例,避免过度拉伸
- ≥ 600vp:自动拉伸20%,提升视觉体验
完整实现:
@ObservedV2
class DisplayManager {
/**
* 核心方法:设计稿px到vp的智能适配
* 整合了:全局数据共享、缓存机制、监听器管理
*/
public vpSizer(designPx?: number | null, baseDesignFullWidth: number = 750): number {
if (!designPx || !this._sharedDisplay || !this._uiCtx) {
return 0;
}
// 使用全局共享的窗口宽度(功能模块1:全局数据共享)
const containerWidth = this.windowWidthVp;
// 生成缓存键
const cacheKey: string = `${containerWidth}_${designPx}_${baseDesignFullWidth}`;
// 先查缓存(功能模块4:缓存机制)
const cached = this.layoutCacheGet<number>(cacheKey);
if (cached !== undefined) {
return cached;
}
// 核心适配算法
const n = baseDesignFullWidth / 375; // 设计稿基准比例(750/375=2)
const stretch = containerWidth >= 600 ? 1.2 : 1; // 大屏设备拉伸系数
const result = designPx * stretch / n;
// 写入缓存(功能模块4:缓存机制)
this.layoutCacheSet(cacheKey, result);
return result;
}
}
适配示例:
- 设计稿160px,基准宽度750:
- 小屏设备(375vp):
160 * 1 / 2 = 80vp - 大屏设备(600vp):
160 * 1.2 / 2 = 96vp(拉伸20%)
- 小屏设备(375vp):
核心亮点:
- 智能适配策略:根据容器宽度(600vp分界点)动态调整stretch(1.2或1)
- 缓存机制:使用复合键缓存结果,避免重复计算
- 全局数据共享:使用全局共享的窗口宽度,避免重复获取
2.8 响应式更新机制
如果窗口尺寸变化(如分屏、悬浮窗),需要UI自动刷新:
步骤1:监听窗口变化
// 在DisplayManager中监听窗口变化
private listenToWindow() {
const delegate: WindowDelegate = {
onContainerResized: (canSplit: boolean, size: window.Size, options: WindowOptions) => {
// 更新窗口尺寸(功能模块1:全局数据共享)
this._windowWidthVp = size.width;
this._windowHeightVp = size.height;
// 清理缓存(功能模块4:缓存机制)
this.clearLayoutCache('window change');
// 发送事件,通知ViewModel
emitter.emit('windowSizeChanged'); // 窗口尺寸变化事件
},
};
// 注册窗口变化监听(具体实现根据项目路由系统调整)
windowManager.registerDelegate(delegate, this._params);
}
步骤2:ViewModel监听事件并重新计算
// 在ViewModel中存储尺寸值,使用@Trace装饰器
@ObservedV2
class TabViewModel {
@Trace stackWidth: number[] = new Array(this.tabCount).fill(sz(160));
@Trace listWidth: number[] = new Array(this.tabCount).fill(sz(160));
@Trace isScreenChanged: number = 0; // 标记屏幕变化,触发UI刷新
init() {
// 监听窗口变化事件
emitter.on('windowSizeChanged', () => {
// 窗口变化时,重新计算尺寸(sz()会使用新的窗口宽度)
this.stackWidth = new Array(this.tabCount).fill(sz(160));
this.listWidth = new Array(this.tabCount).fill(sz(160));
this.isScreenChanged++; // 触发UI刷新
});
}
}
步骤3:UI自动刷新
// ✅ 使用ViewModel中的响应式尺寸值
@ComponentV2
export struct TabView {
@Param viewModel: TabViewModel = new TabViewModel();
build() {
Row() {
ForEach(this.viewModel.itemList, (item, index) => {
Stack()
.width(this.viewModel.stackWidth[index]) // 响应式更新
.height(this.viewModel.listWidth[index]);
});
}
}
}
完整流程:
- 窗口尺寸变化 →
onContainerResized回调触发 - 更新窗口尺寸 →
_windowWidthVp更新(功能模块1:全局数据共享) - 清理缓存 → 之前的缓存值失效(功能模块4:缓存机制)
- 发送事件 →
emitter.emit('windowSizeChanged') - ViewModel响应 → 重新计算尺寸值(
sz()使用新的窗口宽度) - 更新响应式变量 →
@Trace标记的变量更新 - UI自动刷新 → HarmonyOS框架自动刷新UI
2.9 实际效果
使用完整的全局工具类设计后,我们获得了以下效果:
- 使用简单:一行代码完成适配,开发效率高
- 适配准确:大屏设备自动拉伸20%,视觉体验更好
- 性能优秀:缓存机制避免重复计算,性能提升显著
- 维护简单:适配逻辑集中管理,易于维护
- 资源管理:生命周期管理避免内存泄漏
性能说明:
- 缓存命中率:在Tab组件中,相同尺寸的重复调用较多,缓存命中率约80-90%
- 性能提升:相比无缓存方案,重复计算场景性能提升约5-10倍
- 内存开销:缓存占用内存较小(每个缓存项约100-200字节),可接受
边界情况处理:
- 极端尺寸:100vp以下按比例缩放,2000vp以上限制最大拉伸系数
- 异常值:designPx为0或负数时返回0,避免错误计算
- 窗口频繁变化:通过事件节流和批量更新,避免性能问题
三、理论分析:从工具封装到基础设施架构
全局工具类的设计,不仅仅是解决一个具体问题,更是构建项目的基础设施层。
传统视角(工具封装):
- 全局工具类只是一个工具,解决具体问题
- 关注点:功能实现、使用便捷性
架构视角(基础设施):
- 全局工具类是基础设施,支撑整个项目
- 关注点:生命周期管理、初始化分离、响应式更新、可维护性
架构价值:
- 基础设施层:为整个项目提供稳定的基础服务
- 统一管理:全局状态、资源、事件统一管理
- 架构支撑:支撑上层业务逻辑,降低耦合度
各功能模块的详细价值已在实战案例中说明(见"核心价值"部分),这里不再重复。
四、整体架构图
全局尺寸适配工具的整体架构如下:
graph TB
%% 第一层:系统层(最底部)
subgraph 系统层
K[窗口管理系统]
L[Display系统]
end
%% 第二层:DisplayManager核心
subgraph DisplayManager单例 [DisplayManager单例]
C[getInstance]
D[constructor<br/>无依赖初始化]
E[init<br/>有依赖初始化]
F[生命周期管理]
%% 功能模块分组
subgraph 数据管理
I[全局数据共享]
H[缓存管理]
end
subgraph 事件管理
G[监听器管理]
end
subgraph 核心计算
J[vpSizer<br/>核心适配方法]
end
end
%% 第三层:应用层(最顶部)
subgraph 应用层
B[ViewModel]
A[UI组件]
end
%% 系统到DisplayManager的连接
K -->|窗口变化| E
L -->|屏幕变化| G
L -->|屏幕数据| I
%% DisplayManager内部连接
C --> D
C --> E
D --> F
E --> F
F --> G
F --> H
F --> I
H --> J
I --> J
%% DisplayManager到应用层的连接
G -->|事件通知| B
J -->|适配计算| A
I -->|共享数据| A
B -->|业务逻辑| A
架构说明:
- 应用层:UI组件通过sz()函数调用,获取全局共享数据,ViewModel监听窗口变化事件
- DisplayManager单例:管理生命周期、监听器、缓存、全局数据共享,提供vpSizer核心适配方法
- 系统层:窗口管理系统、Display系统提供底层支持
五、补充说明:初始化分离与缓存优化
5.1 初始化分离:职责划分与生命周期同步
全局工具类需要区分无依赖初始化和有依赖初始化,核心在于生命周期依赖:
@ObservedV2
class DisplayManager {
private static instance: DisplayManager | null = null;
/**
* getInstance:单例模式的实例缓存
* - 通过静态实例缓存保证全局唯一性
* - 实例销毁后支持重新创建
*/
static getInstance(): DisplayManager {
if (DisplayManager.instance && !DisplayManager.instance._isDestroyed) {
return DisplayManager.instance;
}
DisplayManager.instance = new DisplayManager();
return DisplayManager.instance;
}
/**
* constructor:无依赖初始化阶段
* - 初始化不依赖外部上下文的成员(Display对象、监听器设置等)
* - 限制:无法获取UIContext等组件生命周期相关的依赖
*/
private constructor() {
this._isDestroyed = false;
this.initDisplaySize(); // 无依赖,可直接调用
this.setupDisplayListener(); // 无依赖,可直接调用
}
/**
* init:有依赖初始化阶段
* - 初始化需要组件生命周期上下文的成员(UIContext、路由参数等)
* - 调用时机:组件aboutToAppear时(此时所有依赖已就绪)
* - 支持多次调用:组件重建时可重新初始化
*/
public init(uiCtx: UIContext, params?: object) {
this._uiCtx = uiCtx;
this._params = params;
this.listenToWindow(); // 需要params才能执行
}
}
设计考虑:
- 生命周期依赖:UIContext、路由参数等依赖组件生命周期,必须在组件创建后才能获取
- 避免循环依赖:constructor中无法获取UIContext(组件还未创建),避免初始化阶段的循环依赖
- 支持重新初始化:组件重建时可以重新调用init(),适应HarmonyOS的组件重建机制
使用方式:
// 获取单例实例(实例缓存机制)
const displayManager = DisplayManager.getInstance();
// 在组件生命周期中初始化有依赖的部分
displayManager.init(uiCtx, params);
5.2 缓存优化建议
在第二节的缓存机制基础上,可以进一步优化:
- 缓存键生成:使用字符串拼接
${containerWidth}_${designPx}_${baseDesignFullWidth},避免JSON序列化开销 - 缓存大小限制:设置最大缓存数量(如1000条),避免内存无限增长
- 缓存过期策略:设置缓存过期时间(如5分钟),自动清理过期缓存
六、方案能力边界
根据实际需求,选择合适的模块组合:
功能模块选择:
- 全局数据共享:耗时操作(如
display.getDefaultDisplaySync())或需要自动更新时使用 - 生命周期管理:有监听器、缓存或资源需要清理时使用
- 监听器管理:需要监听系统变化且多个组件共享监听时使用
- 缓存机制:重复计算、计算耗时、缓存命中率高时使用
- 全局函数:工具类特性、使用频率高、无状态操作时使用
组合方案示例:
- 完整方案(尺寸适配工具):全局数据共享 + 生命周期管理 + 监听器管理 + 缓存机制 + 全局函数
- 简化方案(简单工具类):仅全局函数
- 中等方案(主题管理工具):全局数据共享 + 生命周期管理 + 监听器管理
七、总结提升:全局工具类的架构价值
7.1 实践验证理论
在实际项目中,完整的单例模式设计带来了显著的效果:
- 全局数据共享:Display对象、屏幕尺寸等耗时操作只获取一次,全局共享,避免重复调用
- 生命周期管理:正确处理组件生命周期,避免内存泄漏
- 初始化分离:无依赖和有依赖初始化分离,避免循环依赖
- 响应式更新:与HarmonyOS响应式系统集成,自动刷新UI
- 使用体验优秀:一行代码完成适配,无需手动管理
- 性能优秀:缓存机制避免重复计算,性能提升显著
7.2 从工具封装到基础设施架构
全局工具类的核心价值在于:
- 基础设施层:为整个项目提供稳定的基础服务
- 架构支撑:支撑上层业务逻辑,降低耦合度
- 统一管理:全局状态、资源、事件统一管理
- 设计模式实践:单例模式、生命周期管理、响应式更新等设计模式的完整实践
7.3 设计思路的可迁移性
这套设计思路不仅适用于尺寸适配工具,还可以应用到其他全局工具类:
主题管理工具:
- 全局数据共享:当前主题、主题配置
- 生命周期管理:组件销毁时清理主题监听器
- 监听器管理:监听系统主题变化
- 响应式更新:主题变化时通过事件通知UI组件自动刷新
网络请求工具:
- 全局数据共享:请求配置、全局拦截器
- 生命周期管理:组件销毁时清理请求队列、取消未完成的请求
- 监听器管理:监听网络状态变化
- 响应式更新:请求失败时通过事件通知UI组件显示错误提示
日志工具:
- 全局数据共享:日志配置、日志级别
- 生命周期管理:组件销毁时刷新日志缓冲区、关闭日志文件
- 监听器管理:监听日志级别变化
- 响应式更新:日志级别变化时通过事件通知UI组件调整日志显示
本地存储工具:
- 全局数据共享:存储配置、缓存数据
- 生命周期管理:组件销毁时刷新缓存、持久化数据
- 监听器管理:监听存储数据变化
- 响应式更新:存储数据变化时通过事件通知UI组件自动刷新
这些工具类都可以使用相同的设计模式:单例模式 + 生命周期管理 + 初始化分离 + 响应式更新。
7.4 架构价值
全局工具类的架构价值:
- 可维护性:生命周期管理、初始化分离,代码结构清晰
- 可扩展性:设计模式完整,易于扩展新功能
- 可测试性:初始化分离,便于单元测试
- 性能优化:缓存机制、响应式更新,性能优秀
这套设计思路的价值:
- 通用性:适用于几乎所有全局工具类
- 可复用性:设计模式可以复用到其他项目
- 可维护性:统一的架构设计,降低维护成本
- 可扩展性:易于扩展新功能,适应业务变化
八、下一章预告
在下一章中,我们将从这3个案例出发提炼和总结深一层的经验,分析HarmonyOS的优势,帮助我们对未来更有方向感。
说明:本文中的代码示例均经过脱敏处理,部分实现细节已简化,主要用于演示设计思路和架构理念。代码结构符合HarmonyOS规范,但实际使用时请根据具体业务场景调整,并参考HarmonyOS官方文档和最佳实践。