IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter GetX框架状态管理源码原理分析 -> 正文阅读

[移动开发]Flutter GetX框架状态管理源码原理分析

GetX状态管理

优点
性能:GetX 打包后得apk占用大小和运行时得内存占用少
在这里插入图片描述
在这里插入图片描述

效率:语法简洁,并保持了极高得性能,极大缩短开发时间
结构:可以将界面、逻辑、依赖和路由完全解耦,逻辑更清晰。
生态:能够在Android、iOS、web、Linux、windows上用相同的代码运行。
提高开发效率的插件:
Getx_template:一键生成框架模板

在这里插入图片描述

GetX Snippets:代码提示,模板代码

状态管理:简单状态管理器(GetBuilder)、响应式状态管理器(GetX)
GetX响应式状态管理器:
优点:
1.不需要创建StreamControllers
2.不需要为每个变量创建一个SreamBuilder
3.不需要为每个状态创建一个类。
4.当状态发生改变时,只会改变那些真正发生改变的状态,可以更精细化控制,只需要继承statessful即可。

角色:
GetController:负责状态通知和逻辑处理。

在GetxController中,总共有这几种状态,并且,当APP 状态改变时会发生回调,可根据实际情况在这些回调方法中做具体逻辑处理。

可被观察对象:可使用简单写法,直接使用.obs来设置可观察对象,除了基本数据类型也可对自定义类型设置。
例:
Var mInt= 0.obs;
Var mStr= ‘’.obs;
Var mBool=false.obs;
Var mMap=<String,int>{}.obs;
Var TestModel =TestModel().obs;

Widget:UI展示,内部通过Obx 来包裹需要状态管理的Widget,obx相当于观察者,用来监控GetController中被加了.obs的可观察对象。
例:

Obx(() => Text("Clicks: ${c.count}")

GetX使用:
首先将runApp的MaterialApp替换为GetMaterialApp。

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: testPage(),
    );
  }
}

2.创建继承于GetxController的数据类,其内部将需要监听状态的对象使用.obs后缀进行处理。

import 'package:get/get.dart';

class TestState extends GetxController {
  var _testInt=1.obs;
  var _testString="test".obs;

  get pTestInt =>_testInt.value;
  set pTestInt(int value) =>_testInt.value=value;

  get pTestString =>_testString.value;
  set pTestString(String value) =>_testString.value=value;
}

3.创建业务逻辑类,其内部引入数据类的对象,并继承于GetxController。

import 'package:get/get.dart';
import 'state.dart';

class testLogic extends GetxController {
  final _state = TestState();

  TestState getModel(){
    return _state;
  }
}

4.在widget中使用Get.put方法引入GetController类,并使用Obx包裹需要监听状态变更的widget.

class testPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final testLogic logic = Get.put(testLogic());
    return
      Container(
        color: Colors.white,
        child: Obx(() =>
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(logic
                    .getModel()
                    .pTestString),
                Text((logic
                    .getModel()
                    .pTestInt).toString()),
                Text((logic
                    .getModel()
                    .pTestInt++).toString()),
                Text((logic
                    .getModel()
                    .pTestInt++).toString()),
                TextButton(onPressed: () {
                  logic.changeMsg();
                }, child: Text("change"))
              ],
            ))
    );
  }
}

5.正确的使用方式:

class testPage extends StatelessWidget {
  final testLogic logic = Get.put(testLogic());
  @override
  Widget build(BuildContext context) {

    return
      Container(
        color: Colors.white,
        child:  Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Obx(()=>Text(logic
                .getModel()
                .pTestString)),
            Obx(()=> Text((logic
                .getModel()
                .pTestInt++).toString())),
            TextButton(onPressed: () {
              logic.changeMsg();
            }, child: Text("change"))
          ],
        )
    );
  }
}

状态更精细化的控制Workers:
在这里插入图片描述

简单状态管理器:Get有一个极其轻巧简单的状态管理器,它不使用ChangeNotifier,可以满足特别是对Flutter新手的需求,而且不会给大型应用带来问题。
优点:

1.只更新需要更新的部件
2.GetBuilder存在initState,可以从控制器调用此事件,不需要通过StatefulWidget中的initState做初始化工作。
3.Get只存储GetBuilder创建者的ID,内存消耗低,根据该ID更新GetBuilder.

使用:
1.在WIdget中使用GetBuilder包裹需要监听状态的组件

GetBuilder<Controller>(
init: Controller(), // 首次启动
builder: (s) => Text(
'${s.counter}',
2.在GetController中,使用updata()更新组件.
), 

源码分析:

注册环节:

final testLogic logic = Get.put(testLogic());

1.首先进入Get.put方法看在其内部做了什么处理,发现在这一步调用了 GetInstance().put方法并将我们传入的GetController作为参数传递进去。

S put<S>(S dependency,
        {String tag,
        bool permanent = false,
        InstanceBuilderCallback<S> builder}) =>
    GetInstance().put<S>(dependency, tag: tag, permanent: permanent);

2.进入 GetInstance().put方法,这一步最终调用了insert方法

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

3.在insert方法中,会通过一个map,_singl将我们传递进来的GetController做一个缓存处理。当我们在重新进入使用GetX的页面以及通过get.find.to方法去获取实例时会优先从缓存中去获取

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


void _insert<S>({
  bool isSingleton,
  String name,
  bool permanent = false,
  InstanceBuilderCallback<S> builder,
  bool fenix = false,
}) {
  assert(builder != null);
  final key = _getKey(S, name);
  _singl.putIfAbsent(
    key,
    () => _InstanceBuilderFactory<S>(
      isSingleton,
      builder,
      permanent,
      false,
      fenix,
      name,
    ),
  );
}

_singl清除调用时机:

bool reset({bool clearFactory = true, bool clearRouteBindings = true}) {
  //  if (clearFactory) _factory.clear();
  if (clearRouteBindings) _routesKey.clear();
  _singl.clear();
  return true;
}

通过此方法可以看到在使用reset时会对该缓存map做清除处理,并且如果需要清除路由绑定,路由key也一并会被清除

/// Clears all registered instances (and/or tags).
/// Even the persistent ones.
///
/// - [clearFactory] clears the callbacks registered by [Get.lazyPut()]
/// - [clearRouteBindings] clears Instances associated with Routes when using
///   [GetMaterialApp].
bool reset({bool clearFactory = true, bool clearRouteBindings = true}) =>
    GetInstance().reset(
        clearFactory: clearFactory, clearRouteBindings: clearRouteBindings);

继续追踪,可以看到此方法的调用时机和Get.lazyPut有关,而此方法调用的时机则是当Get.find首次调用时会被触发。

而_single.remove方法,也是由Inst控制。

/// Deletes the Instance<[S]>, cleaning the memory and closes any open
/// controllers ([DisposableInterface]).
///
/// - [tag] Optional "tag" used to register the Instance
/// - [force] Will delete an Instance even if marked as [permanent].
Future<bool> delete<S>({String tag, bool force = false}) async =>
    GetInstance().delete<S>(tag: tag, force: force);

使用:
当我们初始化完成之后,需要看下观察者Obx是如何完成任务的。

Obx(()=>Text(logic
    .getModel()
    .pTestString)),


class Obx extends ObxWidget {
  final WidgetCallback builder;

  const Obx(this.builder);

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

继续看它的父类,发现是继承于一个有状态的widget,

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

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

  @protected
  Widget build();
}

class _ObxState extends State<ObxWidget> {
  RxInterface _observer;
  StreamSubscription subs;

 在这里完成了_observer 的初始化
  _ObxState() {
    _observer = RxNotifier();
  }

  @override
  void initState() {
  _observer进行注册监听,当变化产生时_updateTree会被调用
    subs = _observer.listen(_updateTree);
    super.initState();
  }

在该方法内刷新界面

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

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

  被传递进来的widget的build方法被执行调用
  Widget get notifyChilds {
    final observer = RxInterface.proxy;
    RxInterface.proxy = _observer;
    final result = widget.build();
    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;
  }

 当_updateTree方法被调用时,最终会走到这里去调用notifyChilds方法
  @override
  Widget build(BuildContext context) => notifyChilds;
}

_observer 的作用
为什么我们的Obx可以接收到被观察者的状态发生了改变,重点需要看_observer ,做了什么。

首先看_observer 它是一个RxInterface类型的接口,其内部定义了多种方法,如添加被观察者和注册观察者

abstract class RxInterface<T> {
  RxInterface([T initial]);

  bool get canUpdate;

  /// Adds a listener to stream
  void addListener(GetStream<T> rxGetx);

  /// Close the Rx Variable
  void close();

  static RxInterface proxy;

  /// Calls [callback] with current value, when the value changes.
  StreamSubscription<T> listen(void Function(T event) onData,
      {Function onError, void Function() onDone, bool cancelOnError});
}

但它的初始化在其中完成

_observer = RxNotifier();
进入RxNotifier,可以发现它继承了NotifyManager,NotifyManager是一个mixin ,它将RxInterface中定义的方法做了重新实现。
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.
  添加被观察者,被观察者为subject ,是一个Stream
  void addListener(GetStream<T> rxGetx) {
    if (!_subscriptions.containsKey(rxGetx)) {
      final subs = rxGetx.listen(subject.add);
      final listSubscriptions =
          _subscriptions[rxGetx] ??= <StreamSubscription>[];
      listSubscriptions.add(subs);
    }
  }
//注册观察者,我们在ObxWidget中添加的_updateTree实际上是被传递到了这里,通过subject,完成了流的注册和监听

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

//此时_updateTree被传递进到liseten方法中,并实例化了一个LightSubscription放到了订阅队列中,继续看该方法处理了什么
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;
}


@override
void onData(OnData<T> handleData) => _data = handleData;

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


//该add方法是GetStream中得方法,也就是说当subject得add方法被调用时,notifyData就会被触发。
void add(T event) {
  assert(!isClosed, 'You cannot add event to closed Stream');
  _value = event;
  _notifyData(event);
}




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

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

我们在使用GetX时都使用的是可被观察的对象,都携带了.obs,以测试案例为例,

class TestState extends GetxController {
  var _testInt=1.obs;
  var _testString="test".obs;

  get pTestInt =>_testInt.value;
  set pTestInt(int value) =>_testInt.value=value;

  get pTestString =>_testString.value;
  set pTestString(String value) =>_testString.value=value;
}

T get value {
  if (RxInterface.proxy != null) {
    RxInterface.proxy.addListener(subject);
  }
  return _value;
}

当我们调用了get方法去获取数据时,可以发现调用了RxInterface.proxy.addListener方法,它是一个静态的接口方法调用,因为在上面有说到在我们使用Obx时mixin NotifyManager对方法进行了重新,
所以在此处使用addListener方法时,其实就是将该subject作为观察者去监听值得改变。最后将其放入到订阅表中去。

GetStream<T> subject = GetStream<T>();

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

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

/// Returns the current [value]
T get value {
  if (RxInterface.proxy != null) {
    RxInterface.proxy.addListener(subject);
  }
  return _value;
}

static RxInterface proxy;
//是一个静态得RxInterface对象, RxInterface.proxy.addListener(subject);就是将当前Obx所对应得subject放到了这个静态得RxInterface中做了缓存,并且在这个静态全局得RxInterface中,使用addListener完成了监听工作。此处传入得subject还是Obx所拥有得——observer所拥有得subject

继续查看,set方法则是将新值替换旧值,并缓存,如果发现值被改变,则调用subject.add(_value);方法触发之前所注册的观察者,从而触发widget的状态的改变,最终完成状态改变。

set value(T val) {
  if (_value == val && !firstRebuild) return;
  firstRebuild = false;
  _value = val;
  subject.add(_value);
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-03 13:09:04  更:2021-12-03 13:10:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:50:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码