「这是我参与2022初次更文挑战的第3天,活动概况检查:2022初次更文挑战」

本文将经过 Getx 的源码剖析 Getx 依靠办理的详细完成,带你一步一步的了解 Getx 的依靠办理原理,从而在开发过程中灵敏运用 Getx 的依靠注入。

之前写了一篇文章介绍 Getx 的集成和运用:Flutter运用结构建立(一)GetX集成及运用详解 ,里面有介绍 Getx 依靠办理的运用。首要包括 注入依靠获取依靠,要害办法如下:

///注入依靠
Get.put();
Get.lazyPut();
Get.putAsync();
Get.create();
///获取依靠
Get.find();

下面将经过这几个办法盯梢源码详细了解 Getx 依靠注入的原理。

Get.put

Get.put 是最简略刺进依靠的办法,它的源码如下:

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

put 办法有四个参数,终究一个 builder 参数被弃用了,前面三个参数在之前的文章也介绍了详细用途:dependency:依靠目标实例;tag:标签,用于区分同一个类型不同实例;permanent:是否永久保存,默以为 false。

put 直接调用了 _insert 办法,将依靠目标 dependency 封装为了 builder 办法传入 _insert 办法的 builder 参数,isSingleton 是否为单例传入的 true_insert 源码如下:

void _insert<S>({
    bool? isSingleton,
    String? name,
    bool permanent = false,
    required InstanceBuilderCallback<S> builder,
    bool fenix = false,
  }) {
    final key = _getKey(S, name);
    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,
      );
    }
  }

_insert 中首要调用 _getKey 获取了保存依靠目标的 key, _getKey 源码很简略,假如 namenull 则直接回来依靠目标的类型称号,假如不为 null 就回来类型称号 + name,这儿的 name 便是 put 办法传入的 tag_getKey 源码如下:

  String _getKey(Type type, String? name) {
    return name == null ? type.toString() : type.toString() + name;
  }

获取到 key 后,判别 _singl 中 key 是否存在,_singl 为 Map 类型,用于保存依靠关系 key 和 _InstanceBuilderFactory 目标:

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

假如 key 不存在,则创立 _InstanceBuilderFactory 目标,存在则获取 _singl 中的 _InstanceBuilderFactory 目标,然后判别是否为 nullisDirty 是否为 true,为 true 则从头创立 _InstanceBuilderFactory 目标并将本来的 _InstanceBuilderFactory 目标传入 lateRemove 参数。

其中 isDirty 为是否等候毁掉,经过盯梢源码发现,该字段只有 markAsDirty 一个办法调用:

 void markAsDirty<S>({String? tag, String? key}) {
    final newKey = key ?? _getKey(S, tag);
    if (_singl.containsKey(newKey)) {
      final dep = _singl[newKey];
      if (dep != null && !dep.permanent) {
        dep.isDirty = true;
      }
    }
  }

该办法经过 key 获取到 _InstanceBuilderFactory 目标 dep,然后判别 dep 不为 nullpermanent 为 false,则将 isDirty 标记为 true,即等候毁掉。

持续盯梢源码发现 markAsDirty 办法是在 reportRouteWillDispose 中调用的,也便是在路由即将毁掉的时分调用,此时更改依靠目标 isDirty 的值。

经过 put 的源码发现 Getx 办理依靠关系便是将依靠目标封装为 _InstanceBuilderFactory 目标经过 key 保存到 Map 中,假如对应的key 值现已存在,且没有标记为等候毁掉,则会忽略 put 操作,否则刺进新的 _InstanceBuilderFactory 目标。

终究传入的依靠目标会被封装到 _InstanceBuilderFactory 目标里再放入到 _singl 的 Map 里,_InstanceBuilderFactory 源码:

class _InstanceBuilderFactory<S> {
  /// Marks the Builder as a single instance.
  /// For reusing [dependency] instead of [builderFunc]
  bool? isSingleton;
  /// When fenix mode is avaliable, when a new instance is need
  /// Instance manager will recreate a new instance of S
  bool fenix;
  /// Stores the actual object instance when [isSingleton]=true.
  S? dependency;
  /// Generates (and regenerates) the instance when [isSingleton]=false.
  /// Usually used by factory methods
  InstanceBuilderCallback<S> builderFunc;
  /// Flag to persist the instance in memory,
  /// without considering `Get.smartManagement`
  bool permanent = false;
  bool isInit = false;
  _InstanceBuilderFactory<S>? lateRemove;
  bool isDirty = false;
  String? tag;
  _InstanceBuilderFactory(
    this.isSingleton,
    this.builderFunc,
    this.permanent,
    this.isInit,
    this.fenix,
    this.tag, {
    this.lateRemove,
  });
  void _showInitLog() {
    if (tag == null) {
      Get.log('Instance "$S" has been created');
    } else {
      Get.log('Instance "$S" has been created with tag "$tag"');
    }
  }
  /// Gets the actual instance by it's [builderFunc] or the persisted instance.
  S getDependency() {
    if (isSingleton!) {
      if (dependency == null) {
        _showInitLog();
        dependency = builderFunc();
      }
      return dependency!;
    } else {
      return builderFunc();
    }
  }
}

_InstanceBuilderFactory 里最要害的便是 getDependency 办法获取依靠,判别是否为单例,假如不为单例则每次都调用 builderFunc 办法,假如为单例则判别 dependency 是否为 null 不为空直接回来,为空则调用 builderFunc 办法 。

builderFunc 办法则是一开始在 put 中传入的 builder,实践为() => dependency 也便是 put 办法传入的依靠目标。

put 办法终究调用了 find 办法并把回来值 return 了回去,find 办法是获取依靠,终究调用了 find 办法,相当于刺进依靠后立刻又获取了依靠,这也是为什么 put 办法是直接传入依靠的实体目标,而废弃了 builder 参数的原因, 由于终究都会在 put 办法内初始化依靠目标。

Get.find

进入 find 办法源码:

  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())"';
    }
  }

经过源码发现 find 的全体逻辑为判别依靠是否注册,假如未注册则抛出反常;假如已注册则从 _singl 中取出依靠,判别取出的依靠 dep 是否为 null ,如为 null 则抛出反常,不为空则调用 _initDependencies 初始化依靠,终究判别初始化依靠的回来值是否为 null ,不为 null 则直接回来,为空则再调用 getDependency 办法获取依靠目标实例。

isRegistered 是怎样判别是否注册的,源码如下:

 bool isRegistered<S>({String? tag}) => _singl.containsKey(_getKey(S, tag));

其实便是判别 key 是否存在,_getKey 办法的完成在上面现已讲过了。

持续盯梢源码剖析 _initDependencies 是怎么初始化依靠的:

  S? _initDependencies<S>({String? name}) {
    final key = _getKey(S, name);
    final isInit = _singl[key]!.isInit;
    S? i;
    if (!isInit) {
      i = _startController<S>(tag: name);
      if (_singl[key]!.isSingleton!) {
        _singl[key]!.isInit = true;
        if (Get.smartManagement != SmartManagement.onlyBuilder) {
          RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
        }
      }
    }
    return i;
  }

首要获取依靠经过 isInit 判别是否现已初始化,isInit 默以为 false,假如未初始化则调用 _startController ,假如现已初始化则这儿直接回来 i 未赋值为 null,持续盯梢 _startController 源码:

  S _startController<S>({String? tag}) {
    final key = _getKey(S, tag);
    final i = _singl[key]!.getDependency() as S;
    if (i is GetLifeCycleBase) {
      i.onStart();
      if (tag == null) {
        Get.log('Instance "$S" has been initialized');
      } else {
        Get.log('Instance "$S" with tag "$tag" has been initialized');
      }
      if (!_singl[key]!.isSingleton!) {
        RouterReportManager.appendRouteByCreate(i);
      }
    }
    return i;
  }

经过 _singl 获取依靠目标,然后判别依靠目标是否为 GetLifeCycleBase 类型,是则调用其 onStart 办法,终究回来依靠目标。

GetLifeCycleBase是一个 mixinGetxController 终究混入了 GetLifeCycleBase ,所以这儿相当于调用了 GetxControlleronStart 办法。

总结: find 办法从 _singl 中查找对应类型和 tag 的依靠,假如依靠未初始化则初始化,已初始化则直接回来。

Get.lazyPut

lazyPut 是推迟初始化,源码如下:

void lazyPut<S>(
    InstanceBuilderCallback<S> builder, {
    String? tag,
    bool? fenix,
    bool permanent = false,
  }) {
    _insert(
      isSingleton: true,
      name: tag,
      permanent: permanent,
      builder: builder,
      fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
    );
  }

put 办法一样,调用的 _insert 办法,区别是依靠不是直接传入的实例目标,而是传入创立实例的 builder 办法, 经过前面的源码剖析知道改办法终究是在 find 办法里调用。而 lazyPut 终究并没有调用 find 办法,所以会在后面第一次运用 find 办法时初始化依靠目标。

Get.putAsync

putAsync 是异步注入依靠,源码如下:

  Future<S> putAsync<S>(
    AsyncInstanceBuilderCallback<S> builder, {
    String? tag,
    bool permanent = false,
  }) async {
    return put<S>(await builder(), tag: tag, permanent: permanent);
  }

实践调用的是 put 办法,经过异步获取 builder 的值然后传入 put 办法。

Get.create

create 源码:

  void create<S>(InstanceBuilderCallback<S> builder,
          {String? tag, bool permanent = true}) =>
      GetInstance().create<S>(builder, tag: tag, permanent: permanent);

create 办法的 permanent 参数默以为 true, 即永久保存,然后调用了 GetInstancecreate 办法,源码如下:

  void create<S>(
    InstanceBuilderCallback<S> builder, {
    String? tag,
    bool permanent = true,
  }) {
    _insert(
      isSingleton: false,
      name: tag,
      builder: builder,
      permanent: permanent,
    );
  }

GetInstancecreate 也是调用的 _insert 办法,区别是 isSingleton 默以为 false, 经过前面的源码剖析知道当 isSingleton 为 false 时,每次 find 时都会从头创立依靠目标 ,所以 create 注入的依靠是不会跟着页面毁掉而移除依靠注入关系,但却会每次调用 find 获取时都从头创立依靠目标。

Get.delete

delete 是用于毁掉依靠,假如运用的是 Getx 的路由办理,则会在页面毁掉时调用该办法而无需手动调用,源码如下:

  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;
      }
    }
  }
  • 首要获取依靠
  • 判别依靠的 permanent 为 true 是永久保存且不是 force 为 false 不是强制删去时直接 return false
  • 判别依靠是否为 GetxServiceMixin 且不是强制删去时直接 return falseGetxService 混入了 GetxServiceMixin , 所以 GetxService 能在运用存活期间永久保存。
  • 判别依靠是否为 GetLifeCycleBase 也便是 GetxController 则调用其 onDelete 办法。
  • 假如 fenix 为 true, 则将当时依靠 dependency 赋值为 null ,isInit 设置为 false,并没有删去 key,所以下次调用 find 办法时会再次调用 builder 创立依靠实例。
  • 假如 lateRemove 不为 null ,则将其赋值为 null,否则将当时依靠关系的 key 从 _singl 中 remove。

总结

经过阅读剖析 Getx 的源码发现, Getx 的依靠办理实质是经过一个 Map 保存依靠关系,当调用 find 办法获取依靠时,再从 Map 中进行查找。

希望能经过本篇文章让你更加深入的了解 Getx 依靠办理的原理,在开发过程中做到灵敏运用 Getx 的依靠注入。