ValueNotifier及ValueListenableBuilder源码解析
背景
之前就使用过ValueNotifier
及ValueListenableBuilder
做局部刷新,但是没有看过源码实现,最近几天看某个三方源码的时候发现继承了Listenable
,就想了解一下它到底是干嘛的,然后就发现了ValueNotifier的元类就是Listenable
,所以今天就跟大家分享一下
文章分为两个部分第一部分简单写下ValueNotifier的使用,第二部分就是我们的重头戏源码解析,了解用法的可以直接看第二趴
ValueNotifier及ValueListenableBuilder使用方法
以flutter create的模板工程为例,我们创建一个类型为ValueNotifier
的对象泛型设置为int类型,并进行初始化,初始值为0
ValueNotifier<int> count = ValueNotifier(0);
然后我们将数字显示区域的Text替换成ValueListenableBuilder
并在builder中return Text()
ValueListenableBuilder(
valueListenable: count,
builder: (context,value,child){
return Text('${count.value}');
}),
最后将_incrementCounter
中的setState修改为count.value++;
void _incrementCounter() {
count.value++;
}
这时候我们再点击就可以看到数字区域有变化了,怎么样使用起来是不是很简单,其实ValueListenableBuilder
中还有个可选参数child,child的使用方法就不介绍了,感兴趣的自己研究一下吧
源码实现介绍
先看下ValueNotifier
的源码实现再看ValueListenableBuilder
的
UML类图
Listenable
Listenable是一个抽象类,内部的方法也不多,就一个构造函数一个工厂方法加两个方法,比较简单,重点我们看下子类的实现
abstract class Listenable {
/// Abstract const constructor. This constructor enables subclasses to provide
/// 文摘const构造函数。这个构造函数允许子类提供
/// const constructors so that they can be used in const expressions.
/// Const构造函数,以便在Const表达式中使用
const Listenable();
/// Return a [Listenable] that triggers when any of the given [Listenable]s
/// themselves trigger.
/// 返回一个[Listenable],当任何给定的[Listenable]本身触发时触发。
///
/// The list must not be changed after this method has been called. Doing so
/// will lead to memory leaks or exceptions.
/// 在调用此方法后,不能更改列表。这样做将导致内存泄漏或异常。
///
/// The list may contain nulls; they are ignored.
/// 列表可以包含空值;他们将被忽略
factory Listenable.merge(List<Listenable?> listenables) = _MergingListenable;
/// Register a closure to be called when the object notifies its listeners.
/// 注册一个闭包,当对象通知它的侦听器时调用它。
void addListener(VoidCallback listener);
/// Remove a previously registered closure from the list of closures that the
/// object notifies.
/// 从对象通知的闭包列表中删除先前注册的闭包。
void removeListener(VoidCallback listener);
}
子类实现
ValueListenable
该类也是一个抽象类,只定义了一个构造函数和value的get方法
abstract class ValueListenable<T> extends Listenable {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
/// 文摘const构造函数。这个构造函数允许子类提供const构造函数,以便在const表达式中使用它们。
const ValueListenable();
/// The current value of the object. When the value changes, the callbacks
/// registered with [addListener] will be invoked.
/// 对象的当前值。当值改变时,注册到[addListener]的回调函数将被调用。
T get value;
}
ChangeNotifier
这个就是一个比较重要的类了,先来看下成员变量
属性介绍
名称 | 作用 |
---|---|
int _count | 实际监听数量 |
List _listeners | 储存回调方法 |
int _notificationCallStackDepth | 递归锁 |
int _reentrantlyRemovedListeners | 缩容使用 |
bool _debugDisposed | 标记是否已经释放(在释放后无法进行添加或删除操作,会报错) |
成员变量就上面这么多,也不复杂,接下来看看这些属性在方法中是如何使用的
hasListeners
该方法是判断是否有监听器注册,主要在重写addListener
及removeListener
时使用,官方不推荐依赖该方法进行判断行为,实现也非常简单
bool get hasListeners {
assert(_debugAssertNotDisposed());
return _count > 0;
}
addListener
添加监听事件,会将传入的方法加入_listeners
中,另外会在该方法内进行数组的扩容操作,主要通过_count与_listeners.length进行比较, 如果相等则进行扩容的操作,最后将callback方法存入数组中
void addListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
if (_count == _listeners.length) {
if (_count == 0) {
_listeners = List<VoidCallback?>.filled(1, null);
} else {
// 扩容
final List<VoidCallback?> newListeners =
List<VoidCallback?>.filled(_listeners.length * 2, null);
for (int i = 0; i < _count; i++) {
newListeners[i] = _listeners[i];
}
_listeners = newListeners;
}
}
_listeners[_count++] = listener;
}
_removeAt(int index)
该方法是一个私有方法,通过index进行删除监听的操作,其中也会进行数组的缩容操作,判断当_count
为_listeners
的一半时进行缩容操作,这里有个比较巧妙的操作,当_count
大于_listeners
的一半时,不会使用数组的remove
而是置为null
,从而达到性能优化的目的
void _removeAt(int index) {
_count -= 1;
if (_count * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners =
List<VoidCallback?>.filled(_count, null);
// Listeners before the index are at the same place.
for (int i = 0; i < index; i++) newListeners[i] = _listeners[i];
// Listeners after the index move towards the start of the list.
for (int i = index; i < _count; i++) newListeners[i] = _listeners[i + 1];
_listeners = newListeners;
} else {
// When there are more listeners than half the length of the list, we only
// shift our listeners, so that we avoid to reallocate memory for the
// whole list.
for (int i = index; i < _count; i++) _listeners[i] = _listeners[i + 1];
_listeners[_count] = null;
}
}
removeListener(VoidCallback listener)
该方法十一个对外暴露的remove方法,首先会for循环找到目标listener,然后会进行一个判断,通过_notificationCallStackDepth
判断当前是否在进行迭代,如果在迭代中那先不进行缩容处理,直接将对应位置的Listener置为null,并记录null的数量,如果没有在迭代则调用_removeAt方法
void removeListener(VoidCallback listener) {
for (int i = 0; i < _count; i++) {
final VoidCallback? _listener = _listeners[i];
if (_listener == listener) {
if (_notificationCallStackDepth > 0) {
_listeners[i] = null;
_reentrantlyRemovedListeners++;
} else {
_removeAt(i);
}
break;
}
}
}
dispose
没什么好说的直接看代码吧
void dispose() {
assert(_debugAssertNotDisposed());
assert(() {
_debugDisposed = true;
return true;
}());
}
notifyListeners
这个是个重点的方法,通知所有监听器,首先通过_notificationCallStackDepth++
进行加锁,然后通过遍历调用_listeners中的方法,当遍历完成时使用_notificationCallStackDepth--
进行解锁
然后判断_notificationCallStackDepth是否为0,主要为了判断是否结束了所有的通知,然后判断_reentrantlyRemovedListeners > 0,为了判断数组是否有占位null,当两者同时满足时真正的删除监听器, 这时计算出真正的有用监听器,当newLength为_listeners
数量一半时,创建一个数组进行缩容,否则通过排序算法将null全部排在最后面
将_reentrantlyRemovedListeners
置0,_count
赋值为newLength
void notifyListeners() {
_notificationCallStackDepth++;
final int end = _count;
for (int i = 0; i < end; i++) {
_listeners[i]?.call();
}
_notificationCallStackDepth--;
if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) {
final int newLength = _count - _reentrantlyRemovedListeners;
if (newLength * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners =
List<VoidCallback?>.filled(newLength, null);
int newIndex = 0;
for (int i = 0; i < _count; i++) {
final VoidCallback? listener = _listeners[i];
if (listener != null) {
newListeners[newIndex++] = listener;
}
}
_listeners = newListeners;
} else {
for (int i = 0; i < newLength; i += 1) {
if (_listeners[i] == null) {
int swapIndex = i + 1;
while (_listeners[swapIndex] == null) {
swapIndex += 1;
}
_listeners[i] = _listeners[swapIndex];
_listeners[swapIndex] = null;
}
}
}
_reentrantlyRemovedListeners = 0;
_count = newLength;
}
}
_MergingListenable
该类是个私有类,主要用于批量操作Listenable的,我没有找到实际应用,代码也比较简单不介绍了,上代码
class _MergingListenable extends Listenable {
_MergingListenable(this._children);
final List<Listenable?> _children;
@override
void addListener(VoidCallback listener) {
for (final Listenable? child in _children) {
child?.addListener(listener);
}
}
@override
void removeListener(VoidCallback listener) {
for (final Listenable? child in _children) {
child?.removeListener(listener);
}
}
@override
String toString() {
return 'Listenable.merge([${_children.join(", ")}])';
}
}
ValueNotifier
该类是个实现类,也是我们经常使用的类,主要继承于ChangeNotifier实现了ValueListenable,主要代码其实都在ChangeNotifier中,该类主要添加了value的get、set方法
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
ValueNotifier(this._value);
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
ValueListenableBuilder
ValueListenableBuilder主要搭配ValueNotifier使用,该类是个StatefulWidget类,
在initState中注册_valueChanged
在didUpdateWidget判断新老valueListenable
是否一致如果不一致则注销旧的方法,注册新的方法,
核心方法在_valueChanged
中,当ValueNotifier
中的value发生变化时,会调用_valueChanged方法,_valueChanged中会调用setState()进行调用build方法,build方法会会标builder方法并把参数进行回传,没错ValueListenableBuilder本质还是setState,并没有什么黑魔法
class _ValueListenableBuilderState<T> extends State<ValueListenableBuilder<T>> {
late T value;
@override
void initState() {
super.initState();
value = widget.valueListenable.value;
widget.valueListenable.addListener(_valueChanged);
}
@override
void didUpdateWidget(ValueListenableBuilder<T> oldWidget) {
if (oldWidget.valueListenable != widget.valueListenable) {
oldWidget.valueListenable.removeListener(_valueChanged);
value = widget.valueListenable.value;
widget.valueListenable.addListener(_valueChanged);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
widget.valueListenable.removeListener(_valueChanged);
super.dispose();
}
void _valueChanged() {
setState(() {
value = widget.valueListenable.value;
});
}
@override
Widget build(BuildContext context) {
return widget.builder(context, value, widget.child);
}
}
小彩蛋
关于源码中的性能优化,本来在看代码的时候,觉得ChangeNotifier实现扩容缩容是性能优化的一部分,然后就想去看看源码中List的扩容缩容的实现是什么样的,没有找到,add方法中并不包含扩容的实现,而是很简单的添加进数组中
void add(E element) {
this[this.length++] = element;
}
反而在看remove方法的时候,觉得可能主要的性能优化在这个地方,在remove对象结束的时候会将后面的对象向前移动,这就导致每次删除时都需要进行移动操作,而在ChangeNotifier的实现中,将要删除的对象置为null,一次性全部删除掉就比较方便
bool remove(Object? element) {
for (int i = 0; i < this.length; i++) {
if (this[i] == element) {
this._closeGap(i, i + 1);
return true;
}
}
return false;
}
/// Removes elements from the list starting at [start] up to but not including
/// [end]. Arguments are pre-validated.
void _closeGap(int start, int end) {
int length = this.length;
assert(0 <= start);
assert(start < end);
assert(end <= length);
int size = end - start;
for (int i = end; i < length; i++) {
this[i - size] = this[i];
}
this.length = length - size;
}
如果有知道List的扩容和缩容实现在什么地方的麻烦告知一下,感谢
转载自:https://juejin.cn/post/7088687841290485768