Flutter 应用生命周期:使用 AppLifecycleListener 阻止应用崩溃

作者:JarvanMo日期:2025/10/8

当我刚开始进行 Flutter 开发时,应用生命周期(App Life Cycle)感觉就像一个神秘的黑盒子。为什么我点击 Home 键时我的计时器会停止?为什么我恢复应用时相机有时会崩溃?朋友们,答案就在于应用生命周期

欢迎关注我的微信公众号:OpenFlutter,感谢。 image.png

作为一名 Flutter 开发者,掌握应用生命周期不仅仅是一个好主意——它是构建专业应用的必不可少的条件。它能让你防止应用崩溃、节省用户的电量,并保护他们宝贵的数据。

本指南将详细解析这些关键状态,并使用 AppLifecycleListener 给你一个完整、现代的示例。


为什么生命周期很重要:“Paused” 状态陷阱

忘记“hello world”吧——你需要管理的最重要的单个状态是 paused(暂停)。

1. show (启动/变得可见)

  • 类比: 你正走进一个房间。🚶
  • 简单解释: 当你的应用首次启动,或者从后台返回并实际在屏幕上绘制其像素时发生。这是用户可以再次看到你的 UI 的那一刻。

2. resume (活动且就绪)

  • 类比: 你完全清醒并正在说话。🗣️
  • 简单解释: 应用完全处于前台,可见,并主动接收触摸输入和运行代码。这是正常的、完全运行的状态。你希望你的繁重工作(如动画或网络轮询)在这里运行。

3. hide (被遮盖)

  • 类比: 有人把一块大宣传板放在你面前。🖼️
  • 简单解释: 应用正在运行,但它被其他东西完全遮挡了,比如系统警报、隐私屏幕或另一个应用叠加层。它还没有进入后台,但用户看不到它。你应该停止绘制,但保持核心功能就绪。

4. inactive (短暂暂停)

  • 类比: 电话铃响了,你冻结在句子中间。📞
  • 简单解释: 这是一个临时的、短暂的暂停。应用是可见的,但它失去了焦点,无法接收触摸。它通常发生在应用即将进入后台之前(例如,当你点击 Home 按钮时),或者当一个系统级事件(比如来电或系统权限对话框)弹出时。

5. pause (在后台)

  • 类比: 你离开了房间并关上了门。🚪
  • 简单解释: 应用完全处于后台且不再可见。它仍存在于手机内存中,但处于休眠状态。这是你的警告! 当你看到这个状态时,你必须保存用户数据停止任何耗费资源的活动(如相机或 GPS),因为手机可能随时会终止你的应用以释放资源。

6. detach (正在关闭)

  • 类比: 房子正在被推土机夷平。💥
  • 简单解释: 这是最终状态。Flutter 框架正在被销毁并从操作系统的视图中移除。应用即将被终止并从内存中清除。这是你进行任何少量日志记录或清理的最后机会。

7. restart (进程重新启动)

  • 类比: 你瞬间被传送回起点。🔄
  • 简单解释: 这个状态通常意味着整个应用程序进程被操作系统终止(要么是用户手动操作,要么是由于内存不足自动终止),而现在它正在从头开始重新启动。一切都从头开始(就像 showresume)。如果应用之前处于 pause 状态被终止,但用户随后试图将其重新唤醒,你就会看到这个状态。

典型的流程(进入主屏幕并返回)

  1. resume(活动)
  2. inactive(短暂中断)
  3. pause(在后台 — 关键的清理时间!
  4. (… 应用保持在这里,直到操作系统需要内存 …)
  5. show(返回前台)
  6. resume(再次活动)

陷阱(The Trap)

如果当应用进入 paused 状态时,你仍然让相机或大规模、活跃的网络同步运行,操作系统 (OS) 会将你的应用标记为资源占用者。然后,操作系统可以在没有警告的情况下终止你的应用以释放资源给其他应用。你将丢失用户数据,用户也会遇到崩溃。管理生命周期就是你防止这种情况发生的方式。


分步实现:AppLifecycleListener

第 1 步:设置根 Widget

此步骤定义了应用程序的入口点,并建立了基本的 Widget 树。

1void main() {
2  runApp(const MyApp());
3}
4
5class MyApp extends StatelessWidget {
6  const MyApp({super.key});
7
8  
9  Widget build(BuildContext context) {
10    return const MaterialApp(
11      home: Scaffold(body: AppLifecycleDisplay()),
12    );
13  }
14}
15

第 2 步:定义 AppLifecycleDisplay Widget

此步骤建立一个 StatefulWidget 来管理和跟踪生命周期事件,因为日志记录和 UI 更新都需要改变状态。

1class AppLifecycleDisplay extends StatefulWidget {
2  const AppLifecycleDisplay({super.key});
3
4  
5  State<AppLifecycleDisplay> createState() => _AppLifecycleDisplayState();
6}
7

第 3 步:在 initState 中设置监听器

initState 方法是实例化 AppLifecycleListener 并配置它来监听所有可能事件的地方。

1late final AppLifecycleListener _listener;
2  final ScrollController _scrollController = ScrollController();
3  final List<String> _states = <String>[];
4  late AppLifecycleState? _state;
5
6  
7  void initState() {
8    super.initState();
9    _state = SchedulerBinding.instance.lifecycleState;
10    _listener = AppLifecycleListener(
11      onShow: () => _handleTransition('show'),
12      onResume: () => _handleTransition('resume'),
13      onHide: () => _handleTransition('hide'),
14      onInactive: () => _handleTransition('inactive'),
15      onPause: () => _handleTransition('pause'),
16      onDetach: () => _handleTransition('detach'),
17      onRestart: () => _handleTransition('restart'),
18      onStateChange: _handleStateChange,
19    );
20    if (_state != null) {
21      _states.add(_state!.name);
22    }
23  }
24
25  void _handleTransition(String name) {
26    setState(() {
27      _states.add(name);
28    });
29    _scrollController.animateTo(
30      _scrollController.position.maxScrollExtent,
31      duration: const Duration(milliseconds: 200),
32      curve: Curves.easeOut,
33    );
34  }
35
36  void _handleStateChange(AppLifecycleState state) {
37    setState(() {
38      _state = state;
39    });
40  }
41
42

第 4 步:在 dispose 中清理资源

监听器会在 dispose 方法中被停止和释放,以防止内存泄漏。对于 Flutter widget 中任何可释放的资源来说,这是一个关键的最佳实践。

1
2  void dispose() {
3    _listener.dispose();
4    super.dispose();
5  }
6

第 5 步:构建用户界面

此步骤定义了视觉布局,用于显示当前状态和历史日志。

1
2  Widget build(BuildContext context) {
3    return MaterialApp(
4      debugShowCheckedModeBanner: false,
5      home: Scaffold(
6        appBar: AppBar(
7          title: const Text('App LifeCycle State'),
8          elevation: 0,
9        ),
10        body: Center(
11          child: SizedBox(
12            width: 300,
13            child: SingleChildScrollView(
14              controller: _scrollController,
15              child: Column(
16                children: <Widget>[
17                  Text('Current State: ${_state ?? 'Not initialized yet'}'),
18                  const SizedBox(height: 30),
19                  Text('State History:\n  ${_states.join('\n  ')}'),
20                ],
21              ),
22            ),
23          ),
24        ),
25      ),
26    );
27  }
28

完整代码

1
2import 'package:flutter/material.dart';
3import 'package:flutter/scheduler.dart';
4
5void main() {
6  runApp(const MyApp());
7}
8
9class MyApp extends StatelessWidget {
10  const MyApp({super.key});
11
12  
13  Widget build(BuildContext context) {
14    return const MaterialApp(
15      home: Scaffold(body: AppLifecycleDisplay()),
16    );
17  }
18}
19
20class AppLifecycleDisplay extends StatefulWidget {
21  const AppLifecycleDisplay({super.key});
22
23  
24  State<AppLifecycleDisplay> createState() => _AppLifecycleDisplayState();
25}
26
27class _AppLifecycleDisplayState extends State<AppLifecycleDisplay> {
28  late final AppLifecycleListener _listener;
29  final ScrollController _scrollController = ScrollController();
30  final List<String> _states = <String>[];
31  late AppLifecycleState? _state;
32
33  
34  void initState() {
35    super.initState();
36    _state = SchedulerBinding.instance.lifecycleState;
37    _listener = AppLifecycleListener(
38      onShow: () => _handleTransition('show'),
39      onResume: () => _handleTransition('resume'),
40      onHide: () => _handleTransition('hide'),
41      onInactive: () => _handleTransition('inactive'),
42      onPause: () => _handleTransition('pause'),
43      onDetach: () => _handleTransition('detach'),
44      onRestart: () => _handleTransition('restart'),
45      onStateChange: _handleStateChange,
46    );
47    if (_state != null) {
48      _states.add(_state!.name);
49    }
50  }
51
52  void _handleTransition(String name) {
53    setState(() {
54      _states.add(name);
55    });
56    _scrollController.animateTo(
57      _scrollController.position.maxScrollExtent,
58      duration: const Duration(milliseconds: 200),
59      curve: Curves.easeOut,
60    );
61  }
62
63  void _handleStateChange(AppLifecycleState state) {
64    setState(() {
65      _state = state;
66    });
67  }
68
69  
70  void dispose() {
71    _listener.dispose();
72    super.dispose();
73  }
74
75  
76  Widget build(BuildContext context) {
77    return MaterialApp(
78      debugShowCheckedModeBanner: false,
79      home: Scaffold(
80        appBar: AppBar(
81          title: const Text('App LifeCycle State'),
82          elevation: 0,
83        ),
84        body: Center(
85          child: SizedBox(
86            width: 300,
87            child: SingleChildScrollView(
88              controller: _scrollController,
89              child: Column(
90                children: <Widget>[
91                  Text('Current State: ${_state ?? 'Not initialized yet'}'),
92                  const SizedBox(height: 30),
93                  Text('State History:\n  ${_states.join('\n  ')}'),
94                ],
95              ),
96            ),
97          ),
98        ),
99      ),
100    );
101  }
102}
103

最后的思考:去测试一下吧!

现在来进行真正的测试:

  1. 运行你的应用。状态应该是 resumed(恢复)。
  2. 按下 Home 键:观察日志快速转换,经历 inactive(非活动)、paused(暂停),并可能经历 hidden(隐藏)。
  3. 返回应用:观察它转换回来,经历 inactive(非活动)回到 resumed(恢复)。

理解这些转换,并将你的清理代码放置在 paused 代码块内,是一个优秀的 Flutter 开发者的标志。正是这一点,将一个业余项目与一个能经受住真实用户手机严格考验的应用区分开来。

生命周期管理愉快!如果你有任何问题,请在下方留言。我很乐意随时提供帮助!


Flutter 应用生命周期:使用 AppLifecycleListener 阻止应用崩溃》 是转载文章,点击查看原文


相关推荐


Oracle云基础设施强势崛起:AI驱动的新一代云计算竞争格局
qife1222025/10/6

Beyond Stargate: Oracle OCI提升云基础设施吸引力 Oracle与前沿AI模型制造商达成了震撼市场的交易,但专家表示,该公司也加强了对云计算三巨头在企业IaaS领域的挑战。 无论企业AI最终是福音还是泡沫,IT买家现在都需要考虑Oracle云基础设施这个新的主要参与者。Oracle本月频频登上头条,包括9月9日发布的强劲云收入预测,这主要得益于来自前沿模型公司的投资,例如OpenAI和软银的Stargate合资企业,该企业于6月与Oracle签署了4.5吉瓦数据中心协议。


数据仓库和商务智能考试考点及关系梳理
轩哥Up2025/10/5

一、核心考点分类及具体内容 (一)基础定义与核心区别(高频基础考点) 核心定义 概念 核心含义 关键目标 数据仓库(Data Warehouse, DW) 面向主题、集成、稳定、时变的结构化数据存储系统,通过整合多个数据源(OLTP系统、文件、API等)的历史数据,为企业决策提供统一的数据基础 1. 数据整合:消除数据孤岛,形成企业级统一数据视图; 2. 历史存储:保留长期历史数


首屏加载优化总结
excel2025/10/4

一、什么是首屏加载 首屏时间(First Contentful Paint,FCP) :浏览器从输入网址到 首屏内容渲染完成 的时间。 不需要整个网页都渲染完,但首屏区域必须显示内容。 重要性:是衡量用户体验最关键的性能指标之一。 计算方式 监听 DOMContentLoaded document.addEventListener('DOMContentLoaded', () => { console.log('first contentful painting'); });


Android Studio | 设置国内代理(SDK 设置国内代理(阿里云镜像))
指针不南2025/10/2

Android Studio 相关下载设置国内代理(阿里云镜像)(Android Git 仓库 国内代理/阿里云镜像) 简介 Android 上的 Git 存储库。 下载地址 阿里云镜像:https://mirrors.aliyun.com/android.googlesource.com/ 相关链接 官方主页 使用方法 打开设置(settings)。进入“外观与行为”(Appearance & Behavior)。选择“系统设置”(System Settings)。点击“HTT


[创业之路-643]:互联网与移动互联网行业与通信行业的关系
文火冰糖的硅基工坊2025/10/2

互联网、移动互联网与通信行业三者之间存在着深度嵌套、相互依存、协同发展的关系。它们不是并列的平行产业,而是构成现代信息社会基础设施的“网络之网”。下面从定义、关系结构、产业链角色和技术演进角度系统解析三者之间的关系。 一、基本定义 行业定义通信行业(Telecommunications)提供信息传输通道的基础服务行业,包括语音、数据、视频等信号的物理传输和网络连接能力。核心是“通路”——让信息从A传到B。互联网(Internet)基于TCP/IP协议族构建的全球性计算机网络系统,通过通信网


头歌Educoder答案 Lucene - 全文检索入门
是犹橐籥2025/10/2

第1关:使用lucene创建索引 任务描述 本关任务:使用lucene完成索引库的创建。 相关知识 如果你还没有接触过Lucene,那么强烈建议你查看背景知识了解Lucene的基础知识和开发环境的配置,如果你已经学习过Lucene那么你可以根据下列知识尝试完成本关任务。 package step1; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import


《系统与软件工程功能规模测量IFPUG方法》(GB/T42449-2023)标准解读
2401_8653825010/1/2025

需注意的是,两类方法在功能点取值上存在差异:NESMA方法中,行标、国标及国际标准ILF默认取值为7,EIF默认取值为5,而北京地标《信息化项目软件开发费用测算规范》(DB11/T1010—2019)及之后的《中国软件行业基准数据》(CSBMK®)与大部分省市如湖南、广西、辽宁、安徽、海南、厦门等地费用标准采用ILF默认取10,EIF默认取7(对应IFPUG方法中功能复杂度为‘中’的情况),这会导致规模计算结果不同。若仅被引用且由其他应用程序识别为内部逻辑文件(ILF),归类为外部接口文件(EIF)。


ChatGPT模型降级事件深度解析:中国卖家的挑战与机遇,应对策略全指南
qmy7569/30/2025

本文深入剖析了ChatGPT模型降级事件对中国卖家的影响,提出了加强自主研发、多元化AI服务布局、深化用户沟通等应对策略,助力中国卖家在这场科技变革中抓住机遇。


基于Hadoop的车辆二氧化碳排放量分析与可视化系统|基于Spark的车辆排放量实时监控与预测系统|基于数据挖掘的汽车排放源识别与减排策略系统
计算机源码社2025/10/9

💕💕作者:计算机源码社 💕💕个人简介:本人八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Spark、hadoop、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流! 💕💕学习资料、程序开发、技术解答、文档报告 💕💕如需要源码,可以扫取文章下方二维码联系咨询 💕💕Java项目 💕💕微信小程序项目 💕💕Android项目 💕💕Python项目 💕💕PHP项目 💕💕ASP.NET项目 💕�


从0到1微调DeepSeek大模型,LoRA+4位量化让24GB显卡也能玩转
陈敬雷-充电了么-CEO兼CTO2025/10/10

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》(跟我一起学人工智能)【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷】 文章目录 GPT多模态大模型与AI Agent智能体系列二百一十六从0到1微调DeepSeek大模型,LoRA+4位量化让24GB显卡也能玩转一、为什么要微调DeepSeek?从“通才”到“专家”的蜕变二、微调核心原理:从损失函数到数据策略

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0