0%

Flutter GetX 之依赖详解

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

本文将通过 Getx 的源码剖析 Getx 依赖管理的具体实现,带你一步一步的了解 Getx 的依赖管理原理,从而在开发过程中灵活使用 Getx 的依赖注入。

之前写了一篇文章介绍 Getx 的集成和使用:Flutter应用框架搭建(一)GetX集成及使用详解 ,里面有介绍 Getx 依赖管理的使用。主要包括 注入依赖获取依赖,关键方法如下:

1
2
3
4
5
6
7
8
///注入依赖
Get.put();
Get.lazyPut();
Get.putAsync();
Get.create();

///获取依赖
Get.find();

下面将通过这几个方法跟踪源码详细了解 Getx 依赖注入的原理。

Get.put

Get.put 是最简单插入依赖的方法,它的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 源码如下:

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

获取到 key 后,判断 _singl 中 key 是否存在,_singl 为 Map 类型,用于保存依赖关系 key 和 _InstanceBuilderFactory 对象:

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

如果 key 不存在,则创建 _InstanceBuilderFactory 对象,存在则获取 _singl 中的 _InstanceBuilderFactory 对象,然后判断是否为 nullisDirty 是否为 true,为 true 则重新创建 _InstanceBuilderFactory 对象并将原来的 _InstanceBuilderFactory 对象传入 lateRemove 参数。

其中 isDirty 为是否等待销毁,通过跟踪源码发现,该字段只有 markAsDirty 一个方法调用:

1
2
3
4
5
6
7
8
9
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 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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 方法源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 是怎么判断是否注册的,源码如下:

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

其实就是判断 key 是否存在,_getKey 方法的实现在上面已经讲过了。

继续跟踪源码分析 _initDependencies 是如何初始化依赖的:

1
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 源码:

1
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 是延迟初始化,源码如下:

1
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 是异步注入依赖,源码如下:

1
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 源码:

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

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

1
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 的路由管理,则会在页面销毁时调用该方法而无需手动调用,源码如下:

1
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 的依赖注入。

 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!