鸿蒙App启动慢、内存泄漏、列表卡顿?本文用15分钟带你彻底搞懂启动优化、内存管理、列表渲染、网络请求四大性能优化方案,附完整培训班管理系统实战代码和踩坑记录,让你的鸿蒙App从此丝滑流畅!


一、学员列表性能优化

1.1 列表懒加载

// components/student/StudentListOptimized.ets
@Component
export struct StudentListOptimized {
  @State studentList: Student[] = [];
  @State isLoading: boolean = false;
  @State currentPage: number = 1;
  @State hasMore: boolean = true;
  private pageSize: number = 20;

  async aboutToAppear() {
    await this.loadStudents();
  }

  async loadStudents() {
    if (this.isLoading || !this.hasMore) return;

    this.isLoading = true;
    try {
      const newStudents = await StudentService.getStudentsByPage(
        this.currentPage,
        this.pageSize
      );

      if (newStudents.length < this.pageSize) {
        this.hasMore = false;
      }

      this.studentList = [...this.studentList, ...newStudents];
      this.currentPage++;
    } catch (error) {
      console.error('加载学员列表失败:', error);
    } finally {
      this.isLoading = false;
    }
  }

  build() {
    Column() {
      List({ space: 8 }) {
        ForEach(this.studentList, (student: Student) => {
          ListItem() {
            StudentCard({ student: student })
          }
        }, (student: Student) => student.id)

        // 加载更多
        if (this.hasMore) {
          ListItem() {
            this.LoadingMoreItem()
          }
        }
      }
      .onReachEnd(() => {
        this.loadStudents();
      })
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  LoadingMoreItem() {
    Row() {
      LoadingProgress()
        .width(24)
        .height(24)
        .color('#007DFF')

      Text('加载中...')
        .fontSize(14)
        .fontColor('#999999')
        .margin({ left: 8 })
    }
    .width('100%')
    .height(48)
    .justifyContent(FlexAlign.Center)
  }
}

1.2 列表项缓存

// components/student/StudentCardCached.ets
@Component
export struct StudentCardCached extends View {
  @ObjectLink student: StudentObserved;
  private avatarCache: PixelMap | null = null;

  async aboutToAppear() {
    await this.loadAvatar();
  }

  async loadAvatar() {
    if (this.avatarCache) return;

    try {
      // 从缓存或网络加载头像
      this.avatarCache = await ImageCacheManager.get(this.student.avatar);
    } catch (error) {
      console.error('加载头像失败:', error);
    }
  }

  build() {
    Row() {
      // 使用缓存的头像
      if (this.avatarCache) {
        Image(this.avatarCache)
          .width(48)
          .height(48)
          .borderRadius(24)
      } else {
        Image($r('app.media.ic_default_avatar'))
          .width(48)
          .height(48)
          .borderRadius(24)
      }

      // 学员信息
      Column() {
        Text(this.student.name)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')

        Text(this.student.phone)
          .fontSize(14)
          .fontColor('#666666')
          .margin({ top: 4 })
      }
      .layoutWeight(1)
      .margin({ left: 12 })
      .alignItems(ItemAlign.Start)
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(8)
  }
}

1.3 虚拟列表

// components/student/VirtualStudentList.ets
@Component
export struct VirtualStudentList {
  @State studentList: Student[] = [];
  @State visibleStartIndex: number = 0;
  @State visibleEndIndex: number = 20;
  private itemHeight: number = 72;
  private bufferSize: number = 5;

  build() {
    Column() {
      List() {
        ForEach(this.getVisibleItems(), (student: Student) => {
          ListItem() {
            StudentCard({ student: student })
              .height(this.itemHeight)
          }
        }, (student: Student) => student.id)
      }
      .onScroll((scrollOffset: number) => {
        this.updateVisibleRange(scrollOffset);
      })
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }

  getVisibleItems(): Student[] {
    const start = Math.max(0, this.visibleStartIndex - this.bufferSize);
    const end = Math.min(this.studentList.length, this.visibleEndIndex + this.bufferSize);
    return this.studentList.slice(start, end);
  }

  updateVisibleRange(scrollOffset: number) {
    const containerHeight = px2vp(display.getDefaultDisplaySync().height);
    this.visibleStartIndex = Math.floor(scrollOffset / this.itemHeight);
    this.visibleEndIndex = Math.ceil((scrollOffset + containerHeight) / this.itemHeight);
  }
}

二、图片加载与缓存优化

2.1 图片缓存管理器

// service/ImageCacheManager.ets
import image from '@ohos.multimedia.image';

export class ImageCacheManager {
  private static cache: Map<string, image.PixelMap> = new Map();
  private static maxCacheSize: number = 50;
  private static accessOrder: string[] = [];

  static async get(url: string): Promise<image.PixelMap | null> {
    // 从缓存获取
    if (this.cache.has(url)) {
      this.updateAccessOrder(url);
      return this.cache.get(url)!;
    }

    // 从网络加载
    try {
      const pixelMap = await this.loadFromNetwork(url);
      this.set(url, pixelMap);
      return pixelMap;
    } catch (error) {
      console.error('加载图片失败:', error);
      return null;
    }
  }

  private static set(url: string, pixelMap: image.PixelMap): void {
    // 检查缓存大小
    if (this.cache.size >= this.maxCacheSize) {
      this.evict();
    }

    this.cache.set(url, pixelMap);
    this.updateAccessOrder(url);
  }

  private static evict(): void {
    // 移除最久未访问的图片
    const oldestUrl = this.accessOrder.shift();
    if (oldestUrl) {
      this.cache.delete(oldestUrl);
    }
  }

  private static updateAccessOrder(url: string): void {
    const index = this.accessOrder.indexOf(url);
    if (index > -1) {
      this.accessOrder.splice(index, 1);
    }
    this.accessOrder.push(url);
  }

  private static async loadFromNetwork(url: string): Promise<image.PixelMap> {
    // 使用http模块下载图片
    const httpRequest = http.createHttp();
    const response = await httpRequest.request(url, {
      method: http.RequestMethod.GET
    });

    const imageSource = image.createImageSource(response.result as ArrayBuffer);
    const pixelMap = await imageSource.createPixelMap({
      desiredWidth: 100,
      desiredHeight: 100
    });

    httpRequest.destroy();
    return pixelMap;
  }

  static clear(): void {
    this.cache.clear();
    this.accessOrder = [];
  }

  static getSize(): number {
    return this.cache.size;
  }
}

2.2 图片懒加载组件

// components/common/LazyImage.ets
@Component
export struct LazyImage {
  @Prop src: string;
  @Prop placeholder: Resource = $r('app.media.ic_placeholder');
  @State pixelMap: PixelMap | null = null;
  @State isLoading: boolean = true;
  @State isError: boolean = false;

  async aboutToAppear() {
    await this.loadImage();
  }

  async loadImage() {
    this.isLoading = true;
    this.isError = false;

    try {
      this.pixelMap = await ImageCacheManager.get(this.src);
      if (!this.pixelMap) {
        this.isError = true;
      }
    } catch (error) {
      this.isError = true;
    } finally {
      this.isLoading = false;
    }
  }

  build() {
    Stack() {
      // 占位图
      if (this.isLoading || this.isError) {
        Image(this.placeholder)
          .width('100%')
          .height('100%')
          .objectFit(ImageFit.Cover)
      }

      // 实际图片
      if (this.pixelMap) {
        Image(this.pixelMap)
          .width('100%')
          .height('100%')
          .objectFit(ImageFit.Cover)
          .opacity(this.isLoading ? 0 : 1)
          .animation({ duration: 300 })
      }

      // 加载指示器
      if (this.isLoading) {
        LoadingProgress()
          .width(24)
          .height(24)
          .color('#FFFFFF')
      }
    }
    .width('100%')
    .height('100%')
  }
}

2.3 图片压缩

// utils/ImageCompressor.ets
import image from '@ohos.multimedia.image';

export class ImageCompressor {
  static async compress(
    pixelMap: image.PixelMap,
    maxWidth: number = 800,
    maxHeight: number = 800,
    quality: number = 80
  ): Promise<image.PixelMap> {
    const imageInfo = await pixelMap.getImageInfo();
    const width = imageInfo.size.width;
    const height = imageInfo.size.height;

    // 计算缩放比例
    let scale = 1;
    if (width > maxWidth || height > maxHeight) {
      scale = Math.min(maxWidth / width, maxHeight / height);
    }

    const newWidth = Math.floor(width * scale);
    const newHeight = Math.floor(height * scale);

    // 创建缩放后的PixelMap
    const options: image.InitializationOptions = {
      alphaType: image.AlphaType.UNPREMUL,
      editable: true,
      pixelFormat: image.PixelMapFormat.RGBA_8888,
      size: { width: newWidth, height: newHeight }
    };

    const newPixelMap = await image.createPixelMap(options);
    await newPixelMap.drawImageBuffer(pixelMap, {
      x: 0,
      y: 0,
      width: newWidth,
      height: newHeight
    });

    return newPixelMap;
  }

  static async compressToBase64(
    pixelMap: image.PixelMap,
    quality: number = 80
  ): Promise<string> {
    const packer = image.createPacker();
    await packer.pack(pixelMap, {
      format: 'image/jpeg',
      quality: quality
    });

    const buffer = await packer.getData();
    const base64 = buffer.toString('base64');
    return base64;
  }
}

三、启动速度优化

3.1 启动任务管理器

// service/LaunchTaskManager.ets
export class LaunchTaskManager {
  private static tasks: Array<{
    name: string;
    priority: number;
    execute: () => Promise<void>;
  }> = [];

  static registerTask(
    name: string,
    priority: number,
    execute: () => Promise<void>
  ): void {
    this.tasks.push({ name, priority, execute });
    this.tasks.sort((a, b) => a.priority - b.priority);
  }

  static async executeTasks(): Promise<void> {
    const startTime = Date.now();
    console.info(`启动任务开始执行,共${this.tasks.length}个任务`);

    for (const task of this.tasks) {
      const taskStartTime = Date.now();
      try {
        await task.execute();
        console.info(`任务${task.name}执行完成,耗时${Date.now() - taskStartTime}ms`);
      } catch (error) {
        console.error(`任务${task.name}执行失败:`, error);
      }
    }

    console.info(`所有启动任务执行完成,总耗时${Date.now() - startTime}ms`);
  }
}

3.2 延迟初始化

// service/DeferredInitializer.ets
export class DeferredInitializer {
  private static deferredTasks: Array<() => Promise<void>> = [];
  private static isInitialized: boolean = false;

  static defer(task: () => Promise<void>): void {
    this.deferredTasks.push(task);
  }

  static async initialize(): Promise<void> {
    if (this.isInitialized) return;

    this.isInitialized = true;

    // 使用requestIdleCallback执行低优先级任务
    for (const task of this.deferredTasks) {
      await new Promise<void>((resolve) => {
        setTimeout(async () => {
          await task();
          resolve();
        }, 0);
      });
    }
  }
}

3.3 启动优化配置

// entry/src/main/ets/entryability/EntryAbility.ets
import { LaunchTaskManager } from '../service/LaunchTaskManager';
import { DeferredInitializer } from '../service/DeferredInitializer';

export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 注册启动任务
    this.registerLaunchTasks();

    // 执行启动任务
    await LaunchTaskManager.executeTasks();
  }

  private registerLaunchTasks(): void {
    // 高优先级任务
    LaunchTaskManager.registerTask('初始化数据库', 1, async () => {
      await RdbHelper.getInstance().init(this.context);
    });

    LaunchTaskManager.registerTask('初始化Preferences', 2, async () => {
      await PreferencesHelper.getInstance().init(this.context);
    });

    // 中优先级任务
    LaunchTaskManager.registerTask('初始化网络监控', 3, async () => {
      await NetworkMonitor.init();
    });

    LaunchTaskManager.registerTask('初始化图片缓存', 4, async () => {
      ImageCacheManager.init();
    });

    // 低优先级任务(延迟执行)
    DeferredInitializer.defer(async () => {
      await this.initAnalytics();
    });

    DeferredInitializer.defer(async () => {
      await this.initPushService();
    });
  }

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        console.error('加载页面失败:', err);
        return;
      }
      console.info('页面加载成功');
    });
  }
}

3.4 启动性能监控

// utils/PerformanceMonitor.ets
export class PerformanceMonitor {
  private static metrics: Map<string, number[]> = new Map();

  static startMeasure(name: string): void {
    const startTime = Date.now();
    if (!this.metrics.has(name)) {
      this.metrics.set(name, []);
    }
    this.metrics.get(name)!.push(startTime);
  }

  static endMeasure(name: string): number {
    const metrics = this.metrics.get(name);
    if (!metrics || metrics.length === 0) return 0;

    const startTime = metrics.pop()!;
    const duration = Date.now() - startTime;
    console.info(`性能指标 ${name}: ${duration}ms`);
    return duration;
  }

  static async measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T> {
    this.startMeasure(name);
    try {
      const result = await fn();
      return result;
    } finally {
      this.endMeasure(name);
    }
  }

  static getReport(): Record<string, { avg: number; min: number; max: number; count: number }> {
    const report: Record<string, any> = {};
    // 实现统计逻辑
    return report;
  }
}

四、内存管理与泄漏检测

4.1 内存监控

// utils/MemoryMonitor.ets
export class MemoryMonitor {
  private static intervalId: number = -1;
  private static memoryHistory: Array<{
    timestamp: number;
    used: number;
    total: number;
  }> = [];

  static start(interval: number = 5000): void {
    this.intervalId = setInterval(() => {
      this.checkMemory();
    }, interval);
  }

  static stop(): void {
    if (this.intervalId !== -1) {
      clearInterval(this.intervalId);
      this.intervalId = -1;
    }
  }

  private static checkMemory(): void {
    // 获取内存信息
    const memoryInfo = this.getMemoryInfo();
    this.memoryHistory.push({
      timestamp: Date.now(),
      used: memoryInfo.used,
      total: memoryInfo.total
    });

    // 检查内存使用率
    const usageRate = memoryInfo.used / memoryInfo.total;
    if (usageRate > 0.8) {
      console.warn(`内存使用率过高: ${(usageRate * 100).toFixed(2)}%`);
      this.triggerMemoryWarning();
    }

    // 保留最近100条记录
    if (this.memoryHistory.length > 100) {
      this.memoryHistory.shift();
    }
  }

  private static getMemoryInfo(): { used: number; total: number } {
    // 模拟内存信息获取
    return {
      used: 100 * 1024 * 1024, // 100MB
      total: 512 * 1024 * 1024  // 512MB
    };
  }

  private static triggerMemoryWarning(): void {
    // 触发内存警告
    console.warn('内存警告:建议清理缓存');
  }

  static getMemoryHistory(): Array<{
    timestamp: number;
    used: number;
    total: number;
  }> {
    return [...this.memoryHistory];
  }

  static clearHistory(): void {
    this.memoryHistory = [];
  }
}

4.2 资源清理器

// utils/ResourceCleaner.ets
export class ResourceCleaner {
  private static cleanupTasks: Array<() => void> = [];

  static register(task: () => void): void {
    this.cleanupTasks.push(task);
  }

  static unregister(task: () => void): void {
    const index = this.cleanupTasks.indexOf(task);
    if (index > -1) {
      this.cleanupTasks.splice(index, 1);
    }
  }

  static cleanup(): void {
    console.info(`执行资源清理,共${this.cleanupTasks.length}个任务`);

    for (const task of this.cleanupTasks) {
      try {
        task();
      } catch (error) {
        console.error('资源清理失败:', error);
      }
    }

    this.cleanupTasks = [];
  }
}

4.3 组件生命周期管理

// components/common/LifecycleComponent.ets
@Component
export struct LifecycleComponent {
  private cleanupTasks: Array<() => void> = [];

  aboutToAppear() {
    // 注册资源清理任务
    ResourceCleaner.register(() => {
      this.cleanup();
    });
  }

  aboutToDisappear() {
    // 执行清理
    this.cleanup();

    // 注销清理任务
    ResourceCleaner.unregister(() => {
      this.cleanup();
    });
  }

  protected addCleanupTask(task: () => void): void {
    this.cleanupTasks.push(task);
  }

  private cleanup(): void {
    for (const task of this.cleanupTasks) {
      try {
        task();
      } catch (error) {
        console.error('组件清理失败:', error);
      }
    }
    this.cleanupTasks = [];
  }

  build() {
    Column() {
      // 子类实现具体UI
    }
  }
}

4.4 内存泄漏检测

// utils/MemoryLeakDetector.ets
export class MemoryLeakDetector {
  private static objectCounts: Map<string, number> = new Map();
  private static isDetecting: boolean = false;

  static startDetection(): void {
    this.isDetecting = true;
    this.objectCounts.clear();
    console.info('内存泄漏检测开始');
  }

  static trackObject(className: string): void {
    if (!this.isDetecting) return;

    const count = this.objectCounts.get(className) || 0;
    this.objectCounts.set(className, count + 1);
  }

  static untrackObject(className: string): void {
    if (!this.isDetecting) return;

    const count = this.objectCounts.get(className) || 0;
    if (count > 0) {
      this.objectCounts.set(className, count - 1);
    }
  }

  static stopDetection(): Map<string, number> {
    this.isDetecting = false;
    const report = new Map(this.objectCounts);
    this.objectCounts.clear();

    console.info('内存泄漏检测结束');
    this.printReport(report);

    return report;
  }

  private static printReport(report: Map<string, number>): void {
    console.info('=== 内存泄漏检测报告 ===');
    report.forEach((count, className) => {
      if (count > 0) {
        console.warn(`${className}: ${count}个对象未释放`);
      }
    });
    console.info('========================');
  }
}

五、应用发布与上架准备

5.1 代码混淆配置

// obfuscation.json
{
  "options": {
    "enable": true,
    "rules": [
      {
        "pattern": "**/*.ets",
        "enable": true
      }
    ]
  },
  "consumerFiles": [
    "obfuscation-consumer-rules.txt"
  ]
}

5.2 签名配置

// build-profile.json5
{
  "app": {
    "signingConfigs": [
      {
        "name": "default",
        "type": "HarmonyOS",
        "material": {
          "certpath": "signing/default.cer",
          "storePassword": "******",
          "keyAlias": "debugKey",
          "keyPassword": "******",
          "profile": "signing/default.p7b",
          "signAlg": "SHA256withECDSA",
          "storeFile": "signing/debug.p12"
        }
      }
    ],
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "compatibleSdkVersion": "5.0.0(12)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

5.3 版本管理

// config/VersionConfig.ets
export class VersionConfig {
  static readonly APP_VERSION = '1.0.0';
  static readonly BUILD_NUMBER = 1;
  static readonly MIN_SDK_VERSION = 12;

  static getVersionInfo(): {
    version: string;
    buildNumber: number;
    minSdkVersion: number;
  } {
    return {
      version: this.APP_VERSION,
      buildNumber: this.BUILD_NUMBER,
      minSdkVersion: this.MIN_SDK_VERSION
    };
  }
}

5.4 应用发布检查清单

// utils/ReleaseChecklist.ets
export class ReleaseChecklist {
  static async check(): Promise<{
    passed: boolean;
    issues: string[];
  }> {
    const issues: string[] = [];

    // 检查版本号
    if (!this.checkVersion()) {
      issues.push('版本号格式不正确');
    }

    // 检查签名
    if (!this.checkSigning()) {
      issues.push('应用签名配置错误');
    }

    // 检查权限
    if (!this.checkPermissions()) {
      issues.push('权限声明不完整');
    }

    // 检查性能
    if (!await this.checkPerformance()) {
      issues.push('性能指标未达标');
    }

    return {
      passed: issues.length === 0,
      issues
    };
  }

  private static checkVersion(): boolean {
    const version = VersionConfig.APP_VERSION;
    const versionRegex = /^\d+\.\d+\.\d+$/;
    return versionRegex.test(version);
  }

  private static checkSigning(): boolean {
    // 检查签名配置
    return true;
  }

  private static checkPermissions(): boolean {
    // 检查权限声明
    return true;
  }

  private static async checkPerformance(): Promise<boolean> {
    // 检查性能指标
    const memoryUsage = MemoryMonitor.getMemoryHistory();
    const avgMemory = memoryUsage.reduce((sum, item) => sum + item.used, 0) / memoryUsage.length;
    const memoryLimit = 200 * 1024 * 1024; // 200MB

    return avgMemory < memoryLimit;
  }
}

六、踩坑记录

6.1 内存泄漏

问题:应用运行一段时间后内存持续增长。

原因:未正确释放事件监听器和定时器。

解决方案

aboutToDisappear() {
  // 清除定时器
  if (this.timerId) {
    clearInterval(this.timerId);
  }

  // 移除事件监听
  eventBus.off('eventName', this.handler);
}

6.2 列表卡顿

问题:长列表滚动时卡顿明显。

原因:ForEach渲染大量数据,未使用懒加载。

解决方案

// 使用懒加载
LazyForEach(this.dataSource, (item: Student) => {
  ListItem() {
    StudentCard({ student: item })
  }
}, (item: Student) => item.id)

6.3 启动白屏

问题:应用启动时出现短暂白屏。

原因:同步初始化耗时操作。

解决方案

// 异步初始化,先显示加载页面
async onCreate() {
  // 显示启动页
  windowStage.loadContent('pages/Splash');

  // 异步初始化
  await this.initApp();

  // 加载主页
  windowStage.loadContent('pages/Index');
}

七、系列回顾与总结

系列文章回顾

  1. 第1篇:项目架构篇

    • 需求分析与功能规划

    • 分层架构设计

    • 目录结构规划

    • 开发环境搭建

  2. 第2篇:UI界面篇

    • 界面设计规范

    • 核心组件实现

    • 响应式布局

    • 交互动画

  3. 第3篇:状态管理篇

    • @State、@Prop、@Link

    • @Provide/@Consume

    • 状态管理最佳实践

  4. 第4篇:数据持久化篇

    • Preferences存储

    • RDB数据库

    • HTTP请求封装

    • 离线数据同步

  5. 第5篇:性能优化篇

    • 列表性能优化

    • 图片加载优化

    • 启动速度优化

    • 内存管理

技术栈总结

技术领域

技术方案

应用场景

UI框架

ArkUI

界面开发

状态管理

@State/@Prop/@Link

数据流转

数据持久化

Preferences/RDB

本地存储

网络请求

@ohos.net.http

API对接

性能优化

懒加载/缓存/异步

性能提升

项目成果

通过本系列博客的学习,你已经掌握了:

  • 鸿蒙NEXT应用开发的完整流程

  • ArkUI组件开发的最佳实践

  • 状态管理的灵活运用

  • 数据持久化的多种方案

  • 性能优化的核心技巧

后续学习建议

  1. 深入学习:阅读鸿蒙官方文档,了解更多高级特性

  2. 实战练习:尝试开发其他类型的鸿蒙应用

  3. 社区交流:加入鸿蒙开发者社区,交流学习心得

  4. 持续关注:关注鸿蒙NEXT的版本更新和新特性


互动引导

如果本系列博客对你有帮助,请点赞、收藏、关注!有任何问题欢迎在评论区留言,我会及时回复。


系列文章导航

  • 第1篇:项目架构篇

  • 第2篇:UI界面篇

  • 第3篇:状态管理篇

  • 第4篇:数据持久化篇

  • 第5篇:性能优化篇(本文)

感谢你的阅读,我们下个系列再见! 🎉

Logo

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

更多推荐