9  第8章 性能优化

9.1 8.1 UI渲染性能

9.1.1 8.1.1 组件渲染机制

HarmonyOS使用ArkUI框架进行UI开发,其渲染引擎基于声明式UI范式。理解组件渲染机制是进行UI性能优化的基础。

ArkUI的渲染流程可以分为以下几个阶段:

  1. 构建阶段(Build):框架执行组件的build()方法,生成虚拟节点树(VDOM)。
  2. 差异计算(Diff):对比新旧虚拟节点树,计算需要更新的最小变更集。
  3. 布局计算(Layout):根据节点属性和约束条件,计算每个节点的位置和尺寸。
  4. 绘制阶段(Paint):将布局结果转换为GPU可执行的绘制指令。
  5. 合成上屏(Composite):GPU执行绘制指令,将最终画面显示在屏幕上。

每一帧的目标是在16.67ms内完成以上所有步骤(对应60fps刷新率),否则会出现掉帧卡顿。

// 组件渲染生命周期
@Component
struct RenderDemo {
  @State count: number = 0

  // 组件创建时调用,仅执行一次
  aboutToAppear(): void {
    console.info('组件即将出现')
  }

  // 组件渲染完成后调用
  onDidBuild(): void {
    console.info('组件渲染完成')
  }

  // 组件销毁时调用
  aboutToDisappear(): void {
    console.info('组件即将销毁')
  }

  build() {
    Column() {
      Text(`计数: ${this.count}`)
        .fontSize(20)

      Button('增加')
        .onClick(() => {
          this.count++  // 状态变化触发重新渲染
        })
    }
  }
}

9.1.2 8.1.2 布局优化

布局优化是UI性能优化的首要环节。不合理的布局结构会导致过多的测量和布局计算。

减少布局嵌套

深层嵌套的布局会显著增加布局计算的复杂度。应尽量使用扁平化布局。

// 不推荐:深层嵌套
@Component
struct BadLayout {
  build() {
    Column() {                          // 第1层
      Row() {                           // 第2层
        Column() {                      // 第3层
          Row() {                       // 第4层 - 不必要的嵌套
            Text('用户名')
            TextInput()
          }
          Row() {                       // 第4层
            Text('密码')
            TextInput()
          }
        }
      }
    }
  }
}

// 推荐:扁平化布局
@Component
struct GoodLayout {
  build() {
    Column({ space: 16 }) {
      Row({ space: 12 }) {
        Text('用户名').width(80)
        TextInput().layoutWeight(1)
      }

      Row({ space: 12 }) {
        Text('密码').width(80)
        TextInput().layoutWeight(1)
      }
    }
    .padding(16)
  }
}

使用相对布局替代嵌套线性布局

// 使用Stack和相对定位减少嵌套
@Component
struct RelativeDemo {
  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      // 背景层
      Image($r('app.media.background'))
        .width('100%')
        .height(200)

      // 内容层 - 使用margin定位而非嵌套容器
      Column() {
        Text('标题')
          .fontSize(24)
          .fontColor(Color.White)
          .margin({ top: 60, left: 16 })

        Text('副标题')
          .fontSize(14)
          .fontColor(Color.White)
          .margin({ top: 8, left: 16 })
      }
      .width('100%')
    }
  }
}

合理使用layoutWeight

// 使用layoutWeight实现弹性布局,避免硬编码尺寸
@Component
struct FlexLayoutDemo {
  build() {
    Row() {
      // 左侧固定宽度
      Image($r('app.media.avatar'))
        .width(48)
        .height(48)
        .borderRadius(24)
        .margin({ right: 12 })

      // 中间自适应
      Column() {
        Text('用户名').fontSize(16).fontWeight(FontWeight.Medium)
        Text('个性签名').fontSize(12).fontColor(Color.Gray)
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)

      // 右侧固定宽度
      Button('关注')
        .width(64)
        .height(32)
    }
    .width('100%')
    .padding(12)
  }
}

9.1.3 8.1.3 按需渲染与局部刷新

ArkUI框架通过@State@Prop@Link等状态变量实现精确的局部刷新。只有依赖了变化状态变量的组件才会重新渲染。

// 精确控制刷新范围
@Component
struct ChildComponent {
  @Prop title: string  // @Prop 仅在父组件传入值变化时刷新

  aboutToAppear(): void {
    console.info('ChildComponent aboutToAppear')
  }

  build() {
    Column() {
      Text(this.title)
    }
  }
}

@Component
struct ParentComponent {
  @State title: string = '初始标题'
  @State unrelatedData: number = 0

  build() {
    Column() {
      // 只有title变化时,ChildComponent才会重新渲染
      ChildComponent({ title: this.title })

      // 这个按钮的变化不会影响ChildComponent
      Button(`无关计数: ${this.unrelatedData}`)
        .onClick(() => {
          this.unrelatedData++  // 仅刷新Button,不刷新ChildComponent
        })

      Button('修改标题')
        .onClick(() => {
          this.title = '新标题'  // 仅刷新ChildComponent
        })
    }
  }
}

使用@Observed和@ObjectLink优化对象属性刷新

@Observed
class TaskItem {
  id: string
  title: string
  isCompleted: boolean

  constructor(id: string, title: string, isCompleted: boolean = false) {
    this.id = id
    this.title = title
    this.isCompleted = isCompleted
  }
}

// 子组件仅监听特定属性的变化
@Component
struct TaskItemView {
  @ObjectLink task: TaskItem

  build() {
    Row() {
      Checkbox()
        .select(this.task.isCompleted)
        .onChange((checked: boolean) => {
          this.task.isCompleted = checked  // 仅刷新当前项
        })

      Text(this.task.title)
        .decoration({
          type: this.task.isCompleted ? TextDecorationType.LineThrough : TextDecorationType.None
        })
        .fontColor(this.task.isCompleted ? Color.Gray : Color.Black)
    }
    .width('100%')
    .padding(12)
  }
}

@Component
struct TaskList {
  @State tasks: TaskItem[] = [
    new TaskItem('1', '学习ArkTS'),
    new TaskItem('2', '完成项目'),
    new TaskItem('3', '编写测试')
  ]

  build() {
    List() {
      ForEach(this.tasks, (task: TaskItem) => {
        ListItem() {
          TaskItemView({ task: task })
        }
      }, (task: TaskItem) => task.id)  // 使用唯一key
    }
  }
}

9.1.4 8.1.4 组件复用(@Reusable

@Reusable装饰器标记组件可以被复用,避免频繁创建和销毁组件带来的性能开销。这在列表场景中效果尤为显著。

// 使用@Reusable标记可复用组件
@Reusable
@Component
struct ProductCard {
  @State product: Product | null = null

  // 组件复用时回调,更新数据
  aboutToReuse(params: Record<string, Object>): void {
    this.product = params.product as Product
    console.info(`组件被复用,新产品: ${this.product?.name}`)
  }

  build() {
    if (this.product) {
      Row() {
        Image(this.product.imageUrl)
          .width(80)
          .height(80)
          .borderRadius(8)
          .objectFit(ImageFit.Cover)

        Column() {
          Text(this.product.name)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })

          Text(${this.product.price.toFixed(2)}`)
            .fontSize(14)
            .fontColor(Color.Red)
            .margin({ top: 4 })

          Text(`已售 ${this.product.soldCount} 件`)
            .fontSize(12)
            .fontColor(Color.Gray)
            .margin({ top: 2 })
        }
        .layoutWeight(1)
        .margin({ left: 12 })
        .alignItems(HorizontalAlign.Start)
      }
      .width('100%')
      .padding(12)
      .backgroundColor(Color.White)
      .borderRadius(12)
    }
  }
}

interface Product {
  id: string
  name: string
  price: number
  imageUrl: string
  soldCount: number
}

9.1.5 8.1.5 列表性能优化

列表是应用中最常见的UI组件,其性能直接影响用户体验。LazyForEach配合唯一key是列表性能优化的核心方案。

// 实现IDataSource接口用于LazyForEach
class ProductDataSource implements IDataSource {
  private products: Product[] = []
  private listeners: DataChangeListener[] = []

  totalCount(): number {
    return this.products.length
  }

  getData(index: number): Product {
    return this.products[index]
  }

  // 追加数据
  appendData(newProducts: Product[]): void {
    const startIndex = this.products.length
    this.products.push(...newProducts)
    // 通知框架新增数据
    for (const listener of this.listeners) {
      listener.onDataAdd(startIndex, newProducts.length)
    }
  }

  // 更新单条数据
  updateItem(index: number, product: Product): void {
    this.products[index] = product
    for (const listener of this.listeners) {
      listener.onDataChange(index)
    }
  }

  // 删除数据
  removeItem(index: number): void {
    this.products.splice(index, 1)
    for (const listener of this.listeners) {
      listener.onDataDelete(index, 1)
    }
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listeners.push(listener)
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const index = this.listeners.indexOf(listener)
    if (index >= 0) {
      this.listeners.splice(index, 1)
    }
  }
}

// 高性能列表组件
@Component
struct OptimizedProductList {
  private dataSource: ProductDataSource = new ProductDataSource()
  private isLoadingMore: boolean = false

  aboutToAppear(): void {
    // 初始加载
    this.loadProducts()
  }

  async loadProducts(): Promise<void> {
    const products = await fetchProducts(0, 20)
    this.dataSource.appendData(products)
  }

  async loadMore(): Promise<void> {
    if (this.isLoadingMore) return
    this.isLoadingMore = true

    try {
      const products = await fetchProducts(this.dataSource.totalCount(), 20)
      this.dataSource.appendData(products)
    } finally {
      this.isLoadingMore = false
    }
  }

  build() {
    List({ space: 8 }) {
      // 使用LazyForEach实现按需加载
      LazyForEach(this.dataSource, (product: Product, index: number) => {
        ListItem() {
          ProductCard({ product: product })
        }
        .onAppear(() => {
          // 接近底部时触发加载更多
          if (index >= this.dataSource.totalCount() - 5) {
            this.loadMore()
          }
        })
      }, (product: Product) => product.id)  // 唯一key,至关重要
    }
    .width('100%')
    .height('100%')
    .cachedCount(5)  // 缓存屏幕外5个item
    .divider({
      strokeWidth: 0.5,
      color: '#F0F0F0',
      startMargin: 12,
      endMargin: 12
    })
  }
}

// 模拟网络请求
async function fetchProducts(offset: number, limit: number): Promise<Product[]> {
  return []
}

列表优化的关键要点:

  1. 必须使用唯一且稳定的key:key用于Diff算法识别列表项身份,使用不稳定的key(如index)会导致不必要的组件重建。
  2. 使用LazyForEach替代ForEach:LazyForEach仅在item进入可视区域时才创建组件,大幅减少初始渲染开销。
  3. 设置合理的cachedCount:缓存适量的屏幕外item,平衡滚动流畅度和内存占用。
  4. 使用@Reusable复用组件:减少item创建和销毁的开销。

9.2 8.2 启动性能优化

9.2.1 8.2.1 冷启动/热启动优化

应用启动分为冷启动和热启动两种模式:

  • 冷启动:应用进程不存在,需要完整创建进程、加载代码、初始化框架。
  • 热启动:应用进程已存在但Activity被回收,需要重建UI。

冷启动耗时远大于热启动,是优化的重点。

// EntryAbility启动优化
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit'
import { window } from '@kit.ArkUI'

export default class EntryAbility extends UIAbility {
  private windowStage: window.WindowStage | null = null

  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 1. 仅做最小初始化,避免onCreate中执行耗时操作
    console.info('Ability onCreate')

    // 2. 预加载关键数据(异步,不阻塞启动)
    this.preloadCriticalData()

    // 3. 延迟初始化非关键服务
    setTimeout(() => {
      this.initNonCriticalServices()
    }, 0)
  }

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    this.windowStage = windowStage

    // 1. 设置启动页(Splash Screen)
    await windowStage.setWindowLayoutFullScreen(true)

    // 2. 加载首页UI
    windowStage.loadContent('pages/Index', (err) => {
      if (err) {
        console.error(`页面加载失败: ${err.message}`)
        return
      }
      console.info('首页加载完成')
    })
  }

  // 预加载关键数据
  private async preloadCriticalData(): Promise<void> {
    // 并行预加载多个数据源
    const [userConfig, appConfig] = await Promise.all([
      this.loadUserConfig(),
      this.loadAppConfig()
    ])
  }

  // 延迟初始化非关键服务
  private initNonCriticalServices(): void {
    // 日志上报、性能监控等非关键服务
    // 在首屏渲染完成后初始化
  }

  private async loadUserConfig(): Promise<object> {
    return {}
  }

  private async loadAppConfig(): Promise<object> {
    return {}
  }
}

9.2.2 8.2.2 应用启动流程分析

HarmonyOS应用启动流程包含以下阶段:

  1. 进程创建阶段:系统创建应用进程,加载运行时环境。
  2. Ability创建阶段:框架创建UIAbility实例,执行onCreate回调。
  3. Window创建阶段:创建窗口,执行onWindowStageCreate回调。
  4. 页面加载阶段:加载首页UI资源,执行组件build()方法。
  5. 首帧渲染阶段:完成首屏数据加载和UI渲染。
// 启动耗时监控
class StartupMonitor {
  private static startTime: number = 0
  private static milestones: Map<string, number> = new Map()

  static markStart(): void {
    StartupMonitor.startTime = Date.now()
  }

  static mark(milestoneName: string): void {
    const elapsed = Date.now() - StartupMonitor.startTime
    StartupMonitor.milestones.set(milestoneName, elapsed)
    console.info(`[启动监控] ${milestoneName}: ${elapsed}ms`)
  }

  static report(): void {
    const report: Record<string, number> = {}
    for (const [name, time] of StartupMonitor.milestones) {
      report[name] = time
    }
    // 上报到监控平台
    console.info(`[启动监控报告] ${JSON.stringify(report)}`)
  }
}

// 在启动流程中打点
export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    StartupMonitor.markStart()
    StartupMonitor.mark('onCreate')
    // ...
  }

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    StartupMonitor.mark('onWindowStageCreate')

    windowStage.loadContent('pages/Index', (err) => {
      if (!err) {
        StartupMonitor.mark('loadContent_done')
        StartupMonitor.report()
      }
    })
  }
}

9.2.3 8.2.3 延迟加载策略

将非首屏必需的资源和逻辑延迟加载,是降低启动耗时的有效手段。

// 路由级别的延迟加载
// 使用router进行页面懒加载
import { router } from '@kit.ArkUI'

// 在首页中,非首屏模块延迟加载
@Component
struct HomePage {
  @State isModuleLoaded: boolean = false
  @State recommendationData: object[] = []

  aboutToAppear(): void {
    // 首屏数据优先加载
    this.loadEssentialData()

    // 非首屏数据延迟加载
    setTimeout(() => {
      this.loadRecommendation()
    }, 500)  // 延迟500ms
  }

  async loadEssentialData(): Promise<void> {
    // 加载首屏必需数据
  }

  async loadRecommendation(): Promise<void> {
    // 延迟加载推荐数据
    this.recommendationData = await fetchRecommendations()
    this.isModuleLoaded = true
  }

  build() {
    Column() {
      // 首屏内容 - 立即渲染
      BannerSection()
      CategorySection()

      // 推荐模块 - 延迟渲染
      if (this.isModuleLoaded) {
        RecommendationSection({ data: this.recommendationData })
      } else {
        // 占位骨架屏
        SkeletonPlaceholder()
      }
    }
  }
}

// 骨架屏组件
@Component
struct SkeletonPlaceholder {
  build() {
    Column({ space: 12 }) {
      ForEach([1, 2, 3], () => {
        Row() {
          Column()
            .width(80)
            .height(80)
            .borderRadius(8)
            .backgroundColor('#F0F0F0')

          Column({ space: 8 }) {
            Column()
              .width('60%')
              .height(16)
              .borderRadius(4)
              .backgroundColor('#F0F0F0')
            Column()
              .width('40%')
              .height(12)
              .borderRadius(4)
              .backgroundColor('#F0F0F0')
          }
          .layoutWeight(1)
        }
        .width('100%')
        .padding(12)
      })
    }
  }
}

9.2.4 8.2.4 预加载技术

预加载技术可以在用户操作之前提前加载可能需要的资源。

// 页面预加载管理器
class PagePreloader {
  private static preloadQueue: string[] = []
  private static isPreloading: boolean = false

  // 在首页加载完成后预加载二级页面
  static startPreload(pages: string[]): void {
    PagePreloader.preloadQueue = [...pages]
    PagePreloader.preloadNext()
  }

  private static async preloadNext(): Promise<void> {
    if (PagePreloader.preloadQueue.length === 0 || PagePreloader.isPreloading) {
      return
    }

    PagePreloader.isPreloading = true
    const page = PagePreloader.preloadQueue.shift()!

    try {
      // 预加载页面资源
      await router.preloadPage({
        url: page
      })
      console.info(`预加载页面成功: ${page}`)
    } catch (error) {
      console.warn(`预加载页面失败: ${page}`)
    } finally {
      PagePreloader.isPreloading = false
      // 继续预加载下一个页面
      PagePreloader.preloadNext()
    }
  }
}

// 使用示例
@Component
struct HomePage {
  aboutToAppear(): void {
    // 首屏渲染完成后预加载常用页面
    setTimeout(() => {
      PagePreloader.startPreload([
        'pages/ProductDetail',
        'pages/ShoppingCart',
        'pages/UserCenter'
      ])
    }, 2000)
  }

  build() {
    // 首页内容...
    Column() {}
  }
}

9.2.5 8.2.5 启动耗时测量工具

// 启动性能分析工具
class StartupProfiler {
  private phases: StartupPhase[] = []
  private currentPhase: string = ''
  private phaseStartTime: number = 0

  startPhase(phaseName: string): void {
    if (this.currentPhase) {
      this.endPhase()
    }
    this.currentPhase = phaseName
    this.phaseStartTime = Date.now()
  }

  endPhase(): void {
    const duration = Date.now() - this.phaseStartTime
    this.phases.push({
      name: this.currentPhase,
      startTime: this.phaseStartTime,
      duration: duration
    })
    this.currentPhase = ''
  }

  getReport(): StartupReport {
    const totalDuration = this.phases.reduce((sum, p) => sum + p.duration, 0)
    const bottleneck = this.phases.reduce((max, p) =>
      p.duration > max.duration ? p : max
    )

    return {
      phases: this.phases,
      totalDuration: totalDuration,
      bottleneck: bottleneck,
      suggestions: this.generateSuggestions()
    }
  }

  private generateSuggestions(): string[] {
    const suggestions: string[] = []

    for (const phase of this.phases) {
      if (phase.name === 'onCreate' && phase.duration > 200) {
        suggestions.push('onCreate耗时超过200ms,建议检查是否有同步IO操作')
      }
      if (phase.name === 'loadContent' && phase.duration > 500) {
        suggestions.push('页面加载耗时超过500ms,建议优化首屏组件复杂度')
      }
      if (phase.name === 'dataLoad' && phase.duration > 1000) {
        suggestions.push('数据加载耗时超过1s,建议使用缓存或预加载')
      }
    }

    return suggestions
  }
}

interface StartupPhase {
  name: string
  startTime: number
  duration: number
}

interface StartupReport {
  phases: StartupPhase[]
  totalDuration: number
  bottleneck: StartupPhase
  suggestions: string[]
}

9.3 8.3 内存管理

9.3.1 8.3.1 ArkTS内存模型

ArkTS运行在Ark虚拟机上,采用自动内存管理机制。理解内存模型有助于编写高效的代码。

内存主要分为以下区域:

  • 栈(Stack):存储局部变量、函数调用帧。自动分配和释放,速度快但空间有限。
  • 堆(Heap):存储对象实例。由GC(垃圾回收器)管理,空间大但分配和回收开销较高。
  • Native内存:存储图片、音视频等大对象。需要手动管理或使用NAPI接口。
// 内存使用示例
@Component
struct MemoryDemo {
  // 这些变量存储在栈上(基本类型)或堆上(对象类型)
  @State count: number = 0           // 基本类型,栈存储
  @State name: string = ''            // 字符串对象,堆存储
  @State items: number[] = []         // 数组对象,堆存储
  private largeData: ArrayBuffer | null = null  // 大对象

  processData(): void {
    // 局部变量在函数返回后自动释放
    const tempArray: number[] = []
    for (let i = 0; i < 10000; i++) {
      tempArray.push(i)
    }
    // tempArray在函数结束后变为垃圾,等待GC回收
  }

  // 大对象需要手动释放
  loadLargeFile(): void {
    this.largeData = new ArrayBuffer(50 * 1024 * 1024)  // 50MB
    // 使用完毕后应置为null
  }

  releaseLargeFile(): void {
    this.largeData = null  // 解除引用,允许GC回收
  }

  build() {
    Column() {
      Button('处理数据').onClick(() => this.processData())
      Button('释放大对象').onClick(() => this.releaseLargeFile())
    }
  }
}

9.3.2 8.3.2 内存泄漏检测与预防

内存泄漏是应用性能退化的常见原因。以下是常见的内存泄漏场景和预防方法。

// 场景1:未清理的定时器
@Component
struct TimerLeakDemo {
  private timerId: number = -1

  aboutToAppear(): void {
    this.timerId = setInterval(() => {
      // 如果组件销毁后定时器仍在运行,会导致内存泄漏
      this.updateData()
    }, 1000) as number
  }

  // 正确做法:在组件销毁时清理定时器
  aboutToDisappear(): void {
    if (this.timerId !== -1) {
      clearInterval(this.timerId)
      this.timerId = -1
    }
  }

  updateData(): void {
    // 更新数据
  }

  build() {
    Column() {}
  }
}

// 场景2:未取消的事件监听
@Component
struct EventListenerLeakDemo {
  aboutToAppear(): void {
    // 注册全局事件监听
    EventBus.getInstance().on('data_updated', this.handleDataUpdate)
  }

  // 正确做法:在组件销毁时取消监听
  aboutToDisappear(): void {
    EventBus.getInstance().off('data_updated', this.handleDataUpdate)
  }

  handleDataUpdate = (data: object): void => {
    // 处理数据更新
  }

  build() {
    Column() {}
  }
}

// 场景3:闭包持有大对象引用
class DataProcessor {
  private callback: (() => void) | null = null

  // 不推荐:闭包持有大对象
  processWithClosure(largeData: ArrayBuffer): void {
    this.callback = () => {
      // 闭包持有largeData的引用,即使process完成也不会释放
      console.info(`数据大小: ${largeData.byteLength}`)
    }
  }

  // 推荐:显式传递需要的值
  processExplicitly(largeData: ArrayBuffer): void {
    const dataSize = largeData.byteLength  // 只捕获需要的值
    this.callback = () => {
      console.info(`数据大小: ${dataSize}`)
    }
  }

  destroy(): void {
    this.callback = null  // 释放回调引用
  }
}

9.3.3 8.3.3 图片内存优化

图片是应用中最常见的内存消耗源。合理的图片处理策略可以显著降低内存占用。

// 图片加载优化
@Component
struct OptimizedImage {
  @State imageUrl: string = ''

  build() {
    // 优化1:指定明确的尺寸,避免加载原图
    Image(this.imageUrl)
      .width(100)
      .height(100)
      .objectFit(ImageFit.Cover)        // 裁剪适配
      .interpolation(ImageInterpolation.Low)  // 低质量插值,减少CPU开销
      .alt($r('app.media.placeholder'))  // 占位图

    // 优化2:列表中使用合适尺寸的图片
    // 在实际项目中,应根据显示尺寸请求对应分辨率的图片
  }
}

// 图片缓存管理器
class ImageCacheManager {
  private static instance: ImageCacheManager | null = null
  private memoryCache: Map<string, PixelMap> = new Map()
  private maxCacheSize: number = 50  // 最多缓存50张图片

  static getInstance(): ImageCacheManager {
    if (!ImageCacheManager.instance) {
      ImageCacheManager.instance = new ImageCacheManager()
    }
    return ImageCacheManager.instance
  }

  // 获取缓存的图片
  getCachedImage(url: string): PixelMap | null {
    const cached = this.memoryCache.get(url)
    if (cached) {
      return cached
    }
    return null
  }

  // 缓存图片
  cacheImage(url: string, pixelMap: PixelMap): void {
    // 缓存满时淘汰最早的
    if (this.memoryCache.size >= this.maxCacheSize) {
      const firstKey = this.memoryCache.keys().next().value
      if (firstKey) {
        const oldPixelMap = this.memoryCache.get(firstKey)
        oldPixelMap?.release()  // 释放PixelMap资源
        this.memoryCache.delete(firstKey)
      }
    }
    this.memoryCache.set(url, pixelMap)
  }

  // 清空缓存
  clearCache(): void {
    for (const pixelMap of this.memoryCache.values()) {
      pixelMap.release()
    }
    this.memoryCache.clear()
  }
}

// 带降采样的图片加载
async function loadDecimatedImage(
  context: Context,
  resourceId: number,
  targetWidth: number,
  targetHeight: number
): Promise<PixelMap> {
  const resourceManager = context.resourceManager
  const buffer = await resourceManager.getMediaContent(resourceId)

  const imageSource = image.createImageSource(buffer)

  // 使用解码选项进行降采样
  const decodingOptions: image.DecodingOptions = {
    editable: false,
    desiredPixelFormat: 3,  // RGBA_8888
    desiredSize: {
      width: targetWidth,
      height: targetHeight
    },
    // 按比例降采样,减少内存占用
    sampleSize: calculateSampleSize(buffer, targetWidth, targetHeight)
  }

  const pixelMap = await imageSource.createPixelMap(decodingOptions)
  imageSource.release()

  return pixelMap
}

function calculateSampleSize(
  buffer: ArrayBuffer,
  targetWidth: number,
  targetHeight: number
): number {
  // 计算合适的采样率
  return 1  // 简化示例
}

9.3.4 8.3.4 大对象管理

对于大对象(如大型数组、文件缓冲区等),需要特别注意内存管理。

// 大对象池
class ObjectPool<T> {
  private pool: T[] = []
  private maxSize: number
  private factory: () => T
  private reset: (obj: T) => void

  constructor(maxSize: number, factory: () => T, reset: (obj: T) => void) {
    this.maxSize = maxSize
    this.factory = factory
    this.reset = reset
  }

  acquire(): T {
    if (this.pool.length > 0) {
      return this.pool.pop()!
    }
    return this.factory()
  }

  release(obj: T): void {
    if (this.pool.length < this.maxSize) {
      this.reset(obj)
      this.pool.push(obj)
    }
    // 超过池容量,直接丢弃让GC回收
  }

  clear(): void {
    this.pool = []
  }
}

// 使用示例:ArrayBuffer对象池
const bufferPool = new ObjectPool<ArrayBuffer>(
  10,
  () => new ArrayBuffer(1024 * 1024),  // 创建1MB缓冲区
  (buffer: ArrayBuffer) => {
    // 重置缓冲区(清零)
    new Uint8Array(buffer).fill(0)
  }
)

// 获取缓冲区
const buffer = bufferPool.acquire()
// 使用缓冲区...
// 释放回池中
bufferPool.release(buffer)

9.3.5 8.3.5 内存回收策略与资源释放

// 资源管理器 - 统一管理应用中的各类资源
class ResourceManager {
  private static instance: ResourceManager | null = null
  private resources: Map<string, Disposable> = new Map()

  static getInstance(): ResourceManager {
    if (!ResourceManager.instance) {
      ResourceManager.instance = new ResourceManager()
    }
    return ResourceManager.instance
  }

  // 注册可释放资源
  register(id: string, resource: Disposable): void {
    this.resources.set(id, resource)
  }

  // 释放指定资源
  async dispose(id: string): Promise<void> {
    const resource = this.resources.get(id)
    if (resource) {
      await resource.dispose()
      this.resources.delete(id)
    }
  }

  // 释放所有资源
  async disposeAll(): Promise<void> {
    for (const [id, resource] of this.resources) {
      try {
        await resource.dispose()
      } catch (error) {
        console.error(`释放资源 ${id} 失败: ${(error as Error).message}`)
      }
    }
    this.resources.clear()
  }

  // 内存警告处理
  onMemoryWarning(): void {
    console.warn('收到内存警告,开始清理缓存')
    // 清理非关键资源
    this.clearCaches()
  }

  private clearCaches(): void {
    // 清理图片缓存、数据缓存等
    ImageCacheManager.getInstance().clearCache()
  }
}

interface Disposable {
  dispose(): Promise<void>
}

9.4 8.4 功耗优化

9.4.1 8.4.1 后台任务管理

HarmonyOS对后台任务有严格的限制,合理使用后台任务API是功耗优化的关键。

import { backgroundTaskManager } from '@kit.BackgroundTasksKit'

// 后台任务管理器
class BackgroundTaskManager {
  // 启动短时任务(最长3分钟)
  static async startShortTask(taskName: string, taskFn: () => Promise<void>): Promise<void> {
    const shortRequest = new backgroundTaskManager.ShortTimeRequest('dataTransfer')
    shortRequest.serviceExtension = {
      packageName: 'com.example.app',
      abilityName: 'BackgroundAbility'
    }

    try {
      await backgroundTaskManager.startShortTime(shortRequest)
      await taskFn()
      await backgroundTaskManager.stopShortTime(shortRequest)
    } catch (error) {
      console.error(`短时任务 ${taskName} 执行失败`)
      await backgroundTaskManager.stopShortTime(shortRequest)
    }
  }

  // 启动长时任务(需要声明权限)
  static async startLongTask(
    taskType: backgroundTaskManager.WorkMode,
    taskFn: () => Promise<void>
  ): Promise<void> {
    const longRequest = new backgroundTaskManager.LongTimeRequest(taskType)

    try {
      await backgroundTaskManager.startLongTime(longRequest)
      await taskFn()
    } finally {
      await backgroundTaskManager.stopLongTime(longRequest)
    }
  }
}

// 使用示例:后台数据同步
async function syncDataInBackground(): Promise<void> {
  await BackgroundTaskManager.startShortTask('dataSync', async () => {
    const syncManager = new SyncManager()
    await syncManager.incrementalSync('orders')
  })
}

9.4.2 8.4.2 定位服务优化

定位服务是功耗大户,需要合理使用定位策略。

import { geoLocationManager } from '@kit.LocationKit'

// 定位服务优化管理器
class LocationOptimizer {
  private isLocating: boolean = false

  // 场景1:需要精确定位(如导航)
  async startPreciseLocation(callback: (location: geoLocationManager.Location) => void): Promise<void> {
    const request: geoLocationManager.LocationRequest = {
      priority: geoLocationManager.LocationRequestPriority.Accuracy,
      scenario: geoLocationManager.LocationRequestScenario.Navigation,
      interval: 1000,           // 1秒更新一次
      maxAccuracy: 10           // 精度10米
    }

    this.isLocating = true
    await geoLocationManager.on('location', request, callback)
  }

  // 场景2:只需大致位置(如附近推荐)
  async startRoughLocation(callback: (location: geoLocationManager.Location) => void): Promise<void> {
    const request: geoLocationManager.LocationRequest = {
      priority: geoLocationManager.LocationRequestPriority.LocatingMode,
      scenario: geoLocationManager.LocationRequestScenario.SceneBased,
      interval: 60000,          // 60秒更新一次
      maxAccuracy: 1000         // 精度1000米
    }

    this.isLocating = true
    await geoLocationManager.on('location', request, callback)
  }

  // 场景3:单次定位(如签到)
  async getSingleLocation(): Promise<geoLocationManager.Location> {
    const request: geoLocationManager.LocationRequest = {
      priority: geoLocationManager.LocationRequestPriority.Accuracy,
      scenario: geoLocationManager.LocationRequestScenario.Traffic,
      maxAccuracy: 50,
      interval: 0  // 单次定位
    }

    return await geoLocationManager.getCurrentLocation(request)
  }

  // 停止定位
  async stopLocation(callback: (location: geoLocationManager.Location) => void): Promise<void> {
    await geoLocationManager.off('location', callback)
    this.isLocating = false
  }
}

9.4.3 8.4.3 网络请求优化

网络请求是主要的功耗来源之一。通过批量请求和缓存策略可以显著降低功耗。

// 批量请求合并
class BatchRequestOptimizer {
  private pendingRequests: Map<string, {
    resolve: (value: object) => void,
    reject: (reason: Error) => void
  }> = new Map()
  private batchTimer: number = -1
  private batchDelay: number = 100  // 100ms内的请求合并

  // 发起请求(自动合并)
  async request(url: string, params: object): Promise<object> {
    return new Promise((resolve, reject) => {
      const key = `${url}:${JSON.stringify(params)}`
      this.pendingRequests.set(key, { resolve, reject })

      if (this.batchTimer === -1) {
        this.batchTimer = setTimeout(() => this.flushBatch(), this.batchDelay) as number
      }
    })
  }

  // 批量发送
  private async flushBatch(): Promise<void> {
    const requests = Array.from(this.pendingRequests.entries())
    this.pendingRequests.clear()
    this.batchTimer = -1

    if (requests.length === 0) return

    try {
      // 合并为单个批量请求
      const batchPayload = requests.map(([key]) => {
        const [url, paramsStr] = key.split(':')
        return { url, params: JSON.parse(paramsStr) }
      })

      const results = await this.sendBatchRequest(batchPayload)

      // 分发结果
      for (let i = 0; i < requests.length; i++) {
        const { resolve } = requests[i][1]
        resolve(results[i])
      }
    } catch (error) {
      for (const [, { reject }] of requests) {
        reject(error as Error)
      }
    }
  }

  private async sendBatchRequest(batch: object[]): Promise<object[]> {
    // 发送批量请求到服务端
    return []
  }
}

// 网络缓存策略
class NetworkCacheStrategy {
  // 缓存优先策略
  async cacheFirst<T>(key: string, fetchFn: () => Promise<T>, ttl: number): Promise<T> {
    // 先查缓存
    const cached = await this.getFromCache<T>(key)
    if (cached && !this.isExpired(cached.timestamp, ttl)) {
      // 后台静默更新缓存
      this.silentRefresh(key, fetchFn)
      return cached.data
    }

    // 缓存未命中或已过期,发起网络请求
    const data = await fetchFn()
    await this.saveToCache(key, data)
    return data
  }

  // 网络优先策略
  async networkFirst<T>(key: string, fetchFn: () => Promise<T>): Promise<T> {
    try {
      const data = await fetchFn()
      await this.saveToCache(key, data)
      return data
    } catch (error) {
      // 网络失败,降级到缓存
      const cached = await this.getFromCache<T>(key)
      if (cached) {
        return cached.data
      }
      throw error
    }
  }

  private async getFromCache<T>(key: string): Promise<{ data: T; timestamp: number } | null> {
    return null
  }

  private async saveToCache<T>(key: string, data: T): Promise<void> {}

  private isExpired(timestamp: number, ttl: number): boolean {
    return Date.now() - timestamp > ttl
  }

  private async silentRefresh<T>(key: string, fetchFn: () => Promise<T>): Promise<void> {
    try {
      const data = await fetchFn()
      await this.saveToCache(key, data)
    } catch (error) {
      // 静默失败
    }
  }
}

9.4.4 8.4.4 屏幕刷新率管理

根据场景动态调整屏幕刷新率可以节省电量。

import { display } from '@kit.ArkUI'

// 刷新率管理器
class RefreshRateManager {
  // 高刷新率场景(动画、游戏)
  static async setHighRefreshRate(): Promise<void> {
    try {
      const defaultDisplay = display.getDefaultDisplaySync()
      // 请求高刷新率模式
      console.info(`当前刷新率: ${defaultDisplay.refreshRate}Hz`)
    } catch (error) {
      console.error('设置刷新率失败')
    }
  }

  // 标准刷新率场景(普通浏览)
  static async setStandardRefreshRate(): Promise<void> {
    // 设置为60Hz标准刷新率
    console.info('切换到标准刷新率')
  }

  // 低刷新率场景(静态页面、阅读)
  static async setLowRefreshRate(): Promise<void> {
    // 降低刷新率以节省电量
    console.info('切换到低刷新率')
  }
}

9.4.5 8.4.5 传感器使用优化

// 传感器使用优化
class SensorOptimizer {
  // 按需注册传感器
  private sensorCallbacks: Map<number, object> = new Map()

  // 加速度传感器优化
  registerAccelerometer(
    onShake: () => void,
    options?: { threshold?: number; interval?: number }
  ): void {
    const threshold = options?.threshold ?? 15
    const interval = options?.interval ?? 200

    // 使用较低的采样频率,仅在需要时提高
    // sensor.on(sensor.SensorId.ACCELEROMETER, { interval: interval }, (data) => {
    //   if (Math.abs(data.x) > threshold || Math.abs(data.y) > threshold) {
    //     onShake()
    //   }
    // })
  }

  // 页面不可见时自动停止传感器
  onPageHidden(): void {
    // 取消所有传感器监听
    for (const [sensorId] of this.sensorCallbacks) {
      // sensor.off(sensorId)
    }
    this.sensorCallbacks.clear()
  }
}

9.5 8.5 代码性能优化

9.5.1 8.5.1 算法复杂度优化

选择合适的数据结构和算法是性能优化的根本。

// 不推荐:O(n²) 查找
function findDuplicatesBad(arr: number[]): number[] {
  const duplicates: number[] = []
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
        duplicates.push(arr[i])
      }
    }
  }
  return duplicates
}

// 推荐:O(n) 使用Set查找
function findDuplicatesGood(arr: number[]): number[] {
  const seen = new Set<number>()
  const duplicates = new Set<number>()

  for (const num of arr) {
    if (seen.has(num)) {
      duplicates.add(num)
    } else {
      seen.add(num)
    }
  }

  return Array.from(duplicates)
}

// 字符串拼接优化
// 不推荐:字符串直接拼接(每次创建新字符串)
function joinStringsBad(strings: string[]): string {
  let result = ''
  for (const s of strings) {
    result += s  // 每次都创建新字符串对象
  }
  return result
}

// 推荐:使用数组join
function joinStringsGood(strings: string[]): string {
  return strings.join('')
}

9.5.2 8.5.2 数据结构选择

// 不同数据结构的性能对比
class DataStructureDemo {
  // 场景1:频繁查找 - 使用Map
  lookupDemo(): void {
    // 不推荐:数组查找 O(n)
    const userList: Array<{ id: string; name: string }> = []
    const findById = (id: string) => userList.find(u => u.id === id)  // O(n)

    // 推荐:Map查找 O(1)
    const userMap = new Map<string, { id: string; name: string }>()
    const findByIdFast = (id: string) => userMap.get(id)  // O(1)
  }

  // 场景2:去重 - 使用Set
  deduplicateDemo(): void {
    const items = [1, 2, 2, 3, 3, 3, 4, 5]

    // 推荐:使用Set去重
    const unique = [...new Set(items)]
  }

  // 场景3:有序数据 - 使用排序数组 + 二分查找
  sortedSearchDemo(): void {
    const sortedArray = [1, 3, 5, 7, 9, 11, 13, 15]

    // 二分查找 O(log n)
    function binarySearch(arr: number[], target: number): number {
      let left = 0
      let right = arr.length - 1

      while (left <= right) {
        const mid = Math.floor((left + right) / 2)
        if (arr[mid] === target) return mid
        if (arr[mid] < target) left = mid + 1
        else right = mid - 1
      }

      return -1
    }

    const index = binarySearch(sortedArray, 7)  // 返回3
  }
}

9.5.3 8.5.3 异步任务编排

合理的异步任务编排可以最大化利用系统资源,减少等待时间。

// 并行执行多个独立任务
async function parallelExecution(): Promise<void> {
  const startTime = Date.now()

  // 推荐:并行执行独立任务
  const [userData, productData, configData] = await Promise.all([
    fetchUserData(),
    fetchProductData(),
    fetchConfigData()
  ])

  console.info(`并行执行耗时: ${Date.now() - startTime}ms`)
}

// 串行执行有依赖的任务
async function sequentialExecution(): Promise<void> {
  // 步骤1:先登录
  const token = await login('user', 'pass')

  // 步骤2:使用Token获取数据
  const data = await fetchDataWithToken(token)

  // 步骤3:处理数据
  const result = await processData(data)
}

// 流水线模式:任务分阶段并行执行
async function pipelineExecution(items: string[]): Promise<object[]> {
  // 阶段1:并行解析所有项目
  const parsed = await Promise.all(
    items.map(item => parseItem(item))
  )

  // 阶段2:并行验证所有项目
  const validated = await Promise.all(
    parsed.map(item => validateItem(item))
  )

  // 阶段3:并行保存所有项目
  const results = await Promise.all(
    validated.map(item => saveItem(item))
  )

  return results
}

// 并发限制器:控制同时执行的任务数
async function limitedConcurrency<T>(
  tasks: Array<() => Promise<T>>,
  maxConcurrency: number
): Promise<T[]> {
  const results: T[] = []
  const executing: Set<Promise<void>> = new Set()

  for (const task of tasks) {
    const p = task().then(result => {
      results.push(result)
    })

    executing.add(p)
    p.finally(() => executing.delete(p))

    // 达到并发上限时等待任意任务完成
    if (executing.size >= maxConcurrency) {
      await Promise.race(executing)
    }
  }

  await Promise.all(executing)
  return results
}

// 模拟函数
async function fetchUserData(): Promise<object> { return {} }
async function fetchProductData(): Promise<object> { return {} }
async function fetchConfigData(): Promise<object> { return {} }
async function login(u: string, p: string): Promise<string> { return '' }
async function fetchDataWithToken(t: string): Promise<object> { return {} }
async function processData(d: object): Promise<object> { return {} }
async function parseItem(item: string): Promise<object> { return {} }
async function validateItem(item: object): Promise<object> { return {} }
async function saveItem(item: object): Promise<object> { return {} }

9.5.4 8.5.4 缓存策略

// LRU缓存实现
class LRUCache<K, V> {
  private capacity: number
  private cache: Map<K, V>

  constructor(capacity: number) {
    this.capacity = capacity
    this.cache = new Map()
  }

  get(key: K): V | undefined {
    const value = this.cache.get(key)
    if (value !== undefined) {
      // 移到最近使用位置
      this.cache.delete(key)
      this.cache.set(key, value)
    }
    return value
  }

  put(key: K, value: V): void {
    if (this.cache.has(key)) {
      this.cache.delete(key)
    } else if (this.cache.size >= this.capacity) {
      // 淘汰最久未使用的
      const firstKey = this.cache.keys().next().value
      if (firstKey !== undefined) {
        this.cache.delete(firstKey)
      }
    }
    this.cache.set(key, value)
  }

  get size(): number {
    return this.cache.size
  }

  clear(): void {
    this.cache.clear()
  }
}

// FIFO缓存实现
class FIFOCache<K, V> {
  private capacity: number
  private cache: Map<K, V>
  private order: K[]

  constructor(capacity: number) {
    this.capacity = capacity
    this.cache = new Map()
    this.order = []
  }

  get(key: K): V | undefined {
    return this.cache.get(key)
  }

  put(key: K, value: V): void {
    if (this.cache.has(key)) {
      this.cache.set(key, value)
      return
    }

    if (this.cache.size >= this.capacity) {
      // 淘汰最早插入的
      const oldest = this.order.shift()
      if (oldest !== undefined) {
        this.cache.delete(oldest)
      }
    }

    this.cache.set(key, value)
    this.order.push(key)
  }
}

// 使用示例
const lruCache = new LRUCache<string, UserProfile>(100)
lruCache.put('user_001', { id: 'user_001', name: '张三' } as UserProfile)
const user = lruCache.get('user_001')

interface UserProfile {
  id: string
  name: string
}

9.5.5 8.5.5 编译优化选项

HarmonyOS的ArkCompiler提供了多种编译优化选项。

// build-profile.json5 中的编译优化配置
{
  "app": {
    "products": [
      {
        "name": "release",
        "buildOption": {
          "strictMode": {
            "useNormalizedOHMURL": true
          },
          "arkOptions": {
            "runtimeOnly": {
              "sources": [],
              "packages": []
            },
            "byteCodeHar": true
          }
        },
        "compileMode": "esmodule",
        "targetSdkVersion": "5.0.0(12)",
        "compatibleSdkVersion": "5.0.0(12)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

9.6 8.6 性能分析工具

9.6.1 8.6.1 DevEco Profiler使用

DevEco Profiler是HarmonyOS官方提供的性能分析工具集,包含多个子工具用于分析不同维度的性能问题。

主要功能模块: - Time Profiler:分析CPU时间消耗,找出耗时函数 - Frame Profiler:分析帧渲染过程,定位掉帧原因 - Allocation Profiler:分析内存分配,检测内存泄漏 - Snapshot Profiler:获取内存快照,分析对象分布 - Trace Viewer:系统级Trace分析,查看多线程执行情况

// 使用HiTraceMeter进行代码级Trace打点
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'

class TraceDemo {
  async loadData(): Promise<void> {
    // 开始Trace
    hiTraceMeter.startTrace('LoadData', 0)

    try {
      // 子阶段1:网络请求
      hiTraceMeter.startTrace('NetworkRequest', 1)
      const data = await this.fetchFromNetwork()
      hiTraceMeter.finishTrace('NetworkRequest', 1)

      // 子阶段2:数据解析
      hiTraceMeter.startTrace('DataParsing', 2)
      const parsed = this.parseData(data)
      hiTraceMeter.finishTrace('DataParsing', 2)

      // 子阶段3:UI更新
      hiTraceMeter.startTrace('UIUpdate', 3)
      this.updateUI(parsed)
      hiTraceMeter.finishTrace('UIUpdate', 3)
    } finally {
      hiTraceMeter.finishTrace('LoadData', 0)
    }
  }

  async fetchFromNetwork(): Promise<object> { return {} }
  parseData(data: object): object { return {} }
  updateUI(data: object): void {}
}

9.6.2 8.6.2 Frame Profiler分析

Frame Profiler用于分析UI帧渲染性能,帮助定位掉帧和卡顿问题。

// 帧性能监控组件
@Component
struct FrameMonitorDemo {
  @State frameStats: FrameStatistics = {
    totalFrames: 0,
    jankyFrames: 0,
    avgFrameTime: 0,
    maxFrameTime: 0
  }

  aboutToAppear(): void {
    this.startMonitoring()
  }

  startMonitoring(): void {
    // 使用frameMonitor API监控帧率
    // 在DevEco Studio的Frame Profiler中可以可视化查看
  }

  build() {
    Column() {
      Text('帧性能统计')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)

      Text(`总帧数: ${this.frameStats.totalFrames}`)
      Text(`掉帧数: ${this.frameStats.jankyFrames}`)
      Text(`平均帧耗时: ${this.frameStats.avgFrameTime.toFixed(2)}ms`)
      Text(`最大帧耗时: ${this.frameStats.maxFrameTime.toFixed(2)}ms`)
      Text(`掉帧率: ${(this.frameStats.jankyFrames / Math.max(1, this.frameStats.totalFrames) * 100).toFixed(1)}%`)
    }
    .padding(16)
  }
}

interface FrameStatistics {
  totalFrames: number
  jankyFrames: number
  avgFrameTime: number
  maxFrameTime: number
}

9.6.3 8.6.3 Trace分析工具

// 使用HiLog输出结构化日志,配合Trace工具分析
import { hilog } from '@kit.PerformanceAnalysisKit'

class PerformanceLogger {
  private static domain: number = 0x0001
  private static tag: string = 'Performance'

  static logPerformanceMetric(
    metricName: string,
    value: number,
    unit: string = 'ms'
  ): void {
    hilog.info(
      PerformanceLogger.domain,
      PerformanceLogger.tag,
      `[PERF] ${metricName} = ${value}${unit}`
    )
  }

  static logComponentRenderTime(
    componentName: string,
    renderTimeMs: number
  ): void {
    hilog.info(
      PerformanceLogger.domain,
      PerformanceLogger.tag,
      `[RENDER] ${componentName}: ${renderTimeMs}ms`
    )
  }

  static logMemoryUsage(): void {
    // 获取并记录当前内存使用情况
    hilog.info(
      PerformanceLogger.domain,
      PerformanceLogger.tag,
      '[MEMORY] 记录内存使用快照'
    )
  }
}

9.6.4 8.6.4 性能指标监控

// 性能指标收集器
class PerformanceMetricsCollector {
  private metrics: Map<string, number[]> = new Map()

  // 记录指标
  record(metricName: string, value: number): void {
    if (!this.metrics.has(metricName)) {
      this.metrics.set(metricName, [])
    }
    this.metrics.get(metricName)!.push(value)
  }

  // 获取统计信息
  getStats(metricName: string): MetricStats | null {
    const values = this.metrics.get(metricName)
    if (!values || values.length === 0) return null

    const sorted = [...values].sort((a, b) => a - b)
    return {
      count: values.length,
      min: sorted[0],
      max: sorted[sorted.length - 1],
      avg: values.reduce((a, b) => a + b, 0) / values.length,
      p50: sorted[Math.floor(sorted.length * 0.5)],
      p90: sorted[Math.floor(sorted.length * 0.9)],
      p99: sorted[Math.floor(sorted.length * 0.99)]
    }
  }

  // 上报指标
  async report(): Promise<void> {
    const report: Record<string, MetricStats> = {}
    for (const [name] of this.metrics) {
      const stats = this.getStats(name)
      if (stats) {
        report[name] = stats
      }
    }
    // 上报到监控平台
    console.info(`[性能报告] ${JSON.stringify(report)}`)
  }
}

interface MetricStats {
  count: number
  min: number
  max: number
  avg: number
  p50: number
  p90: number
  p99: number
}

// 使用示例
const collector = new PerformanceMetricsCollector()

// 记录页面加载时间
const loadStart = Date.now()
// ... 加载页面
const loadTime = Date.now() - loadStart
collector.record('page_load_time', loadTime)

// 记录列表滚动帧率
collector.record('scroll_fps', 58)

9.6.5 8.6.5 性能基线建立

// 性能基线管理器
class PerformanceBaseline {
  private baseline: Record<string, BaselineMetric> = {}

  // 建立基线
  async establishBaseline(): Promise<void> {
    const metrics: Record<string, number[]> = {
      'cold_start': [],
      'page_switch': [],
      'list_scroll_fps': [],
      'api_response': [],
      'memory_usage': []
    }

    // 多次采样取平均值
    for (let i = 0; i < 5; i++) {
      metrics['cold_start'].push(await this.measureColdStart())
      metrics['page_switch'].push(await this.measurePageSwitch())
      metrics['list_scroll_fps'].push(await this.measureScrollFps())
      metrics['api_response'].push(await this.measureApiResponse())
      metrics['memory_usage'].push(await this.measureMemory())
    }

    // 计算基线值(取P90)
    for (const [name, values] of Object.entries(metrics)) {
      const sorted = values.sort((a, b) => a - b)
      this.baseline[name] = {
        p50: sorted[Math.floor(sorted.length * 0.5)],
        p90: sorted[Math.floor(sorted.length * 0.9)],
        max: sorted[sorted.length - 1]
      }
    }
  }

  // 检查是否超出基线
  checkBaseline(metricName: string, value: number): BaselineCheckResult {
    const baseline = this.baseline[metricName]
    if (!baseline) {
      return { status: 'unknown', message: '无基线数据' }
    }

    if (value > baseline.p90 * 1.5) {
      return {
        status: 'critical',
        message: `严重超标: ${value}ms (基线P90: ${baseline.p90}ms)`
      }
    }

    if (value > baseline.p90) {
      return {
        status: 'warning',
        message: `超出基线: ${value}ms (基线P90: ${baseline.p90}ms)`
      }
    }

    return {
      status: 'normal',
      message: `正常: ${value}ms (基线P90: ${baseline.p90}ms)`
    }
  }

  // 模拟测量方法
  private async measureColdStart(): Promise<number> { return 800 }
  private async measurePageSwitch(): Promise<number> { return 200 }
  private async measureScrollFps(): Promise<number> { return 58 }
  private async measureApiResponse(): Promise<number> { return 300 }
  private async measureMemory(): Promise<number> { return 150 }
}

interface BaselineMetric {
  p50: number
  p90: number
  max: number
}

interface BaselineCheckResult {
  status: 'normal' | 'warning' | 'critical' | 'unknown'
  message: string
}

9.7 本章小结

本章系统讲解了HarmonyOS应用性能优化的完整知识体系:

  1. UI渲染性能:深入理解了组件渲染机制,掌握了布局优化、按需刷新、组件复用(@Reusable)和列表性能优化(LazyForEach + 唯一key)等核心技术。

  2. 启动性能优化:理解了冷启动和热启动的区别,掌握了延迟加载、预加载、启动耗时测量等优化手段。

  3. 内存管理:了解了ArkTS内存模型,掌握了内存泄漏检测与预防、图片内存优化、大对象管理和资源释放最佳实践。

  4. 功耗优化:学习了后台任务管理、定位服务优化、网络请求优化、屏幕刷新率管理和传感器使用优化。

  5. 代码性能优化:掌握了算法复杂度优化、数据结构选择、异步任务编排、缓存策略(LRU、FIFO)等编码层面的优化技巧。

  6. 性能分析工具:熟悉了DevEco Profiler的使用,掌握了Frame Profiler分析、Trace分析、性能指标监控和性能基线建立方法。


9.8 练习题

9.8.1 一、单选题

1. 在HarmonyOS列表性能优化中,LazyForEach的key生成函数应该返回什么?

A. 列表项的索引值(index) B. 列表项的唯一且稳定的标识符 C. 随机生成的字符串 D. 当前时间戳

答案:B

解析: LazyForEach的key用于Diff算法识别列表项的身份变化。使用唯一且稳定的标识符(如数据ID)可以确保框架正确识别列表项的增删改,避免不必要的组件重建。使用index作为key在列表项发生插入或删除时会导致大量不必要的重建。


2. 以下哪种方式最适合优化应用冷启动时间?

A. 在onCreate中初始化所有第三方库 B. 将非首屏必需的初始化逻辑延迟到首屏渲染后执行 C. 增加应用进程优先级 D. 减少页面中的组件数量

答案:B

解析: 延迟加载(Lazy Initialization)是优化冷启动的核心策略。将非首屏必需的初始化逻辑(如第三方SDK、日志系统、推送服务等)延迟到首屏渲染完成后执行,可以显著缩短启动耗时。在onCreate中初始化所有库会增加启动时间。


3. @Reusable装饰器的主要作用是什么

A. 让组件支持跨页面共享 B. 标记组件可以被列表复用,减少创建和销毁开销 C. 让组件支持多线程渲染 D. 缓存组件的网络请求结果

答案:B

解析: @Reusable装饰器标记组件可以在列表中复用。当列表项滑出可视区域后再滑入时,框架不会重新创建组件,而是复用已有的组件实例并调用aboutToReuse回调更新数据,从而避免频繁的组件创建和销毁。


9.8.2 二、多选题

4. 以下哪些是常见的内存泄漏场景?

A. 未清理的定时器(setInterval) B. 未取消的事件监听 C. 闭包持有大对象引用 D. 使用const声明的局部变量 E. 未释放的PixelMap资源

答案:A、B、C、E

解析: 未清理的定时器会持续持有组件引用;未取消的事件监听会阻止组件被GC回收;闭包持有大对象引用会阻止大对象释放;未释放的PixelMap会占用Native内存。使用const声明的局部变量在函数返回后会自动释放,不会造成内存泄漏。


5. 关于HarmonyOS性能分析工具,以下哪些说法是正确的?

A. DevEco Profiler包含Time Profiler、Frame Profiler等多个子工具 B. Frame Profiler可以分析帧渲染过程中的性能瓶颈 C. hiTraceMeter可以用于代码级别的Trace打点 D. 性能基线只需要建立一次,无需持续维护 E. hilog可以输出结构化日志用于性能分析

答案:A、B、C、E

解析: DevEco Profiler是一个工具集,包含多个子分析工具;Frame Profiler分析帧渲染性能;hiTraceMeter支持代码级Trace打点;hilog输出结构化日志。性能基线需要随版本迭代持续维护和更新,因为功能变化会影响性能表现。


9.8.3 三、判断题

6. 在ArkUI中,修改@State变量会触发所有组件重新渲染。

答案:错误

解析: ArkUI框架实现了精确的依赖追踪。修改@State变量只会触发依赖了该变量的组件重新渲染,不会影响不相关的组件。这是ArkUI性能优化的基础机制。


7. 使用ForEach渲染列表时,如果key使用数组index,在列表项发生插入操作时可能导致性能问题。

答案:正确

解析: 使用index作为key时,插入操作会导致后续所有item的index发生变化,框架会认为这些item都发生了变化,从而触发大量不必要的组件重建。应使用数据本身的唯一标识作为key。


8. LRU缓存和FIFO缓存的淘汰策略相同。

答案:错误

解析: LRU(Least Recently Used)淘汰最久未使用的数据,每次访问都会更新数据的使用时间;FIFO(First In First Out)淘汰最早插入的数据,不考虑后续的访问情况。两者的淘汰策略不同,适用场景也不同。