GetX狀態(tài)管理原理分析

GetX, pub.dev 評(píng)分 11535,目前已經(jīng)超越了provider,成為了flutter中最火的狀態(tài)管理框架.GetX現(xiàn)在包含的內(nèi)容很多,這篇主要分析一下其中狀態(tài)管理的原理.

image.png

首先GetX的依賴注入是getx狀態(tài)管理的關(guān)鍵,這一步保證了數(shù)據(jù)的全局訪問(wèn)能力,不受制于節(jié)點(diǎn)(對(duì)比InherentedWidget),只要被注入,全局可訪問(wèn),通過(guò)getx源碼可以看到

  S put<S>(
    S dependency, {
    String? tag,
    bool permanent = false,
    @deprecated InstanceBuilderCallback<S>? builder,
  }) {
    _insert(
        isSingleton: true,
        name: tag,
        permanent: permanent,
        builder: builder ?? (() => dependency));
    return find<S>(tag: tag);
  }

  void _insert<S>({
    bool? isSingleton,
    String? name,
    bool permanent = false,
    required InstanceBuilderCallback<S> builder,
    bool fenix = false,
  }) {
    // 這一步使用了Type+tag作為key
    final key = _getKey(S, name);

    // 這一步就是將傳入的GetController保存起來(lái)
    if (_singl.containsKey(key)) {
      final dep = _singl[key];
      if (dep != null && dep.isDirty) {
        _singl[key] = _InstanceBuilderFactory<S>(
          isSingleton,
          builder,
          permanent,
          false,
          fenix,
          name,
          lateRemove: dep as _InstanceBuilderFactory<S>,
        );
      }
    } else {
      _singl[key] = _InstanceBuilderFactory<S>(
        isSingleton,
        builder,
        permanent,
        false,
        fenix,
        name,
      );
    }
  }

這里GetX使用了一個(gè)單例

class GetInstance {
  factory GetInstance() => _getInstance ??= const GetInstance._();

  const GetInstance._();

  static GetInstance? _getInstance;

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

因此不管是在哪里,都可以獲取到這個(gè)_singl,也就實(shí)現(xiàn)了全局訪問(wèn)的功能

接下來(lái)就是如何使用這個(gè)GetController,在UI顯示上,GetX提供了兩種使用方案:

  • 使用GetBuilder,手動(dòng)更新?tīng)顟B(tài)
  • 使用Obx自動(dòng)刷新

GetBuilder是一個(gè)StatefulWidget

class GetBuilder<T extends GetxController> extends StatefulWidget {

在他的State 的initState中,通過(guò)指定的泛型(Type)以及tag獲取到對(duì)應(yīng)的GetController(之前被注入的,或者是剛剛創(chuàng)建的),賦值給State中的controller屬性,然后調(diào)用_subscribeToController,去將當(dāng)前State的setState方法綁定到GetController上

void initState() {
    // _GetBuilderState._currentState = this;
    super.initState();
    widget.initState?.call(this);

    var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);

    if (widget.global) {
      if (isRegistered) {
        if (GetInstance().isPrepared<T>(tag: widget.tag)) {
          _isCreator = true;
        } else {
          _isCreator = false;
        }
        controller = GetInstance().find<T>(tag: widget.tag);
      } else {
        controller = widget.init;
        _isCreator = true;
        GetInstance().put<T>(controller!, tag: widget.tag);
      }
    } else {
      controller = widget.init;
      _isCreator = true;
      controller?.onStart();
    }

    if (widget.filter != null) {
      _filter = widget.filter!(controller!);
    }

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

void getUpdate() {
    if (mounted) setState(() {});
  }

在GetController中,_updaters保存了所有使用了當(dāng)前getController的State刷新回調(diào)

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

當(dāng)需要更新數(shù)據(jù)的時(shí)候,直接在getController中調(diào)用update,這個(gè)時(shí)候就會(huì)將_updaters中的所有回調(diào)函數(shù)都執(zhí)行一遍,這時(shí)候就完成了頁(yè)面刷新

void update([List<Object>? ids, bool condition = true]) {
    if (!condition) {
      return;
    }
    if (ids == null) {
      refresh();
    } else {
      for (final id in ids) {
        refreshGroup(id);
      }
    }
  }

void refresh() {
    assert(_debugAssertNotDisposed());
    _notifyUpdate();
  }

void _notifyUpdate() {
    for (var element in _updaters!) {
      element!();
    }
  }

Obx自動(dòng)刷新,響應(yīng)式狀態(tài)管理器

這種方式只需要關(guān)注于數(shù)據(jù),不用關(guān)心什么時(shí)候刷新的問(wèn)題,因?yàn)樵谛薷耐陻?shù)據(jù)后,頁(yè)面會(huì)自動(dòng)進(jìn)行刷新
使用Obx,第一步要對(duì)數(shù)據(jù)進(jìn)行"obs"修飾,本質(zhì)是將數(shù)據(jù)包裝了一層

final count = 0.obs;
class Rx<T> extends _RxImpl<T> {
  Rx(T initial) : super(initial);

  @override
  dynamic toJson() {
    try {
      return (value as dynamic)?.toJson();
    } on Exception catch (_) {
      throw '$T has not method [toJson]';
    }
  }
}
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {

最終的數(shù)據(jù)會(huì)被包裝成RxNotifier,作用后面再說(shuō).
接下來(lái)該介紹Obx了,我們會(huì)發(fā)現(xiàn)Obx也是繼承自StatefulWidget,所以應(yīng)該可以想到這種方式最后也是通過(guò)調(diào)用State中的setState來(lái)完成頁(yè)面刷新的(至少能感覺(jué)到)

class Obx extends ObxWidget {
abstract class ObxWidget extends StatefulWidget {

在ObxWidget的State中我們又發(fā)現(xiàn)了一個(gè)RxNotifier,上面說(shuō)了數(shù)據(jù)被包裝成了RxNotifier(這里需要注意,目前已經(jīng)有兩個(gè)RxNotifier屬性了,一個(gè)是GetController中數(shù)據(jù)本身被包裝成RxNotifier,一個(gè)是State中的屬性_observer是一個(gè)RxNotifier)
這里我們可以給這兩個(gè)RxNotifier起個(gè)名字,GetController中的叫做dataRxNotifier(D),State中的叫做observerRxNotifier(O),RxNotifier我們可以簡(jiǎn)單理解他就是一個(gè)包裝類,里邊有一個(gè)可以被多次訂閱的Stream(subject),

final _observer = RxNotifier();
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;

然后看initState,在這里O被監(jiān)聽(tīng)了,當(dāng)O發(fā)生變化后調(diào)用_updateTree,也就是setState

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

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

到這里了,我們可以思考下,如果想要頁(yè)面刷新,是不是只需要觸發(fā)_observer(O)的刷新機(jī)制就可以了!!!
然后看State中的build方法

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

static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
    final _observer = RxInterface.proxy;
    RxInterface.proxy = observer;
    final result = builder();
    if (!observer.canUpdate) {
      RxInterface.proxy = _observer;
      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;
  }

builder就是Obx中傳入的builder, observer就是State中的observer,上文我們給它起名字叫做O,這里會(huì)發(fā)現(xiàn)一個(gè)有趣的問(wèn)題,就是在執(zhí)行builder之前,我們替換了RxInterface.proxy為當(dāng)前State中的observer(O),然后執(zhí)行完后又換了回來(lái),看著一頭霧水,其實(shí)這里真的是點(diǎn)睛之筆,所有的不可能在這里全部連接了起來(lái),接著往下看,就會(huì)豁然開(kāi)朗.

在我們不熟練的時(shí)候,有時(shí)候使用Obx,里邊沒(méi)有obs修飾的參數(shù),這個(gè)時(shí)候getx會(huì)報(bào)異常,其實(shí)就是上邊的這段話,所以getx要求,Obx中必須要包含obs修飾的數(shù)據(jù)
接下來(lái)我們就要看builder了,聯(lián)想一下上邊為什么要臨時(shí)替換RxInterface.proxy
builder中,我們一定會(huì)用到的方法是D的value 的getter方法,例如:

Obx(() {
        return Row(
          children: [
            Text(Get
                .find<User>()
                .name
                .value),
          ],
        );
      })

我們調(diào)用了user的name,然后調(diào)用name的value getter(此時(shí)的name是一個(gè)RxNotifier)

class User extends GetxController {
  final name = "小銘".obs;
}

根據(jù)經(jīng)驗(yàn)我們應(yīng)該能想到,應(yīng)該是在這里做了什么,才能保證數(shù)據(jù)修改,可以刷新我么當(dāng)前的頁(yè)面,所以我們進(jìn)入value getter

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

在這里不光是將數(shù)據(jù)返回,還做了一件事RxInterface.proxy?.addListener(subject);
RxInterface.proxy 被使用了,這個(gè)時(shí)候我們是在D中,完全脫離了O,所以RxInterface.proxy這個(gè)屬性的目的就是在D中可以使用O,通過(guò)每次臨時(shí)替換,保證了D與O的對(duì)應(yīng)關(guān)系的準(zhǔn)確性,完美,通過(guò)一個(gè)靜態(tài)變量解決了這種通信問(wèn)題
我們看看addListener這個(gè)里邊做了什么

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);
    }
  }

rxGetx就是我們上文中D的Stream,而這里的subject,就是O的Stream
所以上邊的代碼可以這樣寫

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

數(shù)據(jù)改變后,會(huì)調(diào)用dStream.listen中的回調(diào)函數(shù),回調(diào)函數(shù)又執(zhí)行了oStream.add(類比sink.add),又會(huì)執(zhí)行oStream.listen中的回調(diào),而 oStream.listen中的回調(diào) 正是_updateTree,也就是setState,這里思路一下子就清晰了

所以可以這樣理解,GetX響應(yīng)式狀態(tài)管理器,其實(shí)依賴于兩個(gè)Stream,一個(gè)是數(shù)據(jù)的Stream,一個(gè)是State 的Stream,數(shù)據(jù)改變時(shí)通過(guò)監(jiān)聽(tīng)回調(diào)告訴State中的Stream,State中的Stream改變,才實(shí)現(xiàn)了頁(yè)面的刷新.

但是其實(shí)這里的GetStream并不是真正的Stream,它實(shí)際上是將所有的回調(diào)保存在_onData中

List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];

當(dāng)執(zhí)行add方法的時(shí)候遍歷執(zhí)行_onData中的回調(diào)函數(shù),

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

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

這里如果可以,我覺(jué)得直接建立value和setState的關(guān)系更加省事,說(shuō)實(shí)在這兩個(gè)GetStream真的有點(diǎn)畫蛇添足了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容