Riverpod Provider 自动销毁原理深度解析
核心概念
Riverpod 的自动销毁(auto-dispose)基于 引用计数 + 生命周期管理。
1. 核心类结构
// 简化的源码结构(基于 Riverpod 3.0)
/// ProviderContainer 是所有 Provider 的容器
class ProviderContainer {
/// 存储所有 Provider 的状态
final Map<ProviderBase, ProviderElementBase> _providerElements = {};
/// 读取 Provider(自动创建 element)
T read<T>(ProviderBase<T> provider) {
return _getOrCreateElement(provider).readSelf();
}
ProviderElementBase _getOrCreateElement(ProviderBase provider) {
return _providerElements.putIfAbsent(provider, () {
// 创建 element 并初始化
return provider.createElement()..mount();
});
}
}
/// ProviderElement 是每个 Provider 实例的实际状态管理者
abstract class ProviderElementBase<State> {
/// 监听者列表(谁在 watch 这个 provider)
final List<ProviderSubscription> _subscriptions = [];
/// 是否应该自动销毁
bool get autoDispose => provider.autoDispose;
/// 引用计数
int get _listenerCount => _subscriptions.length;
/// 是否已销毁
bool _disposed = false;
/// 当前状态
late State _state;
}
2. 监听机制(引用计数)
class ProviderElementBase<State> {
/// 添加监听者
ProviderSubscription<State> addListener(
Node node,
void Function(State? previous, State next) listener,
) {
final subscription = ProviderSubscription._(
this,
node,
listener,
);
_subscriptions.add(subscription);
// 🎯 关键:如果之前没有监听者,触发 onResume
if (_subscriptions.length == 1 && _onResumeCallbacks != null) {
for (final callback in _onResumeCallbacks!) {
callback();
}
}
return subscription;
}
/// 移除监听者
void removeListener(ProviderSubscription subscription) {
_subscriptions.remove(subscription);
// 🎯 关键:如果没有监听者了
if (_subscriptions.isEmpty) {
// 触发 onCancel 回调
if (_onCancelCallbacks != null) {
for (final callback in _onCancelCallbacks!) {
callback();
}
}
// 如果是 autoDispose,标记为待销毁
if (autoDispose) {
_scheduleDispose();
}
}
}
/// 延迟销毁(防止频繁创建销毁)
void _scheduleDispose() {
// 在下一个微任务中检查是否真的需要销毁
scheduleMicrotask(() {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
/// 真正的销毁
void dispose() {
if (_disposed) return;
_disposed = true;
// 🎯 触发 onDispose 回调
if (_onDisposeCallbacks != null) {
for (final callback in _onDisposeCallbacks!) {
callback();
}
}
// 清理状态
_state = null as State;
// 从容器中移除
container._providerElements.remove(provider);
}
}
3. Widget 如何触发监听
/// ConsumerWidget 的实现原理
abstract class ConsumerWidget extends Widget {
@override
ConsumerStatefulElement createElement() => ConsumerStatefulElement(this);
}
class ConsumerStatefulElement extends StatefulElement {
/// 当前 element 持有的所有 subscription
final List<ProviderSubscription> _subscriptions = [];
/// ref.watch 的实现
T watch<T>(ProviderListenable<T> provider) {
// 🎯 关键:添加监听,引用计数 +1
final subscription = provider.addListener(
this, // 传入当前 widget element
(previous, next) {
// 当 provider 状态变化时,标记需要重建
markNeedsBuild();
},
);
_subscriptions.add(subscription);
return provider.read();
}
@override
void unmount() {
// 🎯 关键:Widget 销毁时,取消所有订阅,引用计数 -1
for (final subscription in _subscriptions) {
subscription.close();
}
_subscriptions.clear();
super.unmount();
}
}
4. 完整的生命周期流程
Widget 创建
↓
ref.watch(provider)
↓
创建 ProviderElement(如果不存在)
↓
element.addListener() → 引用计数 +1
↓
如果是第一个监听者 → 触发 onInit + onResume
↓
【Provider 正常工作中】
↓
Widget 销毁(页面退出)
↓
element.removeListener() → 引用计数 -1
↓
如果引用计数 = 0 → 触发 onCancel
↓
如果 autoDispose = true → 延迟销毁
↓
检查:还有监听者吗?
├─ 有 → 取消销毁
└─ 没有 → 执行 dispose()
↓
触发 onDispose
↓
清理资源
↓
从容器中移除
5. 实战示例:源码级别的理解
示例 1:单页面使用
@riverpod // autoDispose: true(默认)
class Counter extends _$Counter {
@override
int build() {
print('🏗️ Counter build - 创建 element');
ref.onInit(() {
print('🎬 onInit - 第一个监听者到来');
});
ref.onResume(() {
print('▶️ onResume - 有新监听者');
});
ref.onCancel(() {
print('⏸️ onCancel - 所有监听者离开');
});
ref.onDispose(() {
print('💀 onDispose - element 销毁');
});
return 0;
}
void increment() => state++;
}
// 场景:进入页面 → 离开页面
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 🔥 watch 触发 addListener,引用计数 +1
final count = ref.watch(counterProvider);
return Text('$count');
}
}
// 日志输出:
// 用户进入页面:
// 🏗️ Counter build
// 🎬 onInit
// ▶️ onResume
//
// 用户离开页面:
// ⏸️ onCancel
// 💀 onDispose
示例 2:多个 Widget 使用
class Page1 extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 引用计数 +1(第1个监听者)
final count = ref.watch(counterProvider);
return Text('Page1: $count');
}
}
class Page2 extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 引用计数 +1(第2个监听者)
final count = ref.watch(counterProvider);
return Text('Page2: $count');
}
}
// 场景:
// 1. 打开 Page1 → 引用计数 = 1 → 触发 onInit + onResume
// 2. 打开 Page2 → 引用计数 = 2 → 不触发任何回调
// 3. 关闭 Page1 → 引用计数 = 1 → 不触发任何回调
// 4. 关闭 Page2 → 引用计数 = 0 → 触发 onCancel → 销毁
示例 3:keepAlive 的区别
@Riverpod(keepAlive: true) // 🔒 永不自动销毁
class GlobalConfig extends _$GlobalConfig {
@override
Config build() {
print('🏗️ GlobalConfig build');
ref.onDispose(() {
print('💀 永远不会执行(除非 app 关闭)');
});
return Config();
}
}
// 即使没有任何 Widget 监听,element 也不会销毁
// 引用计数可以变成 0,但不会触发 dispose()
6. 延迟销毁的作用(防抖)
void _scheduleDispose() {
// 🎯 为什么用 scheduleMicrotask?
// 防止以下场景:
scheduleMicrotask(() {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
// 场景:快速切换页面
// 1. 离开 PageA → 引用计数 = 0 → 调度销毁
// 2. 立即进入 PageB(同一帧内)→ 引用计数 = 1
// 3. 检查销毁条件 → 引用计数 != 0 → 取消销毁
//
// 如果没有延迟,会导致:
// 离开 PageA → 立即销毁 → 进入 PageB → 重新创建
// 性能浪费!
7. 核心数据结构
/// Provider 订阅关系
class ProviderSubscription<T> {
final ProviderElementBase<T> source; // 被监听的 provider
final Node listener; // 监听者(通常是 Widget)
final void Function(T? previous, T next) onChange;
void close() {
source.removeListener(this); // 移除监听,引用计数 -1
}
}
/// 依赖图节点
abstract class Node {
/// 这个节点依赖的所有 provider
final List<ProviderSubscription> _dependencies = [];
/// 这个节点被销毁时,自动清理所有依赖
void dispose() {
for (final dep in _dependencies) {
dep.close(); // 每个依赖的引用计数 -1
}
}
}
8. 真实源码位置
// packages/riverpod/lib/src/framework/container.dart
class ProviderContainer { ... }
// packages/riverpod/lib/src/framework/element.dart
abstract class ProviderElementBase<State> { ... }
// packages/flutter_riverpod/lib/src/consumer.dart
class ConsumerStatefulElement extends StatefulElement { ... }
总结:自动销毁的本质
-
引用计数机制
- 每个
ref.watch→ +1 - Widget dispose → -1
- 计数 = 0 且 autoDispose = true → 销毁
- 每个
-
生命周期回调
onInit: 首次创建时onResume: 0 → 1 监听者时onCancel: n → 0 监听者时onDispose: 真正销毁时
-
延迟销毁优化
- 使用微任务延迟
- 防止频繁创建销毁
- 提升性能
-
keepAlive 的本质
- 就是跳过
if (autoDispose)检查 - 引用计数仍然工作
- 只是不触发销毁
- 就是跳过
这就是 Riverpod 自动管理内存的核心机制!🎯
scheduleMicrotask 在异步代码中优先级最高,但永远晚于同步代码
和Android中的View.post类似
Riverpod 以此 来实现
延迟销毁优化
-
使用微任务延迟
-
防止频繁创建销毁
-
提升性能
// ❌ 如果用 delay(错误的做法)
void _scheduleDispose() {
Future.delayed(Duration(milliseconds: 100), () {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
// ✅ 为什么用 scheduleMicrotask(正确的做法)
void _scheduleDispose() {
scheduleMicrotask(() {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
## 2. Dart 事件循环机制
Dart 的事件循环分为两个队列:
┌─────────────────────────────────────┐
│ Dart Event Loop │
├─────────────────────────────────────┤
│ 1️⃣ Microtask Queue(微任务队列) │ ← 优先级高
│ - scheduleMicrotask │
│ - Future.microtask │
├─────────────────────────────────────┤
│ 2️⃣ Event Queue(事件队列) │ ← 优先级低
│ - Future │
│ - Timer │
│ - I/O events │
│ - User interactions │
└─────────────────────────────────────┘
执行顺序:
当前同步代码 → Microtask Queue → Event Queue → 下一帧
在同一个 Event Loop Tick 内,同步代码先执行完,再执行 Microtask
scheduleMicrotask 在异步代码中优先级最高,但永远晚于同步代码
和Android中的View.post类似
Riverpod 以此 来实现
延迟销毁优化
-
使用微任务延迟
-
防止频繁创建销毁
-
提升性能
// ❌ 如果用 delay(错误的做法)
void _scheduleDispose() {
Future.delayed(Duration(milliseconds: 100), () {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
// ✅ 为什么用 scheduleMicrotask(正确的做法)
void _scheduleDispose() {
scheduleMicrotask(() {
if (_subscriptions.isEmpty && autoDispose && !_disposed) {
dispose();
}
});
}
## 2. Dart 事件循环机制
Dart 的事件循环分为两个队列:
┌─────────────────────────────────────┐
│ Dart Event Loop │
├─────────────────────────────────────┤
│ 1️⃣ Microtask Queue(微任务队列) │ ← 优先级高
│ - scheduleMicrotask │
│ - Future.microtask │
├─────────────────────────────────────┤
│ 2️⃣ Event Queue(事件队列) │ ← 优先级低
│ - Future │
│ - Timer │
│ - I/O events │
│ - User interactions │
└─────────────────────────────────────┘
执行顺序:
当前同步代码 → Microtask Queue → Event Queue → 下一帧
在同一个 Event Loop Tick 内,同步代码先执行完,再执行 Microtask
系统不能干涉,应用层的同步代码是否超时,但是 Riverpod 的设计依托于 Widget 生命周期方法都是同步的,比如系统框架的 build 和 dispose 两个方法里面一定是同步的,而 引用计数和判断计数是否可回收,都是在同步方法里面执行的,完全能保证 Riverpod的逻辑自洽