Getx 之obs Obx刷新

var a = 0.obs

我们看下会发生什么?
a就被RxInt包裹,
extension IntExtension on int {
  /// Returns a `RxInt` with [this] `int` as initial value.
  RxInt get obs => RxInt(this);
}

Rx<T>继承_RxImpl
class Rx<T> extends _RxImpl<T> {

}

接着看
\color{red}{0.obs会在这里会触发初始化,进行赋值}

abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {
//这里触发赋值操作
  _RxImpl(T initial) {
    _value = initial;
  }

  void update(void Function(T? val) fn) {
    fn(_value);
    subject.add(_value);
  }

  void trigger(T v) {
    var firstRebuild = this.firstRebuild;
    value = v;
    if (!firstRebuild && !sentToStream) {
      subject.add(v);
    }
  }
}

\color{red}{重点RxObjectMixin 会涉及到get、set方法,进行赋值和取值}

mixin RxObjectMixin<T> on NotifyManager<T> {
  late T _value;

  bool firstRebuild = true;
  bool sentToStream = false;
 
  String get string => value.toString();

  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 {
//这里的subject是GetStream;   RxInterface.proxy == observer(build里面传入的observer)
    RxInterface.proxy?.addListener(subject);
    return _value;
  }

}

\color{red}{重点 t通过listen会将subject订阅者添加到—onData集合中,以方便在收到add方法的时候遍历_onData调用每个对象的_data.call()触发_updateTree 然后调用setState刷新UI}

class GetStream<T> {
 List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];


  FutureOr<void> addSubscription(LightSubscription<T> subs) async {
    if (!_isBusy!) {
      return _onData!.add(subs);
    } else {
      await Future.delayed(Duration.zero);
      return _onData!.add(subs);
    }
  }
  void _notifyData(T data) {
    _isBusy = true;
    for (final item in _onData!) {
      if (!item.isPaused) {
        item._data?.call(data);
      }
    }
    _isBusy = false;
  }
void add(T event) {
    assert(!isClosed, 'You cannot add event to closed Stream');
    _value = event;
    _notifyData(event);
  }
  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;
  }
}

总结:
当我们给观察者对象添加.obs后,被封装成了Rx<T>,RX继承_RxImpl,_RxImpl 继承RxNotifier with RxObjectMixin,在Obx中使用(a.value)会调用RxObjectMixin的get方法,get方法内部 观察者监听stream的变化(RxInterface.proxy?.addListen(subject)),当赋值的时候(a.value=xx)会调用set方法,如果该被订阅对象已经失效或者被订阅对象的值没有发生变化并且非首次构建则不会刷新,否则会赋值新值,并且调用subject(getStream)的add方法,然后调用GetStream类的_notifyData方法,遍历onData(onData是我们在Obx的时候订阅的集合,Obx继承ObxWidget继承StatefulWidget,initState方法里面绑定观察者订阅关系(final _observer = RxNotifier();)订阅者(late StreamSubscription subs;),subs = _observer.listen(_updateTree,false);RXNotifier混入了NotifyManager,NotifyManager里的(GetStream<T> subject = GetStream<T>();)里面有listen方法,然后再调用subject.listen(),会调用addSubscription(subs),然后 _onData!.add(subs);添加到_onData集合),调用item._data.call()方法。当RxNotifier接受到通知后会通过listen回调调用_updateTree,这里面调用的是setState({})更新UI;

注释:Obx原理
,obx继承自ObxWidget,ObxWidget在initstate方法中绑定了观察者订阅关系。build方法中调用RxInterface.notifyChildren 把_observer作为RxInterface.proxy 的临时属性,调用builder的后恢复原有的属性, 注意builder(controller)函数里一定要包含obs.value,否则在if (!observer.canUpdate) 检测时,由于没有观察对象,会抛出提示异常。

class Obx extends ObxWidget {
  final WidgetCallback builder;

  const Obx(this.builder, {Key? key}) : super(key: key);

  @override
  Widget build() => builder();
}

abstract class ObxWidget extends StatefulWidget {
  const ObxWidget({Key? key}) : super(key: key);

  @override
  ObxState createState() => ObxState();

  @protected
  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
  Widget build(BuildContext context) =>
      RxInterface.notifyChildren(_observer, widget.build);
}
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;

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

  bool get canUpdate => _subscriptions.isNotEmpty;

  /// This is an internal method.
  /// Subscribe to changes on the inner stream.
  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,
      );
  }
}

abstract class RxInterface<T> {
  static RxInterface? proxy;

  bool get canUpdate;

  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;

    }
    RxInterface.proxy = oldObserver;
    return result;
  }
}

为什么要有这一步 RxInterface.proxy = observer;

T get value {
  RxInterface.proxy?.addListener(subject);
  return _value;
}
Rx 最终是混入了 RxObjectMixin 类,即在 Rx 的获取数据中调用了 RxInterface.proxy?.addListener ,那什么时候获取 Rx 的数据呢?
Obx(() {
  return Text("${a.value}");
});
就是在 Obx 的 builder 方法里,这就清楚了为什么在  RxInterface.notifyChildren  方法里是先将传入的 observer 赋值给 proxy 然后再调用 builder 方法了,因为这样在调用 builder 方法时调用了 Rx.value ,而在 get value 中调用了  RxInterface.proxy?.addListener ,且 addListener 传入的 subject 是 Rx  的 GetStream, 而 proxy 是 _ObxState 里创建的 RxNotifier。

文字理解
obx 刷新原理

var a = 1.obs
将变量a包装成RxInt 继承自Rx<T> 继承自抽象类_RxImpl  在这里初始化赋值, 继承RxNotifier并且混入了RxObjectMiXin

obx 继承自obxWidget->statefulWidget

obxState中
final _observer = RxNotifier()
var streamSubScription _subs

initState 中绑定观察者和订阅关系
_subs = _oberver.listen(_updateTree,cancleError:false)
接着会调用
getStream中的listen
listen(){
    创建subs绑定传入的updateTree函数,然后调用addSubscription(subs),添加到_onData集中(这是个订阅者集合)
}

接下来看builder
builder中调用了
RxInterface.notifyChildren(_observer,builder)
实现关键代码: 临时变量保存 const oldObserver = Rxinterface.proxy , Rxinterface.proxy = _observer,调用bulder方法,将原来的oldObserve赋值给Rxinterface.proxy =  oldObserver  

Obx的bulder方法中使用了a.value
a.value 就调用了 rxObjectMixin中的get方法
RxInterface.proxy.addListener(subject) //subject是getStream对象  RxInterface.proxy = build中传入的observer(RxNotifier)
rxNotifier实现了addListener() 是因为Rxnotifier混入了NotifyManager
addListener(subject){
判断subject对象是否有订阅对象 如果没有就调用getStream的listen方法将订阅者加入到订阅者集合
}

当我们给a.value= 2赋值的时候
会调用RxObjectMixin 的set方法
set(val){
首先判断订阅是否关闭
subject.close return
_value == val && !firsrBuld  return
_value = val
subject.add(_value) //调用GetStream中的add方法
add(event){
_value = event
_notifyData(event)
}
_notifyData(){
for(item in _onData){
    item._data.call() //这里就会调用updateTree 接着调用setState({})进行刷新
}
}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容