系列文章:鸿蒙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提供了强大的性能分析工具:

  1. CPU Profiler:分析CPU使用情况

    菜单路径:View -> Tool Windows -> Profiler
  2. Memory Profiler:监控内存使用和泄漏

    • 查看内存分配曲线

    • 检测内存泄漏点

    • 分析对象生命周期

  3. 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、本地缓存

性能优化核心原则

  1. 测量先行:先用Profiler定位瓶颈,再针对性优化

  2. 延迟加载:非首屏内容延迟加载,提升启动速度

  3. 及时释放:组件销毁时清理所有资源(定时器、监听器、缓存)

  4. 避免阻塞:耗时操作使用异步或TaskPool

系列回顾

期数

标题

核心内容

第1篇

鸿蒙NEXT开发从零到一

环境搭建、项目结构、Hello World

第2篇

ArkUI组件库完全指南

基础组件、布局系统、自定义组件

第3篇

状态管理一文通

@State、@Prop、@Link、@Provide/Consume

第4篇

数据持久化与网络请求

Preferences、RDB、HTTP请求封装

第5篇

性能优化实战指南

启动优化、内存管理、列表渲染、网络优化

下期预告

感谢大家一路陪伴!本系列博客到此就告一段落了。如果你觉得这个系列对你有帮助,欢迎点赞、收藏、关注!

后续如果有机会,我会继续更新:

  • 进阶篇:多线程并发编程、跨设备开发

  • 实战篇:完整项目开发实战

  • 源码篇:ArkUI框架源码解析


八、参考资料


如果这个系列对你有帮助,请点赞、收藏、关注支持!你的支持是我持续创作的动力!

有问题欢迎在评论区讨论,我会及时回复!


鸿蒙NEXT开发实战系列 -- 全部文章


标签:鸿蒙NEXT | HarmonyOS NEXT | ArkUI | ArkTS | DevEco Studio | 性能优化 | 启动优化 | 内存管理 | 列表渲染 | LazyForEach | TaskPool | Profiler | 组件复用

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐