性能优化实战指南:启动速度、内存、渲染全方位优化
系列文章:鸿蒙NEXT开发实战系列 -- 第5篇(共5篇) 适合人群:有ArkUI基础,想深入性能优化的开发者 开发环境:DevEco Studio 5.0.5+ | HarmonyOS NEXT (API 14) 阅读时长:约25分钟
上一篇:数据持久化与网络请求全攻略 | 下一篇:无(系列终篇)
一、引言:为什么性能优化是高级开发者的必修课?
在鸿蒙NEXT应用开发中,性能决定了用户体验的上限。一个启动缓慢、内存泄漏、列表卡顿的应用,即使功能再完善,也会被用户无情卸载。数据显示,应用启动时间每增加1秒,用户流失率就会上升7%;内存泄漏导致的崩溃,是用户差评的首要原因。
性能优化不是事后补救,而是贯穿开发全程的工程实践。本文将从启动速度、内存管理、列表渲染、网络请求四大维度,结合DevEco Studio Profiler等性能分析工具,手把手教你打造丝滑流畅的鸿蒙应用。无论你是想提升应用评分,还是想在面试中脱颖而出,这篇文章都值得收藏。
二、应用启动优化:让用户不再等待
2.1 启动阶段分析
鸿蒙NEXT应用启动分为三个阶段:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 冷启动阶段 │ -> │ 资源加载阶段 │ -> │ 首帧渲染阶段 │
│ (系统进程) │ │ (Ability) │ │ (UI渲染) │
└─────────────┘ └─────────────┘ └─────────────┘
2.2 延迟初始化策略
核心思想:将非首屏必需的初始化工作延后执行。
// entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
// 记录启动时间点
private startTime: number = 0;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.startTime = Date.now();
hilog.info(0x0000, 'EntryAbility', 'Application onCreate');
// 只初始化必要的服务
this.initEssentialServices();
}
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(0x0000, 'EntryAbility', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'EntryAbility', 'Failed to load content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(0x0000, 'EntryAbility', 'Succeeded in loading content.');
// 计算启动耗时
const launchTime = Date.now() - this.startTime;
hilog.info(0x0000, 'EntryAbility', `启动耗时: ${launchTime}ms`);
// 首帧渲染完成后,延迟初始化非必要服务
this.delayedInit();
});
}
// 只初始化必要的服务
private initEssentialServices(): void {
// 核心服务初始化
// 如:用户鉴权、配置加载等
}
// 延迟初始化非必要服务
private delayedInit(): void {
// 使用setTimeout延迟初始化
setTimeout(() => {
this.initAnalytics();
this.initPushService();
this.preloadResources();
}, 100);
}
// 数据统计服务初始化
private initAnalytics(): void {
hilog.info(0x0000, 'EntryAbility', 'Analytics service initialized');
}
// 推送服务初始化
private initPushService(): void {
hilog.info(0x0000, 'EntryAbility', 'Push service initialized');
}
// 预加载资源
private preloadResources(): void {
hilog.info(0x0000, 'EntryAbility', 'Resources preloaded');
}
}
2.3 使用TaskPool进行后台初始化
// utils/TaskPoolInit.ets
import { taskpool } from '@kit.CoreKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
// 定义后台任务
@Concurrent
function initDatabaseTask(): string {
// 模拟数据库初始化
const start = Date.now();
// 执行数据库初始化逻辑
// ...
const cost = Date.now() - start;
return `Database initialized in ${cost}ms`;
}
@Concurrent
function loadConfigTask(): string {
// 模拟配置文件加载
const start = Date.now();
// 执行配置加载逻辑
// ...
const cost = Date.now() - start;
return `Config loaded in ${cost}ms`;
}
// 初始化管理器
export class InitManager {
private static instance: InitManager;
private initTasks: taskpool.Task[] = [];
static getInstance(): InitManager {
if (!InitManager.instance) {
InitManager.instance = new InitManager();
}
return InitManager.instance;
}
// 添加初始化任务
addTask(task: taskpool.Task): void {
this.initTasks.push(task);
}
// 并行执行所有任务
async executeAll(): Promise<void> {
const promises = this.initTasks.map(task => taskpool.execute(task));
try {
const results = await Promise.all(promises);
results.forEach(result => {
hilog.info(0x0000, 'InitManager', `Task result: ${result}`);
});
} catch (error) {
hilog.error(0x0000, 'InitManager', `Task failed: ${error}`);
}
}
}
// 使用示例
export function initAppInBackground(): void {
const manager = InitManager.getInstance();
// 添加初始化任务
manager.addTask(new taskpool.Task(initDatabaseTask));
manager.addTask(new taskpool.Task(loadConfigTask));
// 并行执行
manager.executeAll();
}
2.4 启动优化踩坑记录
踩坑1:在UI线程执行耗时操作
// 错误示例:在UI线程同步加载大量数据
@Entry
@Component
struct SlowPage {
@State data: string[] = [];
aboutToAppear(): void {
// 错误:同步加载大量数据会阻塞UI线程
this.data = this.loadDataSync();
}
private loadDataSync(): string[] {
const result: string[] = [];
for (let i = 0; i < 10000; i++) {
result.push(`Item ${i}`);
}
return result;
}
build() {
List() {
ForEach(this.data, (item: string) => {
ListItem() {
Text(item)
}
})
}
}
}
// 正确示例:使用异步加载 + 分页
@Entry
@Component
struct FastPage {
@State data: string[] = [];
@State isLoading: boolean = false;
private pageSize: number = 50;
private currentPage: number = 0;
aboutToAppear(): void {
this.loadDataAsync();
}
private async loadDataAsync(): Promise<void> {
this.isLoading = true;
// 模拟异步加载
await new Promise(resolve => setTimeout(resolve, 0));
const startIndex = this.currentPage * this.pageSize;
const endIndex = startIndex + this.pageSize;
const newData: string[] = [];
for (let i = startIndex; i < endIndex && i < 10000; i++) {
newData.push(`Item ${i}`);
}
this.data = [...this.data, ...newData];
this.currentPage++;
this.isLoading = false;
}
build() {
List() {
ForEach(this.data, (item: string) => {
ListItem() {
Text(item)
}
})
if (this.isLoading) {
ListItem() {
LoadingProgress()
.width(50)
.height(50)
}
}
}
.onReachEnd(() => {
if (!this.isLoading) {
this.loadDataAsync();
}
})
}
}
三、内存管理与泄漏检测:告别OOM崩溃
3.1 常见内存泄漏场景
// 场景1:未取消的定时器
@Entry
@Component
struct TimerLeak {
private timer: number = -1;
aboutToAppear(): void {
// 创建定时器
this.timer = setInterval(() => {
console.info('Timer tick');
}, 1000);
}
// 错误:忘记在组件销毁时清理定时器
// aboutToDisappear(): void {
// clearInterval(this.timer);
// }
build() {
Text('Timer Leak Demo')
}
}
// 正确示例:正确清理资源
@Entry
@Component
struct TimerNoLeak {
private timer: number = -1;
aboutToAppear(): void {
this.timer = setInterval(() => {
console.info('Timer tick');
}, 1000);
}
aboutToDisappear(): void {
// 正确:组件销毁时清理定时器
if (this.timer !== -1) {
clearInterval(this.timer);
this.timer = -1;
}
}
build() {
Text('Timer No Leak Demo')
}
}
3.2 图片资源内存优化
// components/OptimizedImage.ets
import { image } from '@kit.ImageKit';
@Component
export struct OptimizedImage {
@Prop src: string | Resource = '';
@Prop width: number | string = 100;
@Prop height: number | string = 100;
@State imagePixelMap: image.PixelMap | undefined = undefined;
aboutToAppear(): void {
this.loadAndCompressImage();
}
private async loadAndCompressImage(): Promise<void> {
try {
// 获取图片源
const imageSource = image.createImageSource(this.src as string);
// 创建解码选项,指定目标尺寸
const decodingOptions: image.DecodingOptions = {
desiredSize: {
width: Number(this.width) * 2, // 2倍分辨率适配
height: Number(this.height) * 2
}
};
// 解码并压缩图片
this.imagePixelMap = await imageSource.createPixelMap(decodingOptions);
// 释放图片源
imageSource.release();
} catch (error) {
console.error('Image load failed:', error);
}
}
aboutToDisappear(): void {
// 释放PixelMap资源
if (this.imagePixelMap) {
this.imagePixelMap.release();
this.imagePixelMap = undefined;
}
}
build() {
if (this.imagePixelMap) {
Image(this.imagePixelMap)
.width(this.width)
.height(this.height)
.objectFit(ImageFit.Cover)
} else {
// 占位图
Column() {
LoadingProgress()
.width(30)
.height(30)
}
.width(this.width)
.height(this.height)
.justifyContent(FlexAlign.Center)
.backgroundColor('#F5F5F5')
}
}
}
3.3 弱引用与缓存管理
// utils/CacheManager.ets
export class CacheManager<T> {
private cache: Map<string, WeakRef<T>> = new Map();
private cacheSize: number;
private accessOrder: string[] = [];
constructor(maxSize: number = 100) {
this.cacheSize = maxSize;
}
// 设置缓存
set(key: string, value: T): void {
// 检查缓存大小,移除最少使用的
if (this.cache.size >= this.cacheSize) {
this.evictLeastUsed();
}
this.cache.set(key, new WeakRef(value));
this.updateAccessOrder(key);
}
// 获取缓存
get(key: string): T | undefined {
const ref = this.cache.get(key);
if (ref) {
const value = ref.deref();
if (value) {
this.updateAccessOrder(key);
return value;
} else {
// 弱引用已被回收
this.cache.delete(key);
}
}
return undefined;
}
// 更新访问顺序
private updateAccessOrder(key: string): void {
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
this.accessOrder.push(key);
}
// 移除最少使用的缓存
private evictLeastUsed(): void {
if (this.accessOrder.length > 0) {
const leastUsedKey = this.accessOrder.shift();
if (leastUsedKey) {
this.cache.delete(leastUsedKey);
}
}
}
// 清空缓存
clear(): void {
this.cache.clear();
this.accessOrder = [];
}
// 获取缓存大小
get size(): number {
return this.cache.size;
}
}
// 使用示例
class ImageData {
constructor(public id: string, public data: ArrayBuffer) {}
}
const imageCache = new CacheManager<ImageData>(50);
export function getCachedImage(id: string): ImageData | undefined {
return imageCache.get(id);
}
export function setCachedImage(id: string, data: ImageData): void {
imageCache.set(id, data);
}
3.4 内存泄漏检测工具类
// utils/MemoryMonitor.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
export class MemoryMonitor {
private static listeners: Set<string> = new Set();
private static timers: Set<number> = new Set();
// 注册监听器追踪
static trackEventListener(id: string): void {
this.listeners.add(id);
hilog.info(0x0000, 'MemoryMonitor', `Listener registered: ${id}, total: ${this.listeners.size}`);
}
// 注销监听器
static untrackEventListener(id: string): void {
this.listeners.delete(id);
hilog.info(0x0000, 'MemoryMonitor', `Listener unregistered: ${id}, total: ${this.listeners.size}`);
}
// 注册定时器追踪
static trackTimer(timerId: number): void {
this.timers.add(timerId);
hilog.info(0x0000, 'MemoryMonitor', `Timer registered: ${timerId}, total: ${this.timers.size}`);
}
// 注销定时器
static untrackTimer(timerId: number): void {
this.timers.delete(timerId);
hilog.info(0x0000, 'MemoryMonitor', `Timer unregistered: ${timerId}, total: ${this.timers.size}`);
}
// 检查潜在泄漏
static checkForLeaks(): void {
hilog.warn(0x0000, 'MemoryMonitor', `=== Leak Check Report ===`);
hilog.warn(0x0000, 'MemoryMonitor', `Active listeners: ${this.listeners.size}`);
hilog.warn(0x0000, 'MemoryMonitor', `Active timers: ${this.timers.size}`);
if (this.listeners.size > 10) {
hilog.error(0x0000, 'MemoryMonitor', `WARNING: High number of listeners detected!`);
}
if (this.timers.size > 5) {
hilog.error(0x0000, 'MemoryMonitor', `WARNING: High number of timers detected!`);
}
}
// 获取内存使用情况
static getMemoryInfo(): void {
// 注:实际开发中可使用performance.memory API
hilog.info(0x0000, 'MemoryMonitor', `Active listeners: ${this.listeners.size}`);
hilog.info(0x0000, 'MemoryMonitor', `Active timers: ${this.timers.size}`);
}
}
// 使用示例:带内存监控的组件
@Component
export struct MonitoredComponent {
private listenerId: string = `component_${Date.now()}`;
private timerId: number = -1;
aboutToAppear(): void {
// 追踪资源
MemoryMonitor.trackEventListener(this.listenerId);
this.timerId = setInterval(() => {
// 业务逻辑
}, 5000);
MemoryMonitor.trackTimer(this.timerId);
}
aboutToDisappear(): void {
// 清理资源
MemoryMonitor.untrackEventListener(this.listenerId);
if (this.timerId !== -1) {
clearInterval(this.timerId);
MemoryMonitor.untrackTimer(this.timerId);
}
}
build() {
Text('Monitored Component')
}
}
3.5 内存优化踩坑记录
踩坑2:闭包导致的内存泄漏
// 错误示例:闭包持有组件引用
@Entry
@Component
struct ClosureLeak {
@State message: string = 'Hello';
aboutToAppear(): void {
// 错误:闭包持有this引用,组件销毁后定时器仍在执行
setInterval(() => {
console.info(this.message); // this指向已销毁的组件
}, 1000);
}
build() {
Text(this.message)
}
}
// 正确示例:正确管理闭包生命周期
@Entry
@Component
struct ClosureNoLeak {
@State message: string = 'Hello';
private timer: number = -1;
private isDestroyed: boolean = false;
aboutToAppear(): void {
this.timer = setInterval(() => {
if (!this.isDestroyed) {
console.info(this.message);
}
}, 1000);
}
aboutToDisappear(): void {
this.isDestroyed = true;
if (this.timer !== -1) {
clearInterval(this.timer);
}
}
build() {
Text(this.message)
}
}
四、列表渲染优化:丝滑滚动的秘密
4.1 LazyForEach懒加载
// model/DataSource.ets
import { interfaces } from '@kit.ArkUI';
// 自定义数据源
export class ProductDataSource implements interfaces.IProductDataSource {
private products: Product[] = [];
private listener: interfaces.DataChangeListener | null = null;
constructor() {
// 模拟1000条数据
for (let i = 0; i < 1000; i++) {
this.products.push({
id: i.toString(),
name: `Product ${i}`,
price: Math.floor(Math.random() * 1000),
image: `image_${i % 10}.png`
});
}
}
// 获取总数
totalCount(): number {
return this.products.length;
}
// 获取指定索引的数据
getData(index: number): Product {
return this.products[index];
}
// 注册数据变化监听器
registerDataChangeListener(listener: interfaces.DataChangeListener): void {
this.listener = listener;
}
// 注销数据变化监听器
unregisterDataChangeListener(): void {
this.listener = null;
}
}
export interface Product {
id: string;
name: string;
price: number;
image: string;
}
// pages/ProductList.ets
import { ProductDataSource, Product } from '../model/DataSource';
@Entry
@Component
struct ProductListPage {
private dataSource: ProductDataSource = new ProductDataSource();
build() {
Column() {
// 使用LazyForEach懒加载
List() {
LazyForEach(this.dataSource, (product: Product) => {
ListItem() {
ProductItem({ product: product })
}
.margin({ bottom: 10 })
}, (product: Product) => product.id)
}
.cachedCount(5) // 预加载5个item
.edgeEffect(EdgeEffect.Spring)
}
.width('100%')
.height('100%')
}
}
@Component
struct ProductItem {
@Prop product: Product;
build() {
Row() {
// 使用占位图优化图片加载
Image($r(`app.media.${this.product.image}`))
.width(80)
.height(80)
.objectFit(ImageFit.Cover)
.borderRadius(8)
Column() {
Text(this.product.name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(`¥${this.product.price}`)
.fontSize(14)
.fontColor('#FF4D4F')
.margin({ top: 5 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
.layoutWeight(1)
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
}
4.2 ForEach优化Key生成
// 优化前:默认索引作为Key
@Entry
@Component
struct BadKeyDemo {
@State items: string[] = ['A', 'B', 'C', 'D', 'E'];
build() {
Column() {
Button('打乱顺序')
.onClick(() => {
this.items = this.items.sort(() => Math.random() - 0.5);
})
List() {
// 错误:使用索引作为Key,打乱顺序后会全量重新渲染
ForEach(this.items, (item: string, index: number) => {
ListItem() {
Text(item)
.fontSize(20)
.padding(10)
}
}, (item: string, index: number) => index.toString())
}
}
}
}
// 优化后:使用唯一标识作为Key
@Entry
@Component
struct GoodKeyDemo {
@State items: ItemData[] = [
{ id: '1', name: 'A' },
{ id: '2', name: 'B' },
{ id: '3', name: 'C' },
{ id: '4', name: 'D' },
{ id: '5', name: 'E' }
];
build() {
Column() {
Button('打乱顺序')
.onClick(() => {
this.items = this.items.sort(() => Math.random() - 0.5);
})
List() {
// 正确:使用唯一id作为Key,只更新变化的item
ForEach(this.items, (item: ItemData) => {
ListItem() {
Text(item.name)
.fontSize(20)
.padding(10)
}
}, (item: ItemData) => item.id)
}
}
}
}
interface ItemData {
id: string;
name: string;
}
4.3 组件复用优化
// components/ReusableItem.ets
@Reusable
@Component
export struct ReusableItem {
@State title: string = '';
@State content: string = '';
@State isExpanded: boolean = false;
// 组件复用时调用,重置状态
aboutToReuse(params: Record<string, Object>): void {
this.title = params.title as string;
this.content = params.content as string;
this.isExpanded = false;
}
build() {
Column() {
Row() {
Text(this.title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
Blank()
Image(this.isExpanded ? $r('app.media.arrow_up') : $r('app.media.arrow_down'))
.width(20)
.height(20)
}
.width('100%')
.onClick(() => {
this.isExpanded = !this.isExpanded;
})
if (this.isExpanded) {
Text(this.content)
.fontSize(14)
.margin({ top: 10 })
.transition(TransitionEffect.OPACITY.animation({ duration: 200 }))
}
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 10 })
}
}
4.4 列表优化踩坑记录
踩坑3:未使用cachedCount导致滚动卡顿
// 错误示例:未设置cachedCount
List() {
LazyForEach(this.dataSource, (item: DataItem) => {
ListItem() {
ComplexItem({ data: item }) // 复杂的列表项
}
}, (item: DataItem) => item.id)
}
// 问题:滚动时才创建item,导致白屏和卡顿
// 正确示例:设置合理的cachedCount
List() {
LazyForEach(this.dataSource, (item: DataItem) => {
ListItem() {
ComplexItem({ data: item })
}
}, (item: DataItem) => item.id)
}
.cachedCount(10) // 预加载10个item,保证滚动流畅
踩坑4:在build中创建复杂对象
// 错误示例:在build中频繁创建对象
@Entry
@Component
struct BuildObjectLeak {
@State items: string[] = Array.from({ length: 100 }, (_, i) => `Item ${i}`);
build() {
List() {
ForEach(this.items, (item: string) => {
ListItem() {
// 错误:每次build都会创建新的Date对象
Text(`${item} - ${new Date().toLocaleTimeString()}`)
}
}, (item: string) => item)
}
}
}
// 正确示例:使用@State缓存计算结果
@Entry
@Component
struct BuildObjectOptimized {
@State items: string[] = Array.from({ length: 100 }, (_, i) => `Item ${i}`);
@State currentTime: string = new Date().toLocaleTimeString();
aboutToAppear(): void {
// 定时更新时间,而不是每次build都创建
setInterval(() => {
this.currentTime = new Date().toLocaleTimeString();
}, 1000);
}
build() {
List() {
ForEach(this.items, (item: string) => {
ListItem() {
Text(`${item} - ${this.currentTime}`)
}
}, (item: string) => item)
}
}
}
五、网络请求优化:快速响应的秘诀
5.1 请求合并与防抖
// utils/RequestManager.ets
import { http } from '@kit.NetworkKit';
export class RequestManager {
private static instance: RequestManager;
private pendingRequests: Map<string, Promise<http.HttpResponse>> = new Map();
private debounceTimers: Map<string, number> = new Map();
static getInstance(): RequestManager {
if (!RequestManager.instance) {
RequestManager.instance = new RequestManager();
}
return RequestManager.instance;
}
// 合并相同请求
async fetch(url: string, options?: http.HttpRequestOptions): Promise<http.HttpResponse> {
// 如果有相同的请求正在进行,直接返回其Promise
if (this.pendingRequests.has(url)) {
console.info(`Request merged: ${url}`);
return this.pendingRequests.get(url)!;
}
// 创建新请求
const httpRequest = http.createHttp();
const promise = httpRequest.request(url, options)
.then(response => {
// 请求完成后移除
this.pendingRequests.delete(url);
httpRequest.destroy();
return response;
})
.catch(error => {
this.pendingRequests.delete(url);
httpRequest.destroy();
throw error;
});
this.pendingRequests.set(url, promise);
return promise;
}
// 防抖请求
fetchWithDebounce(
key: string,
url: string,
options?: http.HttpRequestOptions,
delay: number = 300
): Promise<http.HttpResponse> {
return new Promise((resolve, reject) => {
// 清除之前的定时器
if (this.debounceTimers.has(key)) {
clearTimeout(this.debounceTimers.get(key));
}
// 设置新的定时器
const timer = setTimeout(async () => {
try {
const response = await this.fetch(url, options);
resolve(response);
} catch (error) {
reject(error);
} finally {
this.debounceTimers.delete(key);
}
}, delay) as unknown as number;
this.debounceTimers.set(key, timer);
});
}
}
5.2 数据缓存策略
// utils/CacheStrategy.ets
import { http } from '@kit.NetworkKit';
import { preferences } from '@kit.ArkData';
interface CacheEntry {
data: string;
timestamp: number;
etag?: string;
}
export class CacheStrategy {
private memoryCache: Map<string, CacheEntry> = new Map();
private cacheDuration: number = 5 * 60 * 1000; // 5分钟缓存
// 带缓存的请求
async fetchWithCache(url: string): Promise<string> {
// 1. 检查内存缓存
const memoryCache = this.memoryCache.get(url);
if (memoryCache && this.isCacheValid(memoryCache)) {
console.info(`Cache hit (memory): ${url}`);
return memoryCache.data;
}
// 2. 检查本地存储缓存
const localCache = await this.getLocalCache(url);
if (localCache && this.isCacheValid(localCache)) {
console.info(`Cache hit (local): ${url}`);
this.memoryCache.set(url, localCache); // 更新内存缓存
return localCache.data;
}
// 3. 发起网络请求
console.info(`Cache miss: ${url}`);
const httpRequest = http.createHttp();
const options: http.HttpRequestOptions = {
header: {
// 如果有ETag,添加条件请求头
...(localCache?.etag ? { 'If-None-Match': localCache.etag } : {})
}
};
const response = await httpRequest.request(url, options);
httpRequest.destroy();
if (response.responseCode === 304) {
// 304 Not Modified,使用缓存数据
console.info(`304 Not Modified: ${url}`);
return localCache!.data;
}
if (response.responseCode === 200) {
const data = response.result as string;
const etag = response.header['etag'] as string;
// 更新缓存
const cacheEntry: CacheEntry = {
data,
timestamp: Date.now(),
etag
};
this.memoryCache.set(url, cacheEntry);
await this.setLocalCache(url, cacheEntry);
return data;
}
throw new Error(`Request failed with code: ${response.responseCode}`);
}
// 检查缓存是否有效
private isCacheValid(entry: CacheEntry): boolean {
return Date.now() - entry.timestamp < this.cacheDuration;
}
// 获取本地缓存
private async getLocalCache(url: string): Promise<CacheEntry | null> {
try {
const prefs = await preferences.getPreferences(this.getContext(), 'http_cache');
const cached = await prefs.get(url, '');
if (cached && typeof cached === 'string' && cached.length > 0) {
return JSON.parse(cached) as CacheEntry;
}
} catch (error) {
console.error('Get local cache failed:', error);
}
return null;
}
// 设置本地缓存
private async setLocalCache(url: string, entry: CacheEntry): Promise<void> {
try {
const prefs = await preferences.getPreferences(this.getContext(), 'http_cache');
await prefs.put(url, JSON.stringify(entry));
await prefs.flush();
} catch (error) {
console.error('Set local cache failed:', error);
}
}
// 清除缓存
clearCache(): void {
this.memoryCache.clear();
}
}
5.3 请求重试机制
// utils/RetryManager.ets
import { http } from '@kit.NetworkKit';
interface RetryOptions {
maxRetries: number;
retryDelay: number;
backoffMultiplier: number;
}
export class RetryManager {
private defaultOptions: RetryOptions = {
maxRetries: 3,
retryDelay: 1000,
backoffMultiplier: 2
};
async fetchWithRetry(
url: string,
options?: http.HttpRequestOptions,
retryOptions?: Partial<RetryOptions>
): Promise<http.HttpResponse> {
const config = { ...this.defaultOptions, ...retryOptions };
let lastError: Error | null = null;
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
try {
const httpRequest = http.createHttp();
const response = await httpRequest.request(url, options);
httpRequest.destroy();
// 成功则返回
if (response.responseCode && response.responseCode >= 200 && response.responseCode < 300) {
return response;
}
// 4xx错误不重试
if (response.responseCode && response.responseCode >= 400 && response.responseCode < 500) {
throw new Error(`Client error: ${response.responseCode}`);
}
lastError = new Error(`Server error: ${response.responseCode}`);
} catch (error) {
lastError = error as Error;
// 最后一次尝试失败,抛出错误
if (attempt === config.maxRetries) {
throw lastError;
}
// 等待后重试(指数退避)
const delay = config.retryDelay * Math.pow(config.backoffMultiplier, attempt);
console.info(`Retry attempt ${attempt + 1} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError || new Error('Unknown error');
}
}
// 使用示例
export async function fetchProductList(): Promise<any> {
const retryManager = new RetryManager();
const response = await retryManager.fetchWithRetry(
'https://api.example.com/products',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
}
},
{
maxRetries: 3,
retryDelay: 1000,
backoffMultiplier: 2
}
);
return JSON.parse(response.result as string);
}
5.4 网络优化踩坑记录
踩坑5:未处理请求取消导致的内存泄漏
// 错误示例:未取消请求
@Entry
@Component
struct RequestLeak {
@State data: string = '';
aboutToAppear(): void {
// 错误:组件销毁后请求仍在执行,回调会访问已销毁的组件
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
this.data = JSON.stringify(data); // 危险:组件可能已销毁
});
}
build() {
Text(this.data)
}
}
// 正确示例:使用AbortController取消请求
@Entry
@Component
struct RequestNoLeak {
@State data: string = '';
private controller: AbortController | null = null;
private isDestroyed: boolean = false;
aboutToAppear(): void {
this.controller = new AbortController();
fetch('https://api.example.com/data', {
signal: this.controller.signal
})
.then(response => response.json())
.then(data => {
if (!this.isDestroyed) {
this.data = JSON.stringify(data);
}
})
.catch(error => {
if (error.name !== 'AbortError') {
console.error('Request failed:', error);
}
});
}
aboutToDisappear(): void {
this.isDestroyed = true;
this.controller?.abort();
}
build() {
Text(this.data)
}
}
六、性能分析工具使用指南
6.1 DevEco Studio Profiler
DevEco Studio提供了强大的性能分析工具:
-
CPU Profiler:分析CPU使用情况
菜单路径:View -> Tool Windows -> Profiler -
Memory Profiler:监控内存使用和泄漏
-
查看内存分配曲线
-
检测内存泄漏点
-
分析对象生命周期
-
-
Network Profiler:分析网络请求
-
查看请求耗时
-
分析响应大小
-
检测请求失败
-
6.2 性能监控工具类
// utils/PerformanceMonitor.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
export class PerformanceMonitor {
private static marks: Map<string, number> = new Map();
private static measures: Map<string, number> = new Map();
// 标记时间点
static mark(name: string): void {
this.marks.set(name, Date.now());
hilog.info(0x0000, 'Performance', `Mark: ${name}`);
}
// 测量两个时间点之间的耗时
static measure(name: string, startMark: string, endMark: string): number {
const startTime = this.marks.get(startMark);
const endTime = this.marks.get(endMark);
if (!startTime || !endTime) {
hilog.error(0x0000, 'Performance', `Marks not found: ${startMark}, ${endMark}`);
return -1;
}
const duration = endTime - startTime;
this.measures.set(name, duration);
hilog.info(0x0000, 'Performance', `Measure: ${name} = ${duration}ms`);
return duration;
}
// 函数耗时装饰器
static measureFunction<T extends (...args: any[]) => any>(
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = Date.now();
const result = originalMethod.apply(this, args);
const end = Date.now();
hilog.info(0x0000, 'Performance',
`Function ${propertyKey} took ${end - start}ms`);
return result;
} as T;
return descriptor;
}
// 获取所有性能数据
static getReport(): Record<string, number> {
const report: Record<string, number> = {};
this.measures.forEach((value, key) => {
report[key] = value;
});
return report;
}
// 清除所有标记
static clear(): void {
this.marks.clear();
this.measures.clear();
}
}
// 使用示例
@Entry
@Component
struct PerformanceDemo {
@State renderTime: number = 0;
aboutToAppear(): void {
PerformanceMonitor.mark('pageInit');
}
aboutToRender(): void {
PerformanceMonitor.mark('renderStart');
}
onAppear(): void {
PerformanceMonitor.mark('renderEnd');
this.renderTime = PerformanceMonitor.measure(
'firstRender', 'renderStart', 'renderEnd'
);
}
build() {
Column() {
Text(`首帧渲染耗时: ${this.renderTime}ms`)
.fontSize(20)
Button('获取性能报告')
.onClick(() => {
const report = PerformanceMonitor.getReport();
console.info('Performance Report:', JSON.stringify(report, null, 2));
})
}
.width('100%')
.padding(20)
}
}
6.3 性能分析最佳实践
// 性能分析配置
export const PerformanceConfig = {
// 开发环境启用详细日志
enableVerboseLog: true,
// 性能阈值配置
thresholds: {
pageLoad: 1000, // 页面加载超过1秒告警
apiRequest: 3000, // API请求超过3秒告警
renderFrame: 16.67, // 渲染帧超过16.67ms告警(60fps)
memoryUsage: 200 // 内存超过200MB告警
},
// 监控开关
monitoring: {
cpu: true,
memory: true,
network: true,
render: true
}
};
// 性能告警处理
export function checkPerformance(name: string, value: number, threshold: number): void {
if (value > threshold) {
hilog.warn(0x0000, 'Performance',
`[WARNING] ${name}: ${value}ms exceeds threshold ${threshold}ms`);
// 可以集成告警系统
// sendAlert(name, value, threshold);
}
}
七、总结与系列回顾
本文要点回顾
本文从四大维度系统讲解了鸿蒙NEXT应用性能优化:
|
优化维度 |
核心策略 |
关键技术 |
|---|---|---|
|
启动优化 |
延迟初始化、并行加载 |
TaskPool、setTimeout |
|
内存管理 |
资源释放、缓存管理 |
WeakRef、PixelMap.release() |
|
列表优化 |
懒加载、组件复用 |
LazyForEach、@Reusable |
|
网络优化 |
请求合并、缓存策略 |
防抖、ETag、本地缓存 |
性能优化核心原则
-
测量先行:先用Profiler定位瓶颈,再针对性优化
-
延迟加载:非首屏内容延迟加载,提升启动速度
-
及时释放:组件销毁时清理所有资源(定时器、监听器、缓存)
-
避免阻塞:耗时操作使用异步或TaskPool
系列回顾
|
期数 |
标题 |
核心内容 |
|---|---|---|
|
第1篇 |
鸿蒙NEXT开发从零到一 |
环境搭建、项目结构、Hello World |
|
第2篇 |
ArkUI组件库完全指南 |
基础组件、布局系统、自定义组件 |
|
第3篇 |
状态管理一文通 |
@State、@Prop、@Link、@Provide/Consume |
|
第4篇 |
数据持久化与网络请求 |
Preferences、RDB、HTTP请求封装 |
|
第5篇 |
性能优化实战指南 |
启动优化、内存管理、列表渲染、网络优化 |
下期预告
感谢大家一路陪伴!本系列博客到此就告一段落了。如果你觉得这个系列对你有帮助,欢迎点赞、收藏、关注!
后续如果有机会,我会继续更新:
-
进阶篇:多线程并发编程、跨设备开发
-
实战篇:完整项目开发实战
-
源码篇:ArkUI框架源码解析
八、参考资料
如果这个系列对你有帮助,请点赞、收藏、关注支持!你的支持是我持续创作的动力!
有问题欢迎在评论区讨论,我会及时回复!
鸿蒙NEXT开发实战系列 -- 全部文章:
-
第5篇:性能优化实战指南(当前)
标签:鸿蒙NEXT | HarmonyOS NEXT | ArkUI | ArkTS | DevEco Studio | 性能优化 | 启动优化 | 内存管理 | 列表渲染 | LazyForEach | TaskPool | Profiler | 组件复用
更多推荐


所有评论(0)