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;
}
相关推荐
liulian09165 小时前
Flutter for OpenHarmony 跨平台开发:单位转换功能实战指南
flutter
千码君20166 小时前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
maaath7 小时前
【maaath】Flutter for OpenHarmony 手表配饰应用实战开发
flutter·华为·harmonyos
maaath8 小时前
【maaath】Flutter for OpenHarmony 跨平台计算器应用开发实践
flutter·华为·harmonyos
maaath13 小时前
【maaath】Flutter for OpenHarmony 闹钟时钟应用开发实战
flutter·华为·harmonyos
maaath13 小时前
【maaath】Flutter for OpenHarmony 短信管理应用实战
flutter·华为·harmonyos
maaath14 小时前
【maaath】Flutter for OpenHarmony打造跨平台便签备忘录应用
flutter·华为·harmonyos
千码君201614 小时前
flutter:与Android Studio模拟器的调试分享
android·flutter
xmdy586615 小时前
Flutter+开源鸿蒙实战|智联邻里Day8 Lottie动画集成+url_launcher跳转拨号+个人中心完善+全局UI统一
flutter·开源·harmonyos
liulian09161 天前
Flutter for OpenHarmony 跨平台开发:颜色选择器功能实战指南
flutter