likes
comments
collection
share

Flutter Getx响应式状态管原理分析

作者站长头像
站长
· 阅读数 25

前言

最近新写一个项目,要用到状态管理。 而之前选择BLOC作为状态管理, 效果还是很不错的. UI和逻辑 分离,可是回过头看项目会发现中各种状态类、事件类的模板代码太多。于是想看看现在社区里有什么新的状态管理方案, 找呀找 于是今天我们的主角登场了GetX 初看它的API发现足够简单,也符合直觉,剧作者说性能还好。

既然实现的简单,当然要看它的实现原理啦

  • GetBuilder 手动管理状态
  • GetX 响应式状态管理

GetBuilder 手动管理

先看看一个简单的例子 建立Controller和View类,Controller里面主动调用update() 去刷新View层里面的UI。

class Controller extends GetxController {
  int counter = 0;
  void increment() {
    counter++;
    update(); // 当调用增量时,使用update()来更新用户界面上的计数器变量。
  }
}

GetBuilder<Controller>(
  init: Controller(), 
  builder: (_) => Text(
    '${_.counter}',
  ),

核心思想

Flutter Getx响应式状态管原理分析

  1. 定义一个继承GetxController的类: 逻辑- Controller
  2. 定义一个GetBuilder类:界面- *View 层
  3. controlelr 里面调用update() 刷新UI

View、Controller是如何建立关系的

分析 GetBuilder 的源码

  1. 它是一个StatefulWidget
  2. initState()寻找定义的Controller. 这里有一些寻找策略,跟我们这没关系忽略掉
  3. subscribeToController() Cool找到了! 准备开始建立关系 给Controller通过调用addListner()的方法增加一个监听者, 并绑定一个函数getUpdate
  4. getUpdate 来源于GetStateUpdaterMixin 功能很简单: 当前widget存活就执行setState()
// get-3.22.2/lib/get_state_manager/src/simple/get_state.dart 
class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  final T init;
  ...

    @override
  _GetBuilderState<T> createState() => _GetBuilderState<T>();

}

class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> with GetStateUpdaterMixin {
  T controller;

  @override
  void initState() {
    // 寻找Controller
    ...
    // 订阅
    _subscribeToController();
  }

  void _subscribeToController() {
    remove?.call();
    remove = (widget.id == null)
        ? controller?.addListener(getUpdate)
        : controller?.addListenerId(widget.id, getUpdate);
  }


}
mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
  void getUpdate() {
    if (mounted) setState(() {});
  }
}

分析 GetxController 的源码

  1. GetxController 实现了Listenable可监听对象的协议
  2. addListener时记录到一个List对象里, 并取名为updater
  3. 调用update() -> refresh() 迭代所有监听者并调用.
 class GetxController extends DisposableInterface with ListNotifier {
     void update([List<String> ids, bool condition = true]) {
     // 移除无用的代码
     ...
     refresh();
     ...
     }
 }

 class ListNotifier implements Listenable {
     List<GetStateUpdate> _updaters = <GetStateUpdate>[];

     @protected
     void refresh() {
     for (var element in _updaters) {
         element();
     }
     }

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

}


  1. GetBuilder是一个状态类,它建立了和Controller的关系并在initState()时增加当前View作为监听者.
  2. Controller里维护了一个updater的列表记录每个监听者,在update()的时候会通知刷新所有View的状态。

总结

  • 优点: API足够简单清晰,占用很少内存资源。
  • 不足: 还是需要我们去手动update(), 有没有响应式的接口? 能让我们直接调用,当然有,请往下面看。

GetX 响应式自动管理

同样需要我们声明Controller和View类,此时最直观的感受就是Controller里面 不需要手动update() 太棒了!!!

class Controller extends GetXcontroller {
  final count1 = 0.obs;
  final count2 = 0.obs;
  int get sum => count1.value + count2.value;
}

GetX<Controller>(
  builder: (controller) {
    print("count 1 rebuild");
    return Text('${controller.count1.value}');
  },
),

核心思想

Flutter Getx响应式状态管原理分析

  1. 定义一个Controller类: 逻辑 - Controller

    Controller里包含多个以 .obs 的形式表示的变量,我们把这个变量称作为响应式变量

  2. 定义一个GetBuilder类:界面 - View

  3. 只要响应式变量发生改变,就会自动通知View刷新界面, 哇太神奇了 :)

分析GetX的源码

  1. GetX 是一个StatefulWidget

    class GetX<T extends DisposableInterface> extends StatefulWidget {
      final GetXControllerBuilder<T> builder;
    
      const GetX({
        this.builder,
      });
    
      @override
      GetXState<T> createState() => GetXState<T>();
    }
    
  2. GetState类初始化的时候创建一个 _observer = RxNotifier() RxNotifier()里包含一个Stream的流

    1. initState() _observer 开始增加一个监听者刷新View

      class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
        GetXState() {
        _observer = RxNotifier();
        }
        RxInterface _observer;
        StreamSubscription subs;
      
        @override
        void initState() {
        // 寻找controller
        // ... 
        subs = _observer.listen((data) => setState(() {}));
        super.initState();
        }
      }
      
    2. build() 这里很有意思. 把_observer作为RxInterface.proxy 的临时属性,在使用builder的后恢复原有的属性, 需要注意的是builder(controller)函数里一定要包含obs.value, 要不然会提示异常。

      Widget get notifyChildren {
        final observer = RxInterface.proxy;
        RxInterface.proxy = _observer;
        final result = widget.builder(controller);
        if (!_observer.canUpdate) {
          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 = observer;
        return result;
      }
      
      @override
      Widget build(BuildContext context) => notifyChildren;
      
      
    3. 执行builder(controller) 包含调用obs.value的属性. 开始订阅当前属性的变化, 当发生改变事就自动触发setState() 这是GetX的核心的地方;

      mixin RxObjectMixin<T> on NotifyManager<T> {
        // controller.count.value
        T get value {
          if (RxInterface.proxy != null) {
            RxInterface.proxy.addListener(subject);
          }
          return _value;
        }
      }
      
      mixin NotifyManager<T> {
        GetStream<T> subject = GetStream<T>();
        final _subscriptions = <StreamSubscription>[];
      
        void addListener(GetStream<T> rxGetx) {
          if (_subscriptions.contains(rxGetx.listen)) {
            return;
          }
      
          final subs = rxGetx.listen((data) {
            subject.add(data);
          });
          _subscriptions.add(subs);
        }
      
      }
      

总结

GetX 初始化的时候会创建一个Stream,它来监听当前View的变化,当UI中响加载 obs.value 时,就会把 View刷新 挂载到这个obs属性上。 只要属性变化就会通知View刷新页面。

GetBuilder vs GetX

方式自动update内存占用
GetBuilder非常少
GetX一般