Riverpod源码分析(二)

79 篇文章 0 订阅
45 篇文章 0 订阅

前沿

上一篇《Riverpod源码分析(一)》我们已经对 Riverpod 做了一个比较全面的介绍,今天我们就开始分析 Riverpod 的源码实现和状态更新执行过程。

Riverpod源码分析

在分析源码之前,先确认下Gihub 上 clone 的版本。我这里使用的是 v2.1.3 最新版本:

riverpod: ^2.1.3
flutter_riverpod: ^2.1.3
hooks_riverpod: ^2.1.3
一、项目结构

可以看到 Riverpod 的主体代码在 packages 中,其中 packages 目录包含:flutter_riverpod、hooks_riverpod、riverpod、riverpod_annotation、riverpod_cli、riverpod_generator、riverpod_graph、riverpod_lint 和 riverpod_lint_flutter_test。下面我们依次来进行介绍:

  1. flutter_riverpod

可以看到该目录建构还是比较简单的,主要定义了 ProviderScope 和 Consumer 这样提供状态管理的 Widget,还定义了 ChangeNotifierProvider 的相关 builders调用

  1. hooks_riverpod

可以看到 hook_riverpod 只有一个 consumer.dart 文件有代码,主要定义了 HookConsumerWidgetHookConsumer 两个既可以使用钩子又可以侦听 providersWidget。其中,HookConsumer 继承 HookConsumerWidget 提供 ConsumerBuilder 回调支持。

  1. riverpod

riverpod 目录是 Riverpod 插件的最核心的目录,其定义的类特别多,这里先不做过多介绍,只需要记住其是实现状态管理的核心 package。

  1. riverpod_annotation

riverpod_annotation 目录,看其名字我们就可以知道是 Riverpod 提供注解的 package。其主要类 Riverpod 代码非常简单,即一个注解的定义:

@Target({TargetKind.classType, TargetKind.function})
class Riverpod {
  /// {@macro riverpod_annotation.provider}
  const Riverpod({
    this.keepAlive = false,
  });

  /// Whether the state of the provider should be maintained if it is no-longer used.
  ///
  /// Defaults to false.
  final bool keepAlive;
}

/// {@macro riverpod_annotation.provider}
@Target({TargetKind.classType, TargetKind.function})
const riverpod = Riverpod();
  1. Riverpod_cli

Riverpod_cli 顾名思义是 Riverpod 的脚手架定义,主要是一些 Riverpod 的命令行,帮助升级到 Riverpod 的新版本。

  1. riverpod_generator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrz059s6-1672732482260)(null)]

riverpod_generator 该项目是 Riverpod 的一个附带包,旨在通过依赖代码生成来提供不同的定义 “providers” 的语法。

  1. riverpod_graph

riverpod_graph 项目非常简单,仅有一个 analyze.dart 文件。主要用来分析 Riverpod 项目和生成 providers / Widget 之间的依赖关系图

  1. Riverpod_lint

Riverpod_lint 也非常简单,主要在编译/构建时进行静态类型检查。

  1. riverpod_lint_flutter_test

riverpod_lint_flutter_test 是 Riverpod_lint 的测试用例。

这里可以得出一些结论:1. Riverpod 是一个纯 dart 实现的 package,没有任何平台相关的代码。2. Riverpod 插件其核心是 riverpod 项目下的状态管理,支持 annotation 注解hook 钩子静态检查 lint 等功能,以及 cli脚手架依赖关系图分析等插件工具组成,可以说插件的功能十分完善。

二、状态管理实现过程分析

看完整个项目结构,发现整个插件实现十分复杂难,让人对于源码分析无从下手。下面我们从使用一个状态管理的案例出发,通过分析其实现的底层逻辑,来推演整个状态更新的过程。

还是从最经典的案例: 计数器开始:

void main() {
  runApp(
    // 添加ProviderScope可以使Riverpod适用于整个项目
    const ProviderScope(child: MyApp()),
  );
}

/// provider是全局声明的,并指定如何创建一个状态
final counterProvider = StateProvider((ref) => 0);

class Home extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      body: Center(
        // Consumer是一个允许读取providers的widget
        child: Consumer(
          builder: (context, ref, _) {
            final count = ref.watch(counterProvider);
            return Text('$count');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // read方法用于更新provider的值
        onPressed: () => ref.read(counterProvider.notifier).state++,
        child: const Icon(Icons.add),
      ),
    );
  }
}
1. 代码结构分析
  1. 添加 ProviderScope 使 Riverpod 适用于整个项目

在前面我们有介绍到 ProviderScope ,是一个状态管理 Widget,位于 flutter_riverpod 项目中。ProviderScope 是一个 StatefulWidget,成员变量有一个 child 用于子类实现 Widget,observers 作为观察者的集合,用于订阅 providers 的监听;overrides 用于 provider/family 如何覆盖的信息;提供 parent 作为 ProviderContainer,暴露其他 packages 下作用域的访问。

  1. 声明全局变量StateProvider

可以看到 StateProvider 继承 _StateProviderBase 混入 AlwaysAliveProviderBase。我们在创建实例时有传入 _createFn ,其实就是一个带 StateProviderRef 参数的 Function

 class StateProvider<T> extends _StateProviderBase<T>
    with AlwaysAliveProviderBase<T> {}

 final T Function(StateProviderRef<T> ref) _createFn;

这里混入 AlwaysAliveProviderBase,AlwaysAliveProviderBase 又混入 ProviderListenable。 主要是为了实现 Provider 的 state 更新功能。

  1. 创建 Consumer 允许读取 providers

这里我们分析下Consumer:

@sealed
class Consumer extends ConsumerWidget {
  /// {@template riverpod.consumer}
  const Consumer({super.key, required ConsumerBuilder builder, Widget? child})
      : _child = child,
        _builder = builder;

  final ConsumerBuilder _builder;
  final Widget? _child;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return _builder(context, ref, _child);
  }
}

Consumer 的实现非常简单,继承 ConsumerWidget,实现 ConsumerBuilder,完成 build 的 Widget 构建。

我们接着往下看,ConsumerWidget 继承 ConsumerStatefulWidget,创建 _ConsumerState,并将 widget.build 添加到自身 build 实现中。

abstract class ConsumerWidget extends ConsumerStatefulWidget {
  /// {@macro riverpod.consumerwidget}
  const ConsumerWidget({super.key});

 	Widget build(BuildContext context, WidgetRef ref);

  @override
  // ignore: library_private_types_in_public_api
  _ConsumerState createState() => _ConsumerState();
}

class _ConsumerState extends ConsumerState<ConsumerWidget> {
  @override
  WidgetRef get ref => context as WidgetRef;

  @override
  Widget build(BuildContext context) {
    return widget.build(context, ref);
  }
}

ConsumerStatefulWidget 只是在 createElement 中将 ConsumerStatefulElement 创建。

abstract class ConsumerStatefulWidget extends StatefulWidget {
  /// A [StatefulWidget] that can read providers.
  const ConsumerStatefulWidget({super.key});

  @override
  // ignore: no_logic_in_create_state
  ConsumerState createState();

  @override
  ConsumerStatefulElement createElement() {
    return ConsumerStatefulElement(this);
  }
}

我们接下来看一看 ConsumerStatefulElement,其继承 StatefulElement,通过在 didChangeDependencies 中判断当前的 ProviderContainer 是否是同一个。

  1. 更新counterProvider的状态
ref.read(counterProvider.notifier).state++

其中 ref 是 WidgetRef,一个抽象类,主要定义了 provider 和 Widget 之前的交互。有 watchexistslistenlistenManualreadrefreshinvalidate等方法。

2. 执行流程分析
  1. Consumer.build 时 ConsumerStatefulElement 执行 watch 方法将 target 添加到 _dependencies 中,然后通过 ProviderContainer 的 listen 方法添加其 provider 的 addListener。最后将 listern 封装成 _ListenerEntry 添加到S _listeners 的集合中。

/// state_notifier-0.7.2+1/lib/state_notifier.dart
RemoveListener addListener(
    Listener<T> listener, {
    bool fireImmediately = true,
  }) {
    final listenerEntry = _ListenerEntry(listener);
    _listeners.add(listenerEntry);
    ...
    return () {
      if (listenerEntry.list != null) {
        listenerEntry.unlink();
      }
    };
  }
  1. 当我们点击 onPressed 时,通过 ref.read 获取到 StateController.state 对象,然后更新其 state 值。在 StateNotifier.state 中我们可以看到其代码逻辑,通过遍历 _listeners 进行回调

/// state_notifier-0.7.2+1/lib/state_notifier.dart
for (final listenerEntry in _listeners) {
  try {
     listenerEntry.listener(value);
  } catch (error, stackTrace) {
     ...
  }
}
  1. 当我们的 listener 触发更新时,前面定义的 Consumer 的 builder 函数就会触发回调,从而更新其监听的 UI。可以看到其更新逻辑也非常简单,将 StateController 的状态 result 赋值给 _stateNotifier,然后调用 setState 触发 state 的更新。

  /// riverpod/lib/src/state_provider/base.dart
  @override
  void create({required bool didChangeDependency}) {
    final provider = this.provider as _StateProviderBase<T>;
    final initialState = provider._create(this);

    final controller = StateController(initialState);
    _controllerNotifier.result = Result.data(controller);

    _removeListener = controller.addListener(
      fireImmediately: true,
      (state) {
        _stateNotifier.result = _controllerNotifier.result;
        setState(state);
      },
    );
  }

至此,我们简单的分析完了整个状态更新的执行过程。

总结

今天我们先介绍了Riverpod项目的整理结构及其功能,然后从计数器案例入手,分析Riverpod的文件组成和源码执行更新状态的整个过程。下一篇文章将继续深入分析Riverpod的实现源码,请大家保持关注。

作者:Fitem
链接:https://juejin.cn/post/7183260548711055421

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值