Riverpod源码分析2:作用域 ProviderScope

1.概述

ProviderScope可以理解成Provider的作用域,其内部会提供一个ProviderContainer负责维护ProviderElement

  1. 对于全局的provider,其状态会被保存在顶层ProviderScope
  2. 对于通过ProviderScope覆写(override)的provider,其状态会被保存在当前的作用域中
  3. 对于依赖了多个其他Providerprovider,它会和自身依赖所处层级最深的provider对象处在相同的作用域中

collectionIdProvider就是一个被覆写的provider,它的状态会保存在当前ProviderScope中,而不是顶层。

ProviderScopeProviderContainer整体较为简单,它们只负责管理ProviderElement,真正监听、刷新的逻辑都在ProviderElementBase里。

2. ProviderContainer

2.0 ProviderScope:向下提供ProviderContainer

ProviderScope是一个StatefulWidget。它会创建一个新的ProviderContainer、获取上级的ProviderContainer、并将它作为新Containerparent参数。

build方法会创建一个InheritedWidget:UncontrolledProviderScope,以向下传递ProviderContainer

2.1 _StateReader:ProviderElement的容器

ProviderContainer不会直接保存ProviderElement,而是保存_StateReader对象。同时_StateReader也负责懒加载ProviderElement

_StateReader中会保存

  1. 用作key的原始的provider对象
  2. 实际生效的override对象,是一个Provider
  3. ProviderElement所属的ProviderContainer对象
  4. 是否是动态创建(或者说非override)的标记位isDynamicallyCreated
dart 复制代码
class _StateReader {
  _StateReader({
    required this.origin,
    required this.override,
    required this.container,
    // 是否为动态创建
    required this.isDynamicallyCreated,
  });

  final ProviderBase<Object?> origin;
  ProviderBase<Object?> override;
  final ProviderContainer container;
  final bool isDynamicallyCreated;

  ProviderElementBase? _element;

  // element是懒加载的
  ProviderElementBase getElement() => _element ??= _create();

  // 初始化ProviderElement的地方
  ProviderElementBase _create() {
    try {
      final element = override.createElement()
        .._provider = override
        .._origin = origin
        .._container = container
        ..mount();
      return element;
    } finally {}
  }
}

2.2 ProviderContainer的创建过程

ProviderContainer是在ProviderScope中被创建的,ProviderScope又会把 上级parent、本级的overrides传递给所属的ProviderContainer。这里我们看看ProviderContainer是怎么处理这些参数的。

dart 复制代码
ProviderContainer({
  ProviderContainer? parent,
  List<Override> overrides = const [],
  List<ProviderObserver>? observers,
})  : depth = parent == null ? 0 : parent.depth + 1,
      _parent = parent,
      // 借用parent中非动态创建的_stateReader
      _stateReaders = {
        if (parent != null)
          for (final entry in parent._stateReaders.entries)
            if (entry.value.isDynamicallyCreated == false) entry.key: entry.value,
      },
      _root = parent?._root ?? parent {
  if (parent != null) {
    parent._children.add(this);
    _overrideForFamily.addAll(parent._overrideForFamily);
  }

  for (final override in overrides) {
    if (override is ProviderOverride) {
      // 保存被覆写的provider
      _overrideForProvider[override._origin] = override._override;
      // 提前创建被覆写的Provider所对应的_StateReader
      _stateReaders[override._origin] = _StateReader(
        origin: override._origin,
        override: override._override,
        container: this,
        isDynamicallyCreated: false,
      );
    } else if (override is FamilyOverride) {
      _overrideForFamily[override.overriddenFamily] = _FamilyOverrideRef(
        override,
        this,
      );
    }
  }
}

根据这段代码,我们可以知道"作用域"的原理:

  1. 根据 自己overrides参数,提前创建并保存对应的_StateReader。这些被提前创建的_StateReader又被称为"非动态创建",isDynamicallyCreated == false
  2. 借用 上级 非动态创建的_StateReader。这里"借用"的意思是_StateReader仍属于创建它的ProviderContainer;它的container参数仍是上级而不是本级;当本ProviderContainer销毁时借来的_StateReader不受影响

3. 获取ProviderElement_putIfAbsent()

之前我们提到ProviderElementBase被保存在_StateReader中。因此,获取ProviderElementBase首先就要获取其对应的_StateReader。考虑到ProviderScope可能存在嵌套关系,不同类型的Provider_StateReader中的存储和获取方式会有所不同:

  1. 对于未覆写、无依赖的Provider,其对应的ProviderElement应该存放在顶层ProviderContainer
  2. 对于被覆写的Provider,其Element应该在当前层。
  3. 对于没有被覆写但依赖了被覆写的Provider,其Element应该存放在它所依赖被覆写的Provider的最深层级。

第3条看起来有点绕,举个例子就明白了。collectionInfo依赖于collectionId,而collectionIdProviderScope中被覆写,此时collectionInfoProvider就是一个自身没有被覆写但依赖了被覆写的ProviderprovidercollectionInfoProvidercollectionIdProvider所对应的ProviderElement在同一层。

_StateReaderProviderContainer#_putIfAbsent创建,我会把步骤注释在代码中。

3.0 对于已保存的_StateReader

对于本级已经保存的对应的_StateReader,直接取出并返回,否则进入getReader()方法。

dart 复制代码
_StateReader _putIfAbsent(ProviderBase<Object?> provider) {
  // 已保存在本层,直接返回
  final currentReader = _stateReaders[provider];
  if (currentReader != null) return currentReader;

  _StateReader getReader() {
    // 3.1 .....
    // 3.2 .....
  }
  // 取出并缓存获取到的_StateReader,这样下一次就不用再跑麻烦的getReader方法了
  return _stateReaders[provider] = getReader();

3.1 & 3.2 小节都是getReader()方法的一部分

3.1 对于 ProviderFamily

dart 复制代码
if (provider.from != null) {
  // reading a family

  final familyOverrideRef = _overrideForFamily[provider.from];
  if (familyOverrideRef != null) {
    // A family was overridden, so we implicitly mount the readers

    if (familyOverrideRef.container._stateReaders.containsKey(provider)) {
      return familyOverrideRef.container._stateReaders[provider]!;
    }

    void setupOverride({
      required ProviderBase<Object?> origin,
      required ProviderBase<Object?> override,
    }) {
      assert(
        origin == override || override.dependencies == null,
        'A provider override cannot specify `dependencies`',
      );

      // setupOverride may be called multiple times on different providers
      // of the same family (provider vs provider.modifier), so we use ??=
      // to initialize the providers only once
      familyOverrideRef.container._stateReaders[origin] ??= _StateReader(
        origin: origin,
        override: override,
        container: familyOverrideRef.container,
        isDynamicallyCreated: true,
      );
    }

    final providerOverride =
        familyOverrideRef.override.getProviderOverride(provider);

    setupOverride(origin: provider, override: providerOverride);

    // if setupOverride overrode the provider, it was already initialized
    // in the code above. Otherwise we initialize it as if it was not overridden
    return familyOverrideRef.container._stateReaders[provider] ??
        _StateReader(
          origin: provider,
          override: provider,
          container: familyOverrideRef.container,
          isDynamicallyCreated: true,
        );
  }
}

3.2 对于普通的 Provider

dart 复制代码
  final root = _root;
  if (root != null) {
    // Provider可能依赖了其他的Provider,找出依赖
    final dependencies = provider.from?.allTransitiveDependencies ??
        provider.allTransitiveDependencies;
    
    // 找到它们所在的ProviderContainer
    final containerForDependencyOverride = dependencies
        ?.map((dep) {
          final reader = _stateReaders[dep];
          if (reader != null) {
            return reader.container;
          }
          final familyOverride = _overrideForFamily[dep];
          return familyOverride?.container;
        })
        .where((container) => container != null)
        .toList();

    // 如果有,找出层级最深的那个container
    if (containerForDependencyOverride != null &&
        containerForDependencyOverride.isNotEmpty) {
        
      final deepestOverrideContainer = containerForDependencyOverride
          .fold<ProviderContainer>(root, (previous, container) {
        if (container!.depth > previous.depth) {
          return container;
        }
        return previous;
      });
      
      // 新创建的ProviderElementBase应该存放到依赖层级最深的ProviderContainer中,放大作用域
      return deepestOverrideContainer._stateReaders.putIfAbsent(provider,
          () {
        return _StateReader(
          origin: provider,
          override: provider,
          container: deepestOverrideContainer,
          isDynamicallyCreated: true,
        );
      });
    }
  } // end if (root != null)
  // 到这里,如果这个provider显式依赖了其他的provider,那么它对应的_StateReader已经被创建并返回了

  // 否则,这个provider没有依赖任何其他的provider。在根container中寻找
  if (_root?._stateReaders.containsKey(provider) ?? false) {
    return _root!._stateReaders[provider]!;
  }

  // 最顶层ProviderContainer中也没有这个provider对应的element。创建并将它放到最顶层中
  final reader = _StateReader(
    origin: provider,
    // If a provider did not have an associated StateReader then it is
    // guaranteed to not be overridden
    override: provider,
    // 当自己为根节点时,_root才为空
    container: _root ?? this,
    isDynamicallyCreated: true,
  );

  if (_root != null) {
    _root!._stateReaders[provider] = reader;
  }

  return reader;
}

以上面举过的例子它为例,collectionId对应的_StateReader会属于图2的ProviderScope(而不是最顶层)中,而collectionInfo由于依赖了id,它对应的ProviderElement也在相同的ProviderContainer中。

图1里有一个appRepoProvider,它是全局的且不依赖其他provider,所以不管在哪里使用它,他都属于顶层ProviderContainer

4. 读取read/监听watch/无效化invalidate

这其实是ProviderElement中的内容,但是它们都用到了ProviderContainer中的readProviderElement方法。这个方法通过_putIfAbsent找到当前层级的ProviderElement,后续操作交由Element完成。

dart 复制代码
  @override
  ProviderElementBase<State> readProviderElement<State>(ProviderBase<State> provider) {
    final reader = _putIfAbsent(provider);
    return reader.getElement() as ProviderElementBase<State>;
  }

5. 释放当前ProviderContainer

获取所有属于本containerProviderElement并释放它们。还记得开头提到"借用"的概念吗?被借用的_StateReader所有权仍属于它们原本的ProviderContainer,释放时会跳过他们。

dart 复制代码
void dispose() {
  if (_disposed) return;

  _disposed = true;
  // 从上级ProviderContainer的孩子列表中移除自己
  _parent?._children.remove(this);
  // 如果是根节点,负责停止调度器
  if (_root == null) scheduler.dispose();

  // 释放属于自己的ProviderElement
  for (final element in getAllProviderElementsInOrder().toList().reversed) {
    element.dispose();
  }
}

// 获取属于自己的ProviderElement
Iterable<ProviderElementBase> getAllProviderElements() sync* {
  for (final reader in _stateReaders.values) {
    // 跳过借用的_StateReader
    if (reader._element != null && reader.container == this) {
      yield reader._element!;
    }
  }
}

6. 调度器ProviderScheduler

和FlutterElement中的BuildOwner类似,ProviderContainer使用调度器来集中刷新ProviderElement

6.1 内部变量

ProviderScheduler中维护了两个列表,分别记录了需要刷新和销毁的ProviderElement

dart 复制代码
// 需要销毁
final _stateToDispose = <AutoDisposeProviderElementMixin<Object?>>[];
// 需要刷新
final _stateToRefresh = <ProviderElementBase>[];

当某个ProviderElement需要被刷新时,就会通过

dart 复制代码
void scheduleProviderRefresh(ProviderElementBase element) {
  _stateToRefresh.add(element);
  _scheduleTask();
}

把自己加入到待刷新列表中,同时尝试调度一次任务。释放dispose同理。

注意:在_task执行时scheduleProviderRefresh也可能再次被调用。

6.2 任务_task、调度_scheduleTask和分帧vsync

6.2.1 任务_task

_task主要内容就是执行以下两个方法:按顺序刷新/销毁ProviderElement

依次刷新。如果某个Provider没有监听者,跳过刷新。

dart 复制代码
void _performRefresh() {
  for (var i = 0; i < _stateToRefresh.length; i++) {
    final element = _stateToRefresh[i];
    if (element.hasListeners) element.flush();
  }
}

依次销毁。如果某个ProviderElement被保活/仍有监听者,跳过销毁。

dart 复制代码
void _performDispose() {
  for (var i = 0; i < _stateToDispose.length; i++) {
    final element = _stateToDispose[i];

    final links = element._keepAliveLinks;

    // 被保活/有监听者,跳过销毁
    if (element.maintainState ||
        (links != null && links.isNotEmpty) ||
        element.hasListeners ||
        element._container._disposed) {
      continue;
    }
    element._container._disposeProvider(element._origin);
  }
}

6.2.2 调度_scheduleTask

_pendingTaskCompleter可以理解成一个标记位,不为空表示已经有任务,跳过调度

dart 复制代码
void _scheduleTask() {
  if (_pendingTaskCompleter != null || _disposed) return;
  _pendingTaskCompleter = Completer<void>();
  vsync(_task);
}

6.2.3 vsync

vsync是一个参数为方法的方法。执行时会把接受的方法包装到Future中,而Future会在下一帧开始(SchedulerPhase.idle)时调用。

dart 复制代码
void _defaultVsync(void Function() task) {
  Future(task);
}

void Function(void Function()) get vsync {
  return _defaultVsync;
}
相关推荐
小墙程序员9 小时前
Flutter 教程(六)路由管理
flutter
zacksleo14 小时前
鸿蒙Flutter开发故事:不,你不需要鸿蒙化
flutter·harmonyos
帅次15 小时前
Flutter DropdownButton 详解
android·flutter·ios·kotlin·gradle·webview
bst@微胖子17 小时前
Flutter项目之构建打包分析
flutter
江上清风山间明月18 小时前
Flutter开发There are multiple heroes that share the same tag within a subtree报错
android·javascript·flutter·动画·hero
无知的前端18 小时前
Flutter 性能优化:实战指南
flutter·性能优化
飞川00119 小时前
Flutter敏感词过滤实战:基于AC自动机的高效解决方案
android·flutter
小墙程序员19 小时前
Flutter 教程(五)事件处理
flutter
耳東陈20 小时前
Flutter开箱即用一站式解决方案
flutter
SoaringHeart20 小时前
Flutter进阶:日志信息的快速定位解决方案 DLog
前端·flutter