Flutter:尝试了解Get

1,162 阅读11分钟

wallhaven-85g5d1.jpg

零、前置

1、依赖注入

“依赖”:当类A需要用到B类的功能时,则表示类A对类B有依赖。在我们正常的情况下,需要使用B类的功能,需要先对B类进行创建、实例化

"依赖注入":将创建对象的动作交给别人,自己直接使用依赖项的功能的这个过程。

我们可以把这个过程简单比作,房子,然后居住

1)在传统的形式下,我们需要赚钱买房(创建对象),然后才可以有的居住

2)在依赖注入的情况下,我们可以通过中介/房东租房,然后居住

而且中介/房东这边有好多房子,如果某个你觉得不合适,可以找到合适的为止

一、Get是什么?

Get是FLutter上一个轻量强大的解决方案,拥有高性能的状态管理智能的依赖注入和便携的路由管理 我们今天暂时不关注他的路由管理~

二、怎么使用Get

Get中有两种方式可以实现状态管理器

1、使用GetBuilder,简单的状态管理器

2、使用Obx,响应式状态管理器

1、使用GetBuilder

1) 代码演示

我们使用最简单的计时器作为例子,每次点击FloatingActionButton,对应Text的值加一并刷新页面

可以看到我们有两个类,分别是MyHomePage 和MainController

在MyHomePage 中

  1. 首先我们定义了 MainController的变量
  2. 在initState中使用了Get.put(MainController())初始化
  3. 在build,使用了GetBuilder作为一个Widget套在我们最外层
  4. 最后在dispose的时候Get.delete()去注销

在MainController 中

  1. 首先继承了GetxController
  2. 定义了一个counter 变量
  3. incrementCounter方法,每次调用都把counter加一,并调用update刷新页面。
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late MainController _controller;

  @override
  void initState() {
    super.initState();
    _controller = Get.put(MainController());
  }

  @override
  Widget build(BuildContext context) {
    return GetBuilder<MainController>(
      builder: (_) {
        return Scaffold(
          body: Center(
            child: Text(
              '${_controller.counter}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _controller.incrementCounter,
            child: const Icon(Icons.add),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    Get.delete<MainController>();
  }
}
import 'package:get/get.dart';

class MainController extends GetxController {
  int counter = 0;

  void incrementCounter() {
    counter++;
    update();
  }
}

2) 使用到的东西

在前面,我们使用到了Get.putGet.deleteGetBuilderGetxControllerupdate\color{blue}{Get.put、Get.delete、GetBuilder、GetxController和update}。现在让我们来看一下他们分别是什么神奇宝贝。

3) Get

首先让我们先看看Get是什么东西

    class _GetImpl extends GetInterface {}
    // ignore: non_constant_identifier_names
    final Get = _GetImpl();

怀着激动的心情,点开了Get,发现只有两行代码,Get是一个全局对象,继承自GetInterface,当我们点开GetInterface,也能发现跟我们要找的没有太大关系,那么put、delete他们在哪里呢?

带着疑惑我点开了put,好家伙,原来都放在另一个目录下,使用的是拓展,拓展了GetInterface ,生怕给我看到一样(开玩笑,封装得真好)

然后我们可以看到,GetInterface(也就是Get),并没有在put做太多的操作,只是把他转给了GetInstance

getinstance.png

那我们的问题一下子变成了了解GetInstance是什么神奇宝贝。

4) GetInstance中的put和delete

我们可以看到GetInstance是一个单例;并且内部维护了一个Map变量:

static final Map<String, _InstanceBuilderFactory> *_singl* = {};

这个是一个string对应 _InstanceBuilderFactory的Map,我们先知道有这么个东西,然后看一下我们着重要注意的putdelete

a) Put
      S put<S>(
        S dependency, {
        String? tag,
        bool permanent = false,
        @Deprecated("Do not use builder, it will be removed in the next update")
        InstanceBuilderCallback<S>? builder,
      }) {
        _insert(
            isSingleton: true,
            name: tag,
            permanent: permanent,
            builder: builder ?? (() => dependency));
        return find<S>(tag: tag);
      }

put又把我们的实例(GetxController)转手就给了自己的内部方法 _insert,有种租房的感觉了,一路上我们从Get这个入口,一直把我们的租房需求(依赖实例)一直传到GetInstance,还是没有找到本地房东~

_insert 用我们给到的参数,创建了一个_InstanceBuilderFactory对象,然后存放到我们上面说的_singl变量中去。

现在,我们看一下这个 _InstanceBuilderFactory又是什么东西。

首先,顾名思义,这个应该是实例构建工厂。我们抛开其他的变量先不谈,着重看一下 _InstanceBuilderFactorygetDependency方法,我们再顾名思义一下,这个是获取依赖的方法。

  **/// _InstanceBuilderFactory 类中的一个方法
  S getDependency() {
    if (isSingleton!) {
      if (dependency == null) {
        _showInitLog();
        dependency = builderFunc();
      }
      return dependency!;
    } else {
      return builderFunc();
    }
  }**

我们可以看到最终都是调用的 builderFunc ,我们回溯到put的时候,可以看到这个builderFunc是 (() => dependency) ,实际上就是一个返回依赖的函数。那么我们基本可以确定这个 _InstanceBuilderFactory 就是一个拥有房本的本地房东了,他直接管理我们想要的依赖(GetxController)

我们简单理一下到目前为止的流程:

get4.png 这个时候再看一下put方法_insert之后还调用了find,去获取并返回对应的依赖项

  1. 首先通过依赖的类名和tag去获取一个key,这个key是我们前面_singl 对应的key
  2. 然后通过这个key去获取对应的_InstanceBuilderFactory,也就是我们的本地房东。
  3. 然后通过_InstanceBuilderFactory的getDependency获取依赖,再对我们的依赖做了初始化的动作,调用了依赖的onStart
  4. 最后返回对应的依赖。
      S find<S>({String? tag}) {
        final key = _getKey(S, tag);
        if (isRegistered<S>(tag: tag)) {
          final dep = _singl[key];
          if (dep == null) {
            if (tag == null) {
              throw 'Class "$S" is not registered';
            } else {
              throw 'Class "$S" with tag "$tag" is not registered';
            }
          }
          final i = _initDependencies<S>(name: tag);
          return i ?? dep.getDependency() as S;
        } else {
          // ignore: lines_longer_than_80_chars
          throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
        }
      }
      
      String _getKey(Type type, String? name) {
        return name == null ? type.toString() : type.toString() + name;
      }
      
      S? _initDependencies<S>({String? name}) {
        final key = _getKey(S, name);
        final isInit = _singl[key]!.isInit;
        S? i;
        if (!isInit) {
          i = _startController<S>(tag: name);
        }
        return i;
      }
      
      S _startController<S>({String? tag}) {
        final key = _getKey(S, tag);
        final i = _singl[key]!.getDependency() as S;
        if (i is GetLifeCycleBase) {
          i.onStart();
        }
        return i;
      }
b) Delete

我们这里只关注我们刚刚put相关的逻辑,

  1. 首先判断当前key是否包含在_singl中并且是否对应的值存在
  2. 通过key获取到的_InstanceBuilderFactory 来获取我们的依赖项并进行onDelete的逻辑
  3. 将_singl中的对应_InstanceBuilderFactory 移除掉。
    bool delete<S>({String? tag, String? key, bool force = false}) {
        final newKey = key ?? _getKey(S, tag);

        if (!_singl.containsKey(newKey)) {
          Get.log('Instance "$newKey" already removed.', isError: true);
          return false;
        }

        final dep = _singl[newKey];

        if (dep == null) return false;

        final _InstanceBuilderFactory builder;
        if (dep.isDirty) {
          builder = dep.lateRemove ?? dep;
        } else {
          builder = dep;
        }

        if (builder.permanent && !force) {
          Get.log(
            // ignore: lines_longer_than_80_chars
            '"$newKey" has been marked as permanent, SmartManagement is not authorized to delete it.',
            isError: true,
          );
          return false;
        }
        final i = builder.dependency;

        if (i is GetxServiceMixin && !force) {
          return false;
        }

        if (i is GetLifeCycleBase) {
          i.onDelete();
          Get.log('"$newKey" onDelete() called');
        }

        if (builder.fenix) {
          builder.dependency = null;
          builder.isInit = false;
          return true;
        } else {
          if (dep.lateRemove != null) {
            dep.lateRemove = null;
            Get.log('"$newKey" deleted from memory');
            return false;
          } else {
            _singl.remove(newKey);
            if (_singl.containsKey(newKey)) {
              Get.log('Error removing object "$newKey"', isError: true);
            } else {
              Get.log('"$newKey" deleted from memory');
            }
            return true;
          }
        }
      }

5) GetBuilder

我们先了解一下Getbuilder这个widget。

Get5.png 他是一个StatefulWidget,我们重点关注他的两个属性:

  • tag,区分controller,例如多个详情页同时开启的时候,可以使用id作为tag用于区分不同的详情页。即详情A和详情B的controller同时存在
  • id :控制刷新颗粒度

我们简单知道这两个的一个作用,tag为什么能区分的秘诀在GetInstance的getKey当中。

至于id如何控制刷新颗粒度?我们一起来把目光投向GetBuilderState

GetBuilderState的initState做了两件事情:

  1. 初始化了controller
  2. 调用了_subscribeToController,而_subscribeToController如下所见,根据是否有id去调用controller的不同方法添加回调,是我们的GetStateUpdaterMixin中的getUpdate,其实也就是setState。
      void _subscribeToController() {
        _remove?.call();
        _remove = (widget.id == null)
            ? controller?.addListener(
                _filter != null ? _filterUpdate : getUpdate,
              )
            : controller?.addListenerId(
                widget.id,
                _filter != null ? _filterUpdate : getUpdate,
              );
      }

      void _filterUpdate() {
        var newFilter = widget.filter!(controller!);
        if (newFilter != _filter) {
          _filter = newFilter;
          getUpdate();
        }
      }
    mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
      void getUpdate() {
        if (mounted) setState(() {});
      }
    }

流程大致如下:

Get6.png

小结

GetBuilder是一个StatefulWidget,初始化的时候将setState传递给GetxController。让GetxController可以去刷新我们的widget

通过tag更加细化controller;通过id控制颗粒度

6) GetxController

通过前面我们已经知道了controller从新建到注销的流程。也知道了GetBuilder传递了setState给到了controller。

那现在我们来看看这个GetxController是什么东西

Get7.png

他们的关系我就不过多赘述了,通过“点点点”,一层一层地拨开GetxController的衣服,发现最为重要的只有两个。分别是ListNotifierMixinGetLifeCycleBase

GetLifeCycleBase

没有太多的东西,声明了onStart和onDelete两个变量,供生命周期变化时调用,像我们前面的GetInstance在find的时候就去调用了。

    mixin GetLifeCycleBase {
      final onStart = InternalFinalCallback<void>();
      final onDelete = InternalFinalCallback<void>();

      void onInit() {}
      void onReady() {}
      void onClose() {}

      bool _initialized = false;
      bool get initialized => _initialized;

      void _onStart() {
        if (_initialized) return;
        onInit();
        _initialized = true;
      }

      bool _isClosed = false;
      bool get isClosed => _isClosed;

      void _onDelete() {
        if (_isClosed) return;
        _isClosed = true;
        onClose();
      }

      void $configureLifeCycle() {
        _checkIfAlreadyConfigured();
        onStart._callback = _onStart;
        onDelete._callback = _onDelete;
      }

      void _checkIfAlreadyConfigured() {
        if (_initialized) {
          throw """You can only call configureLifeCycle once. 
    The proper place to insert it is in your class's constructor 
    that inherits GetLifeCycle.""";
        }
      }
    }

ListNotifierMixin

    mixin ListNotifierMixin on ListenableMixin {

      List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[];

      HashMap<Object?, List<GetStateUpdate>>? _updatersGroupIds =
          HashMap<Object?, List<GetStateUpdate>>();
     
     …… 省略
     }

ListNotifierMixin主要是对其中的两个变量进行管理

  • _updaters :列表,存放id为空的GetBuilder的setState
  • _updatersGroupIds :哈希map,存放id不为空的GetBuilder的setState

前面我们讲GetBuilder的时候说到了他初始化的时候会将setState通过addListeneraddListenerId传递给GetxController。如下所示,就是将他们存到我们上面对应的变量中去,最终在update的时候去调用。

      @override
      Disposer addListener(GetStateUpdate listener) {
        assert(_debugAssertNotDisposed());
        _updaters!.add(listener);
        return () => _updaters!.remove(listener);
      }

      Disposer addListenerId(Object? key, GetStateUpdate listener) {
        _updatersGroupIds![key] ??= <GetStateUpdate>[];
        _updatersGroupIds![key]!.add(listener);
        return () => _updatersGroupIds![key]!.remove(listener);
      }

6) 总结

  • GetBuilder:可以通过id控制刷新粒度,通过tag细化controller;在初始化的时候传递setState给到GetxController。
  • GetxController:通过update去调用传递来的setState从而实现刷新的目的
  • GetInstance:管理_InstanceBuilderFactory(也就是我们的房东)
  • _InstanceBuilderFactory:管理GetxController

2、使用Obx

1) 代码演示

还是使用我们的老朋友,计数器;代码更加的简洁,不用依赖getController

    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key});

      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
      RxInt counter = RxInt(0);

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Obx(
            () => Center(
              child: Text(
                '${counter.value}',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => incrementCounter(),
            child: const Icon(Icons.add),
          ),
        );
      }

      void incrementCounter() async {
        counter.value = counter.value + 1;
      }
    }

2) 使用到的东西

  • 定义了一个RxInt变量,使用了.value
  • 使用了一个Obx组件

就只有两个东西,counter重新赋值之后。Obx就会刷新,有点神奇;

它是怎么实现控制刷新??!以及他们是怎么绑定的? 让我们带着这样的疑问,一步步揭开它神秘的面纱~

3) Obx

Get8.png

可以看出,Obx是一个StatefulWidget,我们重点关注一下ObxState。 我们在这里先不深挖,知道个大概,了解到了setState给传入了某个方法里即可,后面再介绍~ 又留一个悬念(又挖一个坑)

  1. 定义了一个RxNotifier变量_observer
  2. 初始化的时候将setState使用listen传入。好的,我们看到setState了,知道最后肯定调用的都是它去做刷新动作。
  3. 在build时,使用RxInterface.notifyChildren将_observer和widget.build传入。
    class ObxState extends State<ObxWidget> {
      final _observer = RxNotifier();
      late StreamSubscription subs;

      @override
      void initState() {
        super.initState();
        subs = _observer.listen(_updateTree, cancelOnError: false);
      }

      void _updateTree(_) {
        if (mounted) {
          setState(() {});
        }
      }

      @override
      void dispose() {
        subs.cancel();
        _observer.close();
        super.dispose();
      }

      @override
      Widget build(BuildContext context) =>
          RxInterface.notifyChildren(_observer, widget.build);
    }

4) RxInt

Get9.png

不赘述脱衣服的过程,前面的都是中间商,直接进入正题,能看到最重要的有三个,分别是

  • RxNotifier/RxInterface (Obx中也使用到了。)
  • RxObjectMixin
  • NotifyManager

我们一个个看看他们是什么神奇宝贝

5) RxNotifier/RxInterface

这是响应式刷新使用过程中的基类,无论是我们的main中的RxInt,还是Obx中的RxNotifier都是RxInterface

通过RxInterface的代码;我们可以得到以下信息:

  1. RxInterface是一个抽象类,定义了四个方法需要实现,分别是canUpdate、addListener、close、listen;

  2. 一个静态可空变量proxy,在builder过程缓存当前Obx的RxNotifier,看不懂没关系,知道有这么个东西就行了,后面会有一个更加清晰的解析。(又是挖一个坑)

  3. 静态方法notifyChildren,对RxInterface.proxy的操作,显示缓存,然后赋值,然后又把缓存的值设置回去,流程如下:

    1. 先将RxInterface.proxy缓存至oldObserver
    2. 将从Obx定义的observer赋值给了RxInterface.proxy
    3. 调用了传进来的builder方法
    4. 最后将开始缓存的oldObserver 赋值回RxInterface.proxy
    abstract class RxInterface<T> {
      static RxInterface? proxy;
      
      bool get canUpdate;
      void addListener(GetStream<T> rxGetx);
      void close();
      StreamSubscription<T> listen(void Function(T event) onData,
          {Function? onError, void Function()? onDone, bool? cancelOnError});
          
      static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
        final oldObserver = RxInterface.proxy;
        RxInterface.proxy = observer;
        final result = builder();
        if (!observer.canUpdate) {
          RxInterface.proxy = oldObserver;
          throw """
          [Get] the improper use of a GetX has been detected. 
          You should only use GetX or Obx for the specific widget that will be updated.
          If you are seeing this error, you probably did not insert any observable variables into GetX/Obx 
          or insert them outside the scope that GetX considers suitable for an update 
          (example: GetX => HeavyWidget => variableObservable).
          If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
          """;
        }
        RxInterface.proxy = oldObserver;
        return result;
      }
    }

看完之后云里雾里,并不知道这个类究竟要干什么。没关系,我们带着更多的疑问看下一个~(坑好像越来越多了)

6) NotifyManager

  1. 定义了一个GetStream变量subject

  2. 定义了一个Map _subscriptions ,key是GetStream,value是List<StreamSubscription>

  3. 主要是管理两个变量,做了添加addListener、注销close和一个listen操作

    1. 其中addListener会比较不清晰一些,显示判断_subscriptions 是否有包含传入的GetStream, 没有的话,调用了传入GetStream的listen,传入了方法
    2. 然后把这个subs 添加到这个key下的List中去
  4. listen则直接转手调用了subject 的listen,所以我们结合之前Obx中的初始化,listen传入的setState,最终转手又到了GetStream当中去了。

  5. close则是对监听、以及列表资源的释放

    mixin NotifyManager<T> {
      GetStream<T> subject = GetStream<T>();
      final _subscriptions = <GetStream, List<StreamSubscription>>{};

      bool get canUpdate => _subscriptions.isNotEmpty;

      void addListener(GetStream<T> rxGetx) {
        if (!_subscriptions.containsKey(rxGetx)) {
          final subs = rxGetx.listen((data) {
            if (!subject.isClosed) subject.add(data);
          });
          final listSubscriptions =
              _subscriptions[rxGetx] ??= <StreamSubscription>[];
          listSubscriptions.add(subs);
        }
      }

      StreamSubscription<T> listen(
        void Function(T) onData, {
        Function? onError,
        void Function()? onDone,
        bool? cancelOnError,
      }) =>
          subject.listen(
            onData,
            onError: onError,
            onDone: onDone,
            cancelOnError: cancelOnError ?? false,
          );

      void close() {
        _subscriptions.forEach((getStream, subscriptions) {
          for (final subscription in subscriptions) {
            subscription.cancel();
          }
        });

        _subscriptions.clear();
        subject.close();
      }
    }

歇一会,简单捋一下目前我们的问题:

  1. ***** 最终问题:它是怎么实现控制刷新??!以及它们是怎么绑定的?
  2. RxInterface中的proxy是什么东西,为什么在notifyChildren中又是缓存又是赋值最后又设置回去?
  3. GetStream是什么鬼,它把setState怎么处理了?

让我们带着这些问题。继续往下看~

7) *** GetStream

这个东西很重要,是Obx&Rx使用的核心,主要是管理一个List<LightSubscription>? 的变量 _onData,对这个列表进行添加、删除和方法调用。 这个_onData则是核心中的核心

而LightSubscription这个类也并没有什么东西,只是做一回调方法的保存,然后让GetStream在需要的时候调用

简单理解就是:GetStream管理了一个LightSubscription列表,通过这个列表去间接的调用存储在LightSubscription中的方法如**_data**,这个也很重要, 在Obx中使用的时候,它这个就是我们从Obx的初始化传递到NotifyManager中,再转到GetStream的 setState

    class GetStream<T> {
      void Function()? onListen;
      void Function()? onPause;
      void Function()? onResume;
      FutureOr<void> Function()? onCancel;

      GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
      List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];

      bool? _isBusy = false;

      FutureOr<bool?> removeSubscription(LightSubscription<T> subs) async {
        if (!_isBusy!) {
          return _onData!.remove(subs);
        } else {
          await Future.delayed(Duration.zero);
          return _onData?.remove(subs);
        }
      }

      FutureOr<void> addSubscription(LightSubscription<T> subs) async {
        if (!_isBusy!) {
          return _onData!.add(subs);
        } else {
          await Future.delayed(Duration.zero);
          return _onData!.add(subs);
        }
      }

      int? get length => _onData?.length;

      bool get hasListeners => _onData!.isNotEmpty;

      void _notifyData(T data) {
        _isBusy = true;
        for (final item in _onData!) {
          if (!item.isPaused) {
            item._data?.call(data);
          }
        }
        _isBusy = false;
      }

      void _notifyError(Object error, [StackTrace? stackTrace]) {
        assert(!isClosed, 'You cannot add errors to a closed stream.');
        _isBusy = true;
        var itemsToRemove = <LightSubscription<T>>[];
        for (final item in _onData!) {
          if (!item.isPaused) {
            if (stackTrace != null) {
              item._onError?.call(error, stackTrace);
            } else {
              item._onError?.call(error);
            }

            if (item.cancelOnError ?? false) {
              //item.cancel?.call();
              itemsToRemove.add(item);
              item.pause();
              item._onDone?.call();
            }
          }
        }
        for (final item in itemsToRemove) {
          _onData!.remove(item);
        }
        _isBusy = false;
      }

      void _notifyDone() {
        assert(!isClosed, 'You cannot close a closed stream.');
        _isBusy = true;
        for (final item in _onData!) {
          if (!item.isPaused) {
            item._onDone?.call();
          }
        }
        _isBusy = false;
      }

      T? _value;

      T? get value => _value;

      void add(T event) {
        assert(!isClosed, 'You cannot add event to closed Stream');
        _value = event;
        _notifyData(event);
      }

      bool get isClosed => _onData == null;

      void addError(Object error, [StackTrace? stackTrace]) {
        assert(!isClosed, 'You cannot add error to closed Stream');
        _notifyError(error, stackTrace);
      }

      void close() {
        assert(!isClosed, 'You cannot close a closed Stream');
        _notifyDone();
        _onData = null;
        _isBusy = null;
        _value = null;
      }

      LightSubscription<T> listen(void Function(T event) onData,
          {Function? onError, void Function()? onDone, bool? cancelOnError}) {
        final subs = LightSubscription<T>(
          removeSubscription,
          onPause: onPause,
          onResume: onResume,
          onCancel: onCancel,
        )
          ..onData(onData)
          ..onError(onError)
          ..onDone(onDone)
          ..cancelOnError = cancelOnError;
        addSubscription(subs);
        onListen?.call();
        return subs;
      }

      Stream<T> get stream =>
          GetStreamTransformation(addSubscription, removeSubscription);
    }

其中的addSubscriptionremoveSubscription,分别是对列表进行添加和删除。

listen则是通过入参构建一个LightSubscription然后通过addSubscription添加进去_onData中。

我们重点关注一下几个notify;

  • _notifyData,其实就是调用了_onData所有item中的_data方法;
  • _notifyError,流程与_notifyData相似,只不过调用的是item中的_onError方法,并且在把对应的item从_onData中删除。
  • _notifyDone,流程与_notifyData一样,只不过调用的是item中的_onDone方法

我们还可以看到有几个add方法,其实没啥,就是对外暴露内部方法:

  • add对外暴露_notifyData
  • addError对外暴露_notifyError

Get10.png

8) RxObjectMixin

RxObjectMixin主要定义了一个泛型变量value。我们重点关注value的getset,这里是绑定更新的关键。

      late T _value;
    	
      set value(T val) {
        if (subject.isClosed) return;
        sentToStream = false;
        if (_value == val && !firstRebuild) return;
        firstRebuild = false;
        _value = val;
        sentToStream = true;
        subject.add(_value);
      }

      T get value {
        RxInterface.proxy?.addListener(subject);
        return _value;
      }

我们先看看get value,它将是我们揭开谜题的最后一块拼图;它主要做了两件事情:

  1. 调用RxInterface.proxy的addListener将自己的subject传入
  2. 返回当前的_value值

又见到了RxInterface.proxy了。回到我们前面的问题,为什么在RxInterface的notifyChildren中,会出现又是缓存又是重置的情况,肯定跟这个有关系,让我们重新捋一下notifyChildren的流程:

Get111.png

**** 我们可以发现,在将ObxRxNotifier赋值给RxInterface.proxy之后,调用了builder,而我们的build就是我们最开始Obx里的内容,也正是我们使用到RxIntget value的时候。所以,在这时候,get value中的RxInterface.proxy其实就是Obx定义的RxNotifier

这个时候,RxInt将自己的subject通过调用RxNotifier的addListener传了进去。

那我们再回过头看一下addListener方法(在NotifyManager中)

      void addListener(GetStream<T> rxGetx) {
        if (!_subscriptions.containsKey(rxGetx)) {
          final subs = rxGetx.listen((data) {
            if (!subject.isClosed) subject.add(data);
          });
          final listSubscriptions =
              _subscriptions[rxGetx] ??= <StreamSubscription>[];
          listSubscriptions.add(subs);
        }
      }
  1. 判断当前是否已经包含这个rxGetx(RxInt的subject)
  2. 没有包含的情况,调用了rxGetx的listen。我们前面讲到这个listen就是将方法传入存放GetStream中去。
  3. 这个方法的内容是:调用当前subject的add方法,前面GetStream的时候讲到,这里的add调用了内部的_notifyData。最终也就是调用了Obx存放在RxNotifier中的setState方法

那么现在我们的流程串起来了,知道了它们是如何绑定了,大致流程如下:

Get122.png

接下来看是如何刷新,让我们看下set value,我们知道在get value的时候,将setState的调用方法通过listen的方法传入到了GetStream中去了,而set Value 则是最终调用了存在GetStream中的方法。

Get133.png

9) 总结

简单总结一下:

  1. 在Obx中,通过RxNotifier的listen将setState方法最终传入到了GetStream中去;
  2. 在RxInt中,通过RxInterface.proxy.addListener,将Obx中调用setState的方法传入到了RxInt的GetStream中去
  3. RxInt赋值的时候,调用了自己GetStream中的方法,然后调用了Obx中存放SetState的方法,最终调用到了SetState

10) 建议

建议一个Obx对应一个Rx,通过上面绑定的流程可以知道。如果你的builder里面有多个Rx,那么你每改动一个值,都会调用到SetState,那么就跟直接使用SetState没什么区别的,那样还不如不使用Get。

End