打造流畅的 60fps 应用体验

前言

性能优化是移动应用开发的核心课题。一个性能良好的应用应该:

  • 流畅运行:保持 60fps,无卡顿
  • 快速响应:操作响应时间 < 100ms
  • 内存稳定:无内存泄漏,内存占用合理
  • 启动迅速:冷启动 < 3 秒

CleanMark AI 作为图片/视频处理应用,对性能要求更高。这篇文章我会带你系统性地优化应用性能。


一、性能分析工具

1.1 Flutter DevTools

启动 DevTools:

# 运行应用后,在终端执行
flutter pub global activate devtools
flutter pub global run devtools

核心功能:

工具 用途
Performance 分析帧率、重建次数、GPU 线程
Memory 监控内存使用、检测内存泄漏
CPU Profiler 分析 CPU 占用、热点函数
Network 监控网络请求
Logging 查看应用日志

1.2 性能指标

// lib/core/utils/performance_monitor.dart

import 'package:flutter/scheduler.dart';
import 'package:logger/logger.dart';

/// 性能监控工具
class PerformanceMonitor {
  static final Logger _logger = Logger();
  static int _frameCount = 0;
  static DateTime? _lastTime;

  /// 开始监控帧率
  static void startMonitoring() {
    SchedulerBinding.instance.addTimingsCallback(_onFrameCallback);
  }

  /// 停止监控
  static void stopMonitoring() {
    SchedulerBinding.instance.removeTimingsCallback(_onFrameCallback);
  }

  static void _onFrameCallback(List<FrameTiming> timings) {
    _frameCount += timings.length;

    final now = DateTime.now();
    if (_lastTime != null) {
      final elapsed = now.difference(_lastTime!);
      if (elapsed.inSeconds >= 1) {
        final fps = _frameCount / elapsed.inSeconds;
        _logger.d('FPS: ${fps.toStringAsFixed(1)}');

        // 检测掉帧
        if (fps < 55) {
          _logger.w('检测到掉帧: $fps fps');
        }

        _frameCount = 0;
        _lastTime = now;
      }
    } else {
      _lastTime = now;
    }

    // 检测单帧耗时
    for (final timing in timings) {
      final buildDuration = timing.buildDuration;
      final rasterDuration = timing.rasterDuration;

      // 单帧超过 16.67ms(60fps)视为掉帧
      if (buildDuration.inMilliseconds > 16 ||
          rasterDuration.inMilliseconds > 16) {
        _logger.w(
          '单帧耗时过长 - Build: ${buildDuration.inMilliseconds}ms, '
          'Raster: ${rasterDuration.inMilliseconds}ms',
        );
      }
    }
  }
}

1.3 内存监控

// lib/core/utils/memory_monitor.dart

import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

/// 内存监控工具
class MemoryMonitor {
  static final Logger _logger = Logger();

  /// 记录当前内存快照
  static void snapshot(String tag) {
    if (kDebugMode) {
      developer.Timeline.instantSync(
        'Memory Snapshot',
        arguments: {'tag': tag},
      );
      _logger.d('内存快照: $tag');
    }
  }

  /// 强制 GC(仅用于测试)
  static void forceGC() {
    if (kDebugMode) {
      developer.Timeline.instantSync('Force GC');
      _logger.d('强制 GC');
    }
  }

  /// 监控 Widget 生命周期
  static void trackWidget(String widgetName, String event) {
    if (kDebugMode) {
      _logger.d('Widget 生命周期: $widgetName - $event');
    }
  }
}

本篇小结(第一部分)

这部分我们完成了:

  1. ✅ Flutter DevTools 介绍
  2. ✅ 性能监控工具(帧率监控)
  3. ✅ 内存监控工具(快照、GC)

下一部分我会继续讲解 Widget 性能优化。


二、Widget 性能优化

2.1 const 构造函数

原则:能用 const 就用 const

// ❌ 错误:每次 build 都创建新实例
Widget build(BuildContext context) {
  return Text('Hello');
}

// ✅ 正确:使用 const,复用实例
Widget build(BuildContext context) {
  return const Text('Hello');
}

const 优化效果:

  • 减少对象创建
  • 减少 GC 压力
  • 提升 build 性能

2.2 RepaintBoundary

隔离重绘区域:

// lib/shared/widgets/optimized_image.dart

class OptimizedImage extends StatelessWidget {
  final String imageUrl;

  const OptimizedImage({required this.imageUrl, super.key});

  
  Widget build(BuildContext context) {
    // 使用 RepaintBoundary 隔离图片重绘
    return RepaintBoundary(
      child: CachedNetworkImage(
        imageUrl: imageUrl,
        fit: BoxFit.cover,
      ),
    );
  }
}

使用场景:

  • 复杂动画
  • 频繁更新的区域
  • 独立的图片/视频

2.3 Widget 拆分

原则:单个 build 方法不超过 40 行

// ❌ 错误:build 方法过长
class UserProfilePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // 100+ 行代码...
        ],
      ),
    );
  }
}

// ✅ 正确:拆分为独立 Widget
class UserProfilePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const _AvatarSection(),
          const _InfoSection(),
          const _ActionSection(),
        ],
      ),
    );
  }
}

class _AvatarSection extends StatelessWidget {
  const _AvatarSection();

  
  Widget build(BuildContext context) {
    return Container(
      // 头像区域
    );
  }
}

2.4 避免在 build 中创建对象

// ❌ 错误:每次 build 都创建新对象
Widget build(BuildContext context) {
  final formatter = DateFormat('yyyy-MM-dd');
  return Text(formatter.format(DateTime.now()));
}

// ✅ 正确:在 initState 或外部创建
class MyWidget extends StatefulWidget {
  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late final DateFormat _formatter;

  
  void initState() {
    super.initState();
    _formatter = DateFormat('yyyy-MM-dd');
  }

  
  Widget build(BuildContext context) {
    return Text(_formatter.format(DateTime.now()));
  }
}

2.5 Key 的正确使用

// ❌ 错误:使用索引作为 key
ListView.builder(
  itemBuilder: (context, index) {
    return ListTile(
      key: ValueKey(index),  // 数据变化时会出问题
      title: Text(items[index].name),
    );
  },
);

// ✅ 正确:使用唯一标识作为 key
ListView.builder(
  itemBuilder: (context, index) {
    return ListTile(
      key: ValueKey(items[index].id),  // 使用数据的唯一 ID
      title: Text(items[index].name),
    );
  },
);

2.6 避免不必要的 setState

// ❌ 错误:setState 范围过大
class CounterWidget extends StatefulWidget {
  
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 大量不需要更新的 Widget
        const Header(),
        const Banner(),
        // 只有这里需要更新
        Text('$_counter'),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _counter++;  // 整个 Column 都会重建
            });
          },
          child: const Text('增加'),
        ),
      ],
    );
  }
}

// ✅ 正确:缩小 setState 范围
class CounterWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Header(),
        const Banner(),
        const _CounterDisplay(),  // 只有这个 Widget 会重建
      ],
    );
  }
}

class _CounterDisplay extends StatefulWidget {
  const _CounterDisplay();

  
  State<_CounterDisplay> createState() => _CounterDisplayState();
}

class _CounterDisplayState extends State<_CounterDisplay> {
  int _counter = 0;

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$_counter'),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _counter++;  // 只重建 _CounterDisplay
            });
          },
          child: const Text('增加'),
        ),
      ],
    );
  }
}

本篇小结(第二部分)

这部分我们完成了:

  1. ✅ const 构造函数优化
  2. ✅ RepaintBoundary 隔离重绘
  3. ✅ Widget 拆分策略
  4. ✅ 避免在 build 中创建对象
  5. ✅ Key 的正确使用
  6. ✅ 缩小 setState 范围

下一部分我会继续讲解列表和图片优化。


三、列表与图片优化

3.1 列表优化

使用 ListView.builder:

// ❌ 错误:一次性创建所有 Widget
ListView(
  children: items.map((item) => ListTile(title: Text(item))).toList(),
)

// ✅ 正确:按需创建 Widget
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)

性能对比:

  • ListView(children: [...]) - 一次性创建所有子 Widget,内存占用大
  • ListView.builder - 只创建可见区域的 Widget,内存占用小

3.2 分页加载

// lib/shared/widgets/paginated_list.dart

class PaginatedList<T> extends StatefulWidget {
  final Future<List<T>> Function(int page) onLoadMore;
  final Widget Function(BuildContext, T) itemBuilder;

  const PaginatedList({
    required this.onLoadMore,
    required this.itemBuilder,
    super.key,
  });

  
  State<PaginatedList<T>> createState() => _PaginatedListState<T>();
}

class _PaginatedListState<T> extends State<PaginatedList<T>> {
  final List<T> _items = [];
  final ScrollController _scrollController = ScrollController();
  int _currentPage = 1;
  bool _isLoading = false;
  bool _hasMore = true;

  
  void initState() {
    super.initState();
    _loadMore();
    _scrollController.addListener(_onScroll);
  }

  
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  void _onScroll() {
    if (_scrollController.position.pixels >=
        _scrollController.position.maxScrollExtent * 0.8) {
      _loadMore();
    }
  }

  Future<void> _loadMore() async {
    if (_isLoading || !_hasMore) return;

    setState(() {
      _isLoading = true;
    });

    try {
      final newItems = await widget.onLoadMore(_currentPage);

      setState(() {
        _items.addAll(newItems);
        _currentPage++;
        _hasMore = newItems.isNotEmpty;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
    }
  }

  
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      itemCount: _items.length + (_hasMore ? 1 : 0),
      itemBuilder: (context, index) {
        if (index == _items.length) {
          return const Center(
            child: Padding(
              padding: EdgeInsets.all(16),
              child: CircularProgressIndicator(),
            ),
          );
        }
        return widget.itemBuilder(context, _items[index]);
      },
    );
  }
}

3.3 图片优化

使用 CachedNetworkImage:

// ❌ 错误:直接使用 Image.network
Image.network(imageUrl)

// ✅ 正确:使用缓存
CachedNetworkImage(
  imageUrl: imageUrl,
  fit: BoxFit.cover,
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
  memCacheWidth: 800,  // 限制内存缓存尺寸
  maxWidthDiskCache: 1000,  // 限制磁盘缓存尺寸
)

图片压缩:

// lib/core/utils/image_optimizer.dart

import 'dart:io';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart';

class ImageOptimizer {
  /// 压缩图片
  static Future<File?> compress(
    File file, {
    int quality = 85,
    int maxWidth = 1920,
    int maxHeight = 1920,
  }) async {
    try {
      final tempDir = await getTemporaryDirectory();
      final targetPath = '${tempDir.path}/${DateTime.now().millisecondsSinceEpoch}.jpg';

      final result = await FlutterImageCompress.compressAndGetFile(
        file.absolute.path,
        targetPath,
        quality: quality,
        minWidth: maxWidth,
        minHeight: maxHeight,
      );

      return result != null ? File(result.path) : null;
    } catch (e) {
      return null;
    }
  }

  /// 获取图片尺寸
  static Future<Size> getImageSize(File file) async {
    final image = await decodeImageFromList(await file.readAsBytes());
    return Size(image.width.toDouble(), image.height.toDouble());
  }
}

3.4 缩略图生成

// lib/core/utils/thumbnail_generator.dart

import 'dart:io';
import 'package:video_thumbnail/video_thumbnail.dart';
import 'package:path_provider/path_provider.dart';

class ThumbnailGenerator {
  /// 生成视频缩略图
  static Future<File?> generateVideoThumbnail(String videoPath) async {
    try {
      final tempDir = await getTemporaryDirectory();
      final thumbnailPath = await VideoThumbnail.thumbnailFile(
        video: videoPath,
        thumbnailPath: tempDir.path,
        imageFormat: ImageFormat.JPEG,
        maxWidth: 400,
        quality: 75,
      );

      return thumbnailPath != null ? File(thumbnailPath) : null;
    } catch (e) {
      return null;
    }
  }
}

3.5 图片预加载

// lib/core/utils/image_preloader.dart

import 'package:flutter/material.dart';

class ImagePreloader {
  /// 预加载图片列表
  static Future<void> preloadImages(
    BuildContext context,
    List<String> imageUrls,
  ) async {
    final futures = imageUrls.map((url) {
      return precacheImage(NetworkImage(url), context);
    });

    await Future.wait(futures);
  }

  /// 预加载单张图片
  static Future<void> preloadImage(
    BuildContext context,
    String imageUrl,
  ) async {
    await precacheImage(NetworkImage(imageUrl), context);
  }
}

3.6 内存缓存管理

// lib/core/utils/image_cache_manager.dart

import 'package:flutter/material.dart';

class ImageCacheManager {
  /// 清除图片缓存
  static void clearCache() {
    imageCache.clear();
    imageCache.clearLiveImages();
  }

  /// 设置缓存大小
  static void configureCacheSize({
    int maxCacheSize = 100,  // 最多缓存 100 张图片
    int maxCacheBytes = 50 << 20,  // 最大 50MB
  }) {
    imageCache.maximumSize = maxCacheSize;
    imageCache.maximumSizeBytes = maxCacheBytes;
  }

  /// 获取缓存状态
  static Map<String, dynamic> getCacheStatus() {
    return {
      'currentSize': imageCache.currentSize,
      'currentSizeBytes': imageCache.currentSizeBytes,
      'liveImageCount': imageCache.liveImageCount,
      'pendingImageCount': imageCache.pendingImageCount,
    };
  }
}

本篇小结(第三部分)

这部分我们完成了:

  1. ✅ 列表优化(ListView.builder + 分页加载)
  2. ✅ 图片缓存(CachedNetworkImage)
  3. ✅ 图片压缩(flutter_image_compress)
  4. ✅ 缩略图生成(video_thumbnail)
  5. ✅ 图片预加载(precacheImage)
  6. ✅ 内存缓存管理(imageCache 配置)

下一部分我会继续讲解内存管理和泄漏检测。


四、内存管理与泄漏检测

4.1 常见内存泄漏场景

1. 未释放的 Controller

// ❌ 错误:未释放 Controller
class MyPage extends StatefulWidget {
  
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  final TextEditingController _controller = TextEditingController();

  
  Widget build(BuildContext context) {
    return TextField(controller: _controller);
  }
  // 缺少 dispose
}

// ✅ 正确:及时释放
class _MyPageState extends State<MyPage> {
  final TextEditingController _controller = TextEditingController();

  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return TextField(controller: _controller);
  }
}

2. 未取消的 Stream 订阅

// ❌ 错误:未取消订阅
class _MyPageState extends State<MyPage> {
  
  void initState() {
    super.initState();
    someStream.listen((data) {
      // 处理数据
    });
  }
}

// ✅ 正确:取消订阅
class _MyPageState extends State<MyPage> {
  StreamSubscription? _subscription;

  
  void initState() {
    super.initState();
    _subscription = someStream.listen((data) {
      // 处理数据
    });
  }

  
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }
}

3. 未取消的 Timer

// ❌ 错误:未取消 Timer
class _MyPageState extends State<MyPage> {
  
  void initState() {
    super.initState();
    Timer.periodic(const Duration(seconds: 1), (timer) {
      // 定时任务
    });
  }
}

// ✅ 正确:取消 Timer
class _MyPageState extends State<MyPage> {
  Timer? _timer;

  
  void initState() {
    super.initState();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      // 定时任务
    });
  }

  
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

4.2 资源管理工具类

// lib/core/utils/disposable_manager.dart

import 'dart:async';
import 'package:flutter/material.dart';

/// 资源管理器,统一管理需要释放的资源
class DisposableManager {
  final List<VoidCallback> _disposables = [];

  /// 添加需要释放的资源
  void add(VoidCallback disposable) {
    _disposables.add(disposable);
  }

  /// 添加 Controller
  void addController(ChangeNotifier controller) {
    _disposables.add(controller.dispose);
  }

  /// 添加 Stream 订阅
  void addSubscription(StreamSubscription subscription) {
    _disposables.add(subscription.cancel);
  }

  /// 添加 Timer
  void addTimer(Timer timer) {
    _disposables.add(timer.cancel);
  }

  /// 释放所有资源
  void dispose() {
    for (final disposable in _disposables) {
      try {
        disposable();
      } catch (e) {
        // 忽略释放错误
      }
    }
    _disposables.clear();
  }
}

/// 使用示例
class _MyPageState extends State<MyPage> {
  final DisposableManager _disposables = DisposableManager();

  
  void initState() {
    super.initState();

    // 添加 Controller
    final controller = TextEditingController();
    _disposables.addController(controller);

    // 添加订阅
    final subscription = someStream.listen((data) {});
    _disposables.addSubscription(subscription);

    // 添加 Timer
    final timer = Timer.periodic(const Duration(seconds: 1), (timer) {});
    _disposables.addTimer(timer);
  }

  
  void dispose() {
    _disposables.dispose();  // 一次性释放所有资源
    super.dispose();
  }
}

4.3 内存泄漏检测

// lib/core/utils/leak_detector.dart

import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

/// 内存泄漏检测器
class LeakDetector {
  static final Logger _logger = Logger();
  static final Map<String, int> _widgetCounts = {};

  /// 记录 Widget 创建
  static void onCreate(String widgetName) {
    if (kDebugMode) {
      _widgetCounts[widgetName] = (_widgetCounts[widgetName] ?? 0) + 1;
      _logger.d('Widget 创建: $widgetName (总数: ${_widgetCounts[widgetName]})');
    }
  }

  /// 记录 Widget 销毁
  static void onDispose(String widgetName) {
    if (kDebugMode) {
      _widgetCounts[widgetName] = (_widgetCounts[widgetName] ?? 0) - 1;
      _logger.d('Widget 销毁: $widgetName (总数: ${_widgetCounts[widgetName]})');

      // 检测泄漏
      if (_widgetCounts[widgetName]! < 0) {
        _logger.e('检测到内存泄漏: $widgetName 销毁次数超过创建次数');
      }
    }
  }

  /// 打印统计信息
  static void printStats() {
    if (kDebugMode) {
      _logger.i('Widget 实例统计:');
      _widgetCounts.forEach((name, count) {
        if (count > 0) {
          _logger.w('$name: $count 个实例未释放');
        }
      });
    }
  }
}

/// 使用示例
class MyWidget extends StatefulWidget {
  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  
  void initState() {
    super.initState();
    LeakDetector.onCreate('MyWidget');
  }

  
  void dispose() {
    LeakDetector.onDispose('MyWidget');
    super.dispose();
  }
}

4.4 弱引用管理

// lib/core/utils/weak_reference_cache.dart

import 'dart:core';

/// 弱引用缓存,避免强引用导致的内存泄漏
class WeakReferenceCache<K, V> {
  final Map<K, WeakReference<V>> _cache = {};

  /// 添加缓存
  void put(K key, V value) {
    _cache[key] = WeakReference(value);
  }

  /// 获取缓存
  V? get(K key) {
    final ref = _cache[key];
    final value = ref?.target;

    // 如果对象已被 GC,清除缓存
    if (value == null) {
      _cache.remove(key);
    }

    return value;
  }

  /// 清除缓存
  void clear() {
    _cache.clear();
  }

  /// 清除已失效的缓存
  void cleanUp() {
    _cache.removeWhere((key, ref) => ref.target == null);
  }
}

4.5 内存优化最佳实践

1. 及时释放大对象

class ImageProcessingPage extends StatefulWidget {
  
  State<ImageProcessingPage> createState() => _ImageProcessingPageState();
}

class _ImageProcessingPageState extends State<ImageProcessingPage> {
  Uint8List? _imageData;

  Future<void> _processImage() async {
    // 处理图片
    _imageData = await loadImage();
    // ... 处理逻辑
  }

  
  void dispose() {
    // 释放大对象
    _imageData = null;
    super.dispose();
  }
}

2. 使用 AutomaticKeepAliveClientMixin 保持状态

// 对于需要保持状态的 Tab 页面
class MyTabPage extends StatefulWidget {
  
  State<MyTabPage> createState() => _MyTabPageState();
}

class _MyTabPageState extends State<MyTabPage>
    with AutomaticKeepAliveClientMixin {
  
  bool get wantKeepAlive => true;

  
  Widget build(BuildContext context) {
    super.build(context);  // 必须调用
    return Container();
  }
}

3. 避免循环引用

// ❌ 错误:循环引用
class Parent {
  Child? child;
}

class Child {
  Parent parent;  // 强引用父对象
  Child(this.parent);
}

// ✅ 正确:使用弱引用
class Child {
  WeakReference<Parent> parentRef;
  Child(Parent parent) : parentRef = WeakReference(parent);

  Parent? get parent => parentRef.target;
}

本篇小结(第四部分)

这部分我们完成了:

  1. ✅ 常见内存泄漏场景(Controller、Stream、Timer)
  2. ✅ 资源管理工具类(DisposableManager)
  3. ✅ 内存泄漏检测器(LeakDetector)
  4. ✅ 弱引用缓存(WeakReferenceCache)
  5. ✅ 内存优化最佳实践

下一部分我会总结本篇的关键要点。


五、完整小结

本篇完整小结

这篇文章我们完成了:

  1. ✅ 性能分析工具(Flutter DevTools + 帧率监控)
  2. ✅ Widget 性能优化(const、RepaintBoundary、拆分、Key)
  3. ✅ 列表优化(ListView.builder + 分页加载)
  4. ✅ 图片优化(缓存、压缩、预加载)
  5. ✅ 内存管理(资源释放、泄漏检测)
  6. ✅ 最佳实践(弱引用、循环引用避免)

关键要点:

性能优化金字塔

           用户体验
          ↗         ↖
    流畅度 60fps    快速响应
      ↗                ↖
Widget 优化        列表优化
    ↗                  ↖
图片优化            内存管理
      ↘            ↙
        性能监控

Widget 优化清单

优化项 方法 效果
减少重建 const 构造函数 复用实例,减少 GC
隔离重绘 RepaintBoundary 缩小重绘范围
拆分 Widget 独立组件 精确控制更新范围
避免创建对象 提前初始化 减少 build 耗时
正确使用 Key ValueKey(id) 避免不必要的重建
缩小 setState 拆分 StatefulWidget 减少重建范围

列表优化策略

1. 选择正确的列表组件

// 数据量 < 20:ListView(children: [...])
// 数据量 > 20:ListView.builder
// 需要分组:ListView.separated
// 需要网格:GridView.builder
// 需要瀑布流:MasonryGridView

2. 分页加载

  • 滚动到 80% 时触发加载
  • 显示加载指示器
  • 处理加载失败

3. 缓存高度

// 对于固定高度的列表项,使用 itemExtent
ListView.builder(
  itemExtent: 80,  // 固定高度,提升性能
  itemBuilder: (context, index) => ...,
)

图片优化策略

1. 三级缓存

内存缓存(最快)
   ↓ 未命中
磁盘缓存(较快)
   ↓ 未命中
网络加载(最慢)

2. 尺寸控制

CachedNetworkImage(
  imageUrl: url,
  memCacheWidth: 800,      // 内存缓存宽度
  maxWidthDiskCache: 1000, // 磁盘缓存宽度
)

3. 压缩策略

  • 上传前压缩:quality: 85, maxWidth: 1920
  • 缩略图:quality: 75, maxWidth: 400
  • 头像:quality: 80, maxWidth: 200

内存管理原则

1. 及时释放资源


void dispose() {
  // 释放顺序:先释放子资源,再释放父资源
  _controller.dispose();
  _subscription?.cancel();
  _timer?.cancel();
  super.dispose();
}

2. 避免内存泄漏

  • Controller 未释放
  • Stream 订阅未取消
  • Timer 未取消
  • 循环引用
  • 静态变量持有 Widget 引用

3. 大对象管理

// 使用完立即释放
Uint8List? imageData = await loadImage();
// ... 使用 imageData
imageData = null;  // 释放引用

性能监控指标

指标 目标值 说明
帧率 ≥ 55 fps 低于 55 fps 用户可感知卡顿
单帧耗时 ≤ 16ms 超过 16ms 会掉帧
内存占用 < 200MB 根据设备性能调整
启动时间 < 3s 冷启动时间
页面切换 < 300ms 页面切换动画时间

性能优化流程

1. 性能分析
   ↓ 使用 DevTools 定位问题
2. 确定瓶颈
   ↓ 帧率、内存、CPU
3. 针对性优化
   ↓ Widget、列表、图片
4. 验证效果
   ↓ 对比优化前后数据
5. 持续监控
   ↓ 生产环境监控

HarmonyOS 适配要点

1. 内存限制更严格

  • HarmonyOS 对应用内存限制更严格
  • 及时释放大对象
  • 使用弱引用缓存

2. 图片加载优化

// HarmonyOS 推荐使用较小的缓存尺寸
if (PlatformUtils.isHarmonyOS) {
  imageCache.maximumSize = 50;  // 减少缓存数量
  imageCache.maximumSizeBytes = 30 << 20;  // 30MB
}

3. 列表性能

  • HarmonyOS 设备性能差异大
  • 使用 itemExtent 提升性能
  • 避免复杂的列表项布局

调试技巧

1. 性能叠加层

// main.dart
void main() {
  runApp(
    MaterialApp(
      showPerformanceOverlay: true,  // 显示性能叠加层
      home: MyApp(),
    ),
  );
}

2. 检查重建次数


Widget build(BuildContext context) {
  print('${widget.runtimeType} build');  // 打印重建日志
  return Container();
}

3. 内存快照对比

// 操作前
MemoryMonitor.snapshot('before_operation');

// 执行操作
await heavyOperation();

// 操作后
MemoryMonitor.snapshot('after_operation');

// 在 DevTools 中对比两个快照

思考题

  1. 为什么 const 构造函数能提升性能?(提示:对象复用)
  2. RepaintBoundary 应该放在哪里?(提示:独立更新的区域)
  3. 如何检测应用中的内存泄漏?(提示:DevTools Memory 工具)

下一篇预告:第15篇 - 测试与部署


Logo

一站式 AI 云服务平台

更多推荐