这一节我们主要关注ProviderElement
,也是Riverpod
的核心部分。我们通过watch
观察、read
读取、listen
注册回调、invalidate
刷新都要通过它。
老样子,我会以最基础的ProviderElement
为例讲解。
1.Ref
和WidgetRef
1.1 Ref
: ProviderElementBase
implements Ref
Provider
的_createFn(Ref ref)
方法中接收的参数就是本provider
对应的providerElement
实例。ProviderElementBase
实现了Ref
接口 ,通过它我们可以读取/监听某个Provider
的值,刷新、无效化某个provider
等等。
以下是Ref
的主要方法,我会把它们的作用以注释的方式写出
dart
// Ref就是ProviderElementBase
abstract class Ref<
@Deprecated('Will be removed in 3.0') State extends Object?> {
// 本ProviderElement所属的container
ProviderContainer get container;
// 立刻重新计算某个Provider的值,等价于invalidate + read
T refresh<T>(Refreshable<T> provider);
// 令某个或一系列provider失效
void invalidate(ProviderOrFamily provider);
// 通知监听本provider的其他provider,我刷新了
void notifyListeners();
// 无效化自己
void invalidateSelf();
// ---- 生命周期相关
// 有新监听者注册
void onAddListener(void Function() cb);
// 有监听者移除
void onRemoveListener(void Function() cb);
// 从0到1,有一个监听者开始监听本provider时
void onResume(void Function() cb);
// 最后一个监听者被移除时触发(没有被废弃)
void onCancel(void Function() cb);
// 本Element被废弃
void onDispose(void Function() cb);
// ---- 读取其他provider
// 读取某个provider的值
T read<T>(ProviderListenable<T> provider);
// 判断某个ProviderElement是否被初始化过
bool exists(ProviderBase<Object?> provider);
// 观察某个provider,对方变化时自己要重新计算
T watch<T>(ProviderListenable<T> provider);
// 保活
KeepAliveLink keepAlive();
// 监听某个provider,对方变化时执行回调listener
ProviderSubscription<T> listen<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener, {
void Function(Object error, StackTrace stackTrace)? onError,
// 是否立刻执行一次回调
bool fireImmediately,
});
}
1.2 WidgetRef
: ConsumerStatefulElement
extends StatefulElement
implements WidgetRef
StatefulElement
就是BuildContext
,所以WidgetRef
实际上就是BuildContext
。
WidgetRef
方法和Ref
大同小异,区别在于Ref.watch
是刷新provider,而WidgetRef.watch
是通过markNeedsBuild
刷新UI。
2. 初始化ProviderElementBase
通常我们会用Ref
或WidgetRef
从另一个provider
中拿到数据。这里我们以WidgetRef.read(provider)
开始过一遍流程。
2.1 找到对应的_StateReader
dart
// `WidgetRef.read`
@override
T read<T>(ProviderListenable<T> provider) {
return ProviderScope.containerOf(this, listen: false).read(provider);
}
// `ProviderContainer.read`
Result read<Result>(ProviderListenable<Result> provider) {
return provider.read(this);
}
// `ProviderBase.read`, `Node`是`ProviderContainer`
StateT read(Node node) {
// `Node`是`ProviderContainer`。这里会初始化ProviderElement
final element = node.readProviderElement(this);
// 刷新Provider的值
element.flush();
element.mayNeedDispose();
return element.requireState;
}
ProviderContainer#readProviderElement
在第二篇文章也说过,就是通过_StateReader#getElement
懒加载ProviderElement
。我们再贴一遍_StateReader
中相关的代码:
dart
// 使用已有的Element或初始化
ProviderElementBase getElement() => _element ??= _create();
// 创建新的ProviderElement
ProviderElementBase _create() {
final element = override.createElement()
.._provider = override
.._origin = origin
.._container = container
..mount();
element.getState()
return element;
}
从这里开始正式进入ProviderElementBase
内部。
2.2 初始化并赋值:mount()
& buildState()
当ProviderElementBase
被初始化后,_StateReader
随即会调用其mount
方法。
dart
void mount() {
_mounted = true;
buildState();
}
dart
// ProviderElementBase#buildState()
void buildState() {
final previousDidChangeDependency = _didChangeDependency;
_didChangeDependency = false;
_didBuild = false;
try {
_mounted = true;
// 执行Provider的_createFn()
create(didChangeDependency: previousDidChangeDependency);
} catch (err, stack) {
_state = Result.error(err, stack);
} finally {
_didBuild = true;
}
}
对于create()
方法,每种Provider
对应的Element
实现均不相同。我们以ProviderElement
为例:执行provider
的_createFn
方法,把得到的值通过setState()
设置给自己,同时触发回调。
dart
@override
void create({required bool didChangeDependency}) {
final provider = this.provider as InternalProvider<State>;
// 会触发回调,见3.3
setState(provider._create(this));
}
mount()
结束后,这个ProviderElementBase
就算是正式初始化完成了。
2.3 总结
- 创建
ProviderElement
的唯一时机就是通过ProviderContainer
去使用它时,即ProviderContainer#readProviderElement
方法 ProviderElementBase
内部的初始化流程大概是这样的:mount()
->buildState()
->create()
,create()
又会执行provider
的_createFn
。- 如果某个
provider A
需要依赖其他provider B
,那么A
必然要在其_createFn(Ref)
方法中通过ref.watch/read
去监听B
,又会触发ProviderContainer#readProviderElement
...这样,provider
就被递归的初始化。
3. listen
/watch
:监听ProviderElement
ProviderElementBase
中使用列表来保存自己监听和监听自己的订阅者
dart
// 自己监听(listen)其他ProviderElement所返回的Subscription,上级
List<ProviderSubscription>? _subscriptions;
// 其他Provider监听自己的Subscription,下级
List<ProviderSubscription>? _dependents;
// 观察(watch)自己的ProviderElement
final _providerDependents = <ProviderElementBase<Object?>>[];
3.1 ref.listen
:注册回调
ref.listen
的工作原理是:把一个回调listener
添加到想要监听的ProviderElementBase
的回调列表_dependents
中。当上级ProviderElementBase
的状态(_state
)发生改变时就会触发这个回调。
WidgetRef
的watch
也是通过listen实现的,只是注册了一个markNeedsLayout()
回调。
dart
// `ProviderBase#addListener`
@override
ProviderSubscription<StateT> addListener(
Node node,
void Function(StateT? previous, StateT next) listener, {
required void Function(Object error, StackTrace stackTrace)? onError,
required void Function()? onDependencyMayHaveChanged,
required bool fireImmediately,
}) {
// node是ProviderContainer
final element = node.readProviderElement(this);
// 刷新element以获取最新值
element.flush();
// 立刻执行一次回调
if (fireImmediately) {
handleFireImmediately(
element.getState()!,
listener: listener,
onError: onError,
);
}
// 触发onAddListener/onResume回调。 todo 为什么从外部触发?
element._onListen();
// 返回一个ProviderSubscription。node是ProviderContainer,
// listenedElement是监听的ProviderElement
// 在构造方法中把自己添加到被监听ProviderElement的所属
return _ProviderStateSubscription<StateT>(
node,
listenedElement: element,
listener: (prev, next) => listener(prev as StateT?, next as StateT),
onError: onError,
);
}
只要通过Ref
或WidgetRef
监听一个provider
,都会返回一个ProviderSubscription
。通过它我们可以随时获取provider
的值或取消监听。
dart
// 在构造方法中把自己添加到被监听ProviderElement的所属
class _ProviderStateSubscription<StateT> extends ProviderSubscription<StateT> {
_ProviderStateSubscription(
super.source, {
required this.listenedElement,
required this.listener,
required this.onError,
}) {
final dependents = listenedElement._dependents ??= [];
dependents.add(this);
}
final void Function(Object? prev, Object? state) listener;
final ProviderElementBase<StateT> listenedElement;
final OnError onError;
// 获取监听的ProviderElement的值
@override
StateT read() => listenedElement.readSelf();
// 取消订阅
@override
void close() {
if (!closed) {
listenedElement._dependents?.remove(this);
listenedElement._onRemoveListener();
}
super.close();
}
}
listen
方法结束后,会返回一个ProviderSubscription
对象。可以通过它读取监听的Provider
的最新值,也可以用它取消回调。同时它还保存了我们传入的listener
回调,保存在监听的ProviderElement
中。
3.2 ref.watch
:观察另一个ProviderElement
watch
方法会将观察的provider
添加到自己的依赖中,并把自己添加到需要监听的ProviderElement
的_providerDependents
列表中,形成双向绑定的关系。
dart
@override
T watch<T>(ProviderListenable<T> listenable) {
final element = _container.readProviderElement(listenable);
// _dependencies是自己的上级
_dependencies.putIfAbsent(element, () {
final previousSub = _previousDependencies?.remove(element);
if (previousSub != null) {
return previousSub;
}
element
// 触发一次回调
.._onListen()
// 把自己添加到上级的_providerDependents列表中
.._providerDependents.add(this);
return Object();
});
// 返回对方当前的值
return element.readSelf();
}
3.3 _state
改变时,如何通知监听者
- 会依次调用
_dependents
中保存的回调,触发listen
ProviderElement
会通过让下级(即观察watch
自己的Provider
)失效(invalidateSelf
)的方式触发它们的重新计算。
3.3.1 setState
:改变状态
想改变ProviderElement
的状态(或者说它的值)就必须通过setState(Result<T> result)
方法。Result
是一个包装类,内容可能是正常值 value
或异常 error,stackTrace
。修改后会通过_notifyListeners
触发回调、通知下级。
dart
void setState(StateT newState) {
final previousResult = getState();
// 修改_state
final result = _state = ResultData(newState);
_notifyListeners(result, previousResult);
}
3.3.2 _notifyListeners
:触发回调,响应watch/listen
dart
void _notifyListeners(
Result<StateT> newState,
Result<StateT>? previousStateResult, {
bool checkUpdateShouldNotify = true,
}) {
final previousState = previousStateResult?.stateOrNull;
// 如果实际值没有变化则不通知,实现select选择性刷新的效果
if (checkUpdateShouldNotify &&
previousStateResult != null &&
previousStateResult.hasState &&
newState.hasState &&
!updateShouldNotify(
previousState as StateT,
newState.requireState,
)) {
return;
}
final listeners = _dependents?.toList(growable: false);
// listeners中存储了ProviderSubscription,其中保存了listener回调
if (listeners != null) {
for (var i = 0; i < listeners.length; i++) {
final listener = listeners[i];
if (listener is _ProviderStateSubscription) {
listener.listener(previousState,newState.state),
}
}
}
// _providerDependents中存储着下级ProviderElement
for (var i = 0; i < _providerDependents.length; i++) {
// 标记它们的依赖发生变化,实际上就是invalidateSelf()
_providerDependents[i]._markDependencyChanged();
}
}
dart
void _markDependencyChanged() {
_didChangeDependency = true;
if (_mustRecomputeState) return;
invalidateSelf();
}
4. 无效化invalidate
注意:无效化(invalidate
)并不等于废弃(dispose
)。如果某个Provider不是autoDisposed
,那么在它所属的作用域销毁之前它不会被销毁。
- 无效化:
ProviderElement
中的值_state
需要重新计算,执行ref.onDispose()
注册的回调 - 废弃:销毁这个
ProviderElement
。
4.1 ref.invalidate(ProviderOfFamily provider)
我们直接快进到ProviderContainer
的相关方法。invalidate
接收一个ProviderOrFamily
参数。
dart
void invalidate(ProviderOrFamily provider) {
if (provider is ProviderBase) {
final reader = _getOrNull(provider);
reader?._element?.invalidateSelf();
} else {
// 会无效化通过ProviderFamily创造的所有provider
provider as Family;
final familyContainer =
_overrideForFamily[provider]?.container ?? _root ?? this;
for (final stateReader in familyContainer._stateReaders.values) {
if (stateReader.origin.from != provider) continue;
stateReader._element?.invalidateSelf();
}
}
}
4.2 element.invalidateSelf()
:使自己失效
4.2.1 标记自己无效
dart
// 标记自己需要重新计算
@override
void invalidateSelf() {
if (_mustRecomputeState) return;
_mustRecomputeState = true;
// 清理回调及各种注册
runOnDispose();
// 自动销毁的Provider会重写这个方法,否则为空
mayNeedDispose();
// 本帧结束后,如果hasListener不为false,执行flush方法
// 当自己被listen或watch时,hasListener为true
_container.scheduler.scheduleProviderRefresh(this);
// 通知孩子依赖可能被更改,修改它们的标记位
visitChildren(
elementVisitor: (element) => element._markDependencyMayHaveChanged(),
notifierVisitor: (notifier) => notifier.notifyDependencyMayHaveChanged(),
);
}
4.2.2 runOnDispose
: 清理、执行一部分回调
注意_providerDependents(下级)
和_dependencies(listen回调)
列表没有被清空。hasListener
可能返回true。
为什么要清理回调和生命周期相关方法?是因为所有回调都是在Provider的_createFn
中注册的,而无效化需要重新执行_createFn
以获取到最新的值。所以提前清理、再次注册可以防止注册多次回调。
dart
void runOnDispose() {
if (!_mounted) return;
_mounted = false;
final subscriptions = _subscriptions;
if (subscriptions != null) {
while (subscriptions.isNotEmpty) {
final sub = subscriptions.first;
// close会把自己从_subscriptions列表中移除,所以不会无限循环
sub.close();
}
}
// 执行onDispose
_onDisposeListeners?.forEach(runGuarded);
// 清理生命周期方法,再次create时会重新注册
_onDisposeListeners = null;
_onCancelListeners = null;
_onResumeListeners = null;
_onAddListeners = null;
_onRemoveListeners = null;
_onChangeSelfListeners = null;
_onErrorSelfListeners = null;
_didCancelOnce = false;
}
到这里,本帧就结束了,但是ProviderElement
的状态依然没有被刷新。默认情况下,ProviderScheduler
会在下一帧统一执行ProviderElementBase
的flush
方法。
4.2.3 刷新状态_flush
、建立依赖
下一帧开始。buildState
会调用provider
的_createFn
,重新注册listener
、建立依赖关系。
dart
@internal
void flush() {
// 这句有什么用?
_maybeRebuildDependencies();
if (_mustRecomputeState) {
_mustRecomputeState = false;
_performBuild();
}
}
void _maybeRebuildDependencies() {
if (!_dependencyMayHaveChanged) return;
_dependencyMayHaveChanged = false;
visitAncestors(
(element) => element.flush(),
);
}
注意这里:
provider
自身需要重建,为什么还需要刷新自己的上级? 其实这里和刷新机制的性能优化有关,我也是想了好一会才想到的。看到这的同学可以自己想一想,答案在第5段。
buildState
(见2.2)会重新执行_createFn
,注册回调、建立依赖关系,返回最新的值
dart
void _performBuild() {
final previousDependencies = _previousDependencies = _dependencies;
_dependencies = HashMap();
final previousStateResult = _state;
buildState();
// identical比较两个对象是否为同一个对象,这里一定会返回false
if (!identical(_state, previousStateResult)) {
// 把自己添加到待刷新列表
_notifyListeners(_state!, previousStateResult);
}
// 把自己从下级的依赖列表中移除。因为会下级又会重新观察
for (final sub in previousDependencies.entries) {
sub.key
.._providerDependents.remove(this)
.._onRemoveListener();
}
_previousDependencies = null;
}
5. 举个例子...
这一小节我会举几个例子说明Riverpod
的刷新流程。看一看Riverpod
内部是如何处理刷新过程、优化性能、避免不必要的刷新的。
代表B
监听了A
,refB.watch(providerA)
我们假设每个Provider
的内容都是获取上一级Provider
的值,返回其+1
例1
Q :
StateProvider_A
变化时,B
和C
是如何变化的?
A :没有Widget
监听Provider_C
,它的ProviderElement
根本不会被创建,其_createFn
也不会执行;同理,Provider_B
也没有被监听,其Element
也未被创建。它们不会有任何变化。
要修改A,就要通过ProviderContainer.readProviderElement
获取A
对应的ProviderElement
。所以A会被创建、修改。
例2
Q :当
StateProvider_A
的值发生改变,B
C
D
是如何变化的?已知invalidateSelf
每次都只会把自己的直接后继加入到ProviderScheduler
,现在有3个Provider
,会在1帧内完成刷新还是3帧?
A :B C D
会在同一帧刷新。
当前帧:
当A
变化的那一帧,A
会通过setState() -> _notifyListeners() -> B# invalidateSelf() -> _container.scheduler.scheduleProviderRefresh(this)
,把自己的下级(这里是B
)添加到ProviderScheduler
待刷新列表中。下一帧开始刷新。
下一帧
ProviderScheduler
会执行_performRefresh
方法,在for
循环内开始执行Element B
的flush
方法。此时,_stateToRefresh
内只有Element B
,长度为1。
dart
void _performRefresh() {
for (var i = 0; i < _stateToRefresh.length; i++) {
// 此刻_stateToRefresh内只有Element B,长度为1
final element = _stateToRefresh[i];
if (element.hasListeners) element.flush();
}
}
flush
方法会刷新自己的值,再调用下级C
的invalidateSelf
dart
// B的内部
void flush() {
_performBuild();
}
void _performBuild() {
buildState();
_notifyListeners(_state!, previousStateResult);
}
void _notifyListeners(
Result<StateT> newState,
Result<StateT>? previousStateResult, {
bool checkUpdateShouldNotify = true,
}) {
for (var i = 0; i < _providerDependents.length; i++) {
// B的下级为C
_providerDependents[i]._markDependencyChanged();
}
}
以下会执行ProviderElement C
的invalidateSelf()
,将其添加到ProviderScheduer._stateToRefresh
列表中,现在列表长度为2
dart
// 执行C的`invalidateSelf`
void _markDependencyChanged() {
_didChangeDependency = true;
if (_mustRecomputeState) return;
// will notify children that their dependency may have changed
invalidateSelf();
}
void invalidateSelf() {
// C将自己添加到待刷新列表中
_container.scheduler.scheduleProviderRefresh(this);
}
此时_performRefresh
内的for
循环继续,对Element C
、Element D
重复相同的过程,最终D
执行widget注册的回调(WidgetRef.watch
是用listen
实现的)
可以看到随着for循环的执行,_stateToRefresh
需要刷新的Provider越来越多。这个断点需要打在ProviderScheduler
的_performRefresh
方法上。
例3
Q : 先修改
C
,再修改A
,刷新流程是怎样的?Provider D会刷新两次吗?
答: D
只会刷新一次,这里用到了4.3小节中提到过的_maybeRebuildDependencies()
,过程如下
当前帧:
C
会调用D
的invalidateSelf
,把D
加入待刷新列表;A
会调用B
的invalidateSelf
,把B
加入待刷新列表,同时B
标记D
的依赖可能发生改变。
dart
@override
void invalidateSelf() {
_container.scheduler.scheduleProviderRefresh(this);
// B的下级是D,通知孩子依赖可能被更改
visitChildren(
elementVisitor: (element) => element._markDependencyMayHaveChanged(),
notifierVisitor: (notifier) => notifier.notifyDependencyMayHaveChanged(),
);
}
现在_stateToRefresh
内的Element依次是 [D,B]
,D
的_dependencyMayHaveChanged == true
下一帧:
- 首先会执行
D
的flush
方法,进入_maybeRebuildDependencies()
,由于标记位为true
,所以D会尝试刷新自己的上级,即C
和B
dart
// D的flush方法
void flush() {
_maybeRebuildDependencies();
if (_mustRecomputeState) {
_mustRecomputeState = false;
_performBuild();
}
}
// 刷新自己的上级
void _maybeRebuildDependencies() {
if (!_dependencyMayHaveChanged) return;
_dependencyMayHaveChanged = false;
// D的上级是C和B,会调用它们的flush方法
visitAncestors(
(element) => element.flush(),
);
}
C
的_mustRecomputeState
为false,跳过刷新B
还未被刷新(在待刷新列表中,B排在D后面),会执行B
的flush
方法D
的_maybeRebuildDependencies
执行完,B和C都是最新状态,刷新自己for
循环走到下一步,尝试刷新B
。但是它已经被刷新过,_mustRecomputeState == false
,跳过刷新过程
这里time是当前毫秒时间戳,可以看到是同一帧内刷新,刷新顺序也是先B后D,且只刷新一次