「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」
本文将通过 Getx 的源码剖析 Getx 依赖管理的具体实现,带你一步一步的了解 Getx 的依赖管理原理,从而在开发过程中灵活使用 Getx 的依赖注入。
之前写了一篇文章介绍 Getx 的集成和使用:Flutter应用框架搭建(一)GetX集成及使用详解 ,里面有介绍 Getx 依赖管理的使用。主要包括 注入依赖
和 获取依赖
,关键方法如下:
1 | ///注入依赖 |
下面将通过这几个方法跟踪源码详细了解 Getx 依赖注入的原理。
Get.put
Get.put
是最简单插入依赖的方法,它的源码如下:
1 | S put<S>( |
put
方法有四个参数,最后一个 builder 参数被弃用了,前面三个参数在之前的文章也介绍了具体用途:dependency:依赖对象实例;tag:标签,用于区分同一个类型不同实例;permanent:是否永久保留,默认为 false。
put
直接调用了 _insert
方法,将依赖对象 dependency
封装为了 builder
方法传入 _insert
方法的 builder
参数,isSingleton
是否为单例传入的 true
, _insert
源码如下:
1 | void _insert<S>({ |
_insert
中首先调用 _getKey
获取了保存依赖对象的 key, _getKey
源码很简单,如果 name
为 null
则直接返回依赖对象的类型名称,如果不为 null
就返回类型名称 + name
,这里的 name
就是 put
方法传入的 tag
,_getKey
源码如下:
1 | String _getKey(Type type, String? name) { |
获取到 key 后,判断 _singl
中 key 是否存在,_singl
为 Map 类型,用于保存依赖关系 key 和 _InstanceBuilderFactory
对象:
1 | static final Map<String, _InstanceBuilderFactory> _singl = {}; |
如果 key 不存在,则创建 _InstanceBuilderFactory
对象,存在则获取 _singl
中的 _InstanceBuilderFactory
对象,然后判断是否为 null
且 isDirty
是否为 true,为 true 则重新创建 _InstanceBuilderFactory
对象并将原来的 _InstanceBuilderFactory
对象传入 lateRemove
参数。
其中 isDirty
为是否等待销毁,通过跟踪源码发现,该字段只有 markAsDirty
一个方法调用:
1 | void markAsDirty<S>({String? tag, String? key}) { |
该方法通过 key 获取到 _InstanceBuilderFactory
对象 dep,然后判断 dep 不为 null
且 permanent
为 false,则将 isDirty
标记为 true,即等待销毁。
继续跟踪源码发现 markAsDirty
方法是在 reportRouteWillDispose
中调用的,也就是在路由即将销毁的时候调用,此时更改依赖对象 isDirty
的值。
通过 put 的源码发现 Getx 管理依赖关系就是将依赖对象封装为 _InstanceBuilderFactory
对象通过 key 保存到 Map 中,如果对应的key 值已经存在,且没有标记为等待销毁,则会忽略 put 操作,否则插入新的 _InstanceBuilderFactory
对象。
最终传入的依赖对象会被封装到 _InstanceBuilderFactory
对象里再放入到 _singl
的 Map 里,_InstanceBuilderFactory
源码:
1 | class _InstanceBuilderFactory<S> { |
_InstanceBuilderFactory
里最关键的就是 getDependency
方法获取依赖,判断是否为单例,如果不为单例则每次都调用 builderFunc
方法,如果为单例则判断 dependency
是否为 null
不为空直接返回,为空则调用 builderFunc
方法 。
builderFunc
方法则是一开始在 put
中传入的 builder
,实际为() => dependency
也就是 put
方法传入的依赖对象。
put
方法最后调用了 find
方法并把返回值 return
了回去,find
方法是获取依赖,最后调用了 find
方法,相当于插入依赖后马上又获取了依赖,这也是为什么 put
方法是直接传入依赖的实体对象,而废弃了 builder
参数的原因, 因为最终都会在 put 方法内初始化依赖对象。
Get.find
进入 find
方法源码:
1 | S find<S>({String? tag}) { |
通过源码发现 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
是一个 mixin
而 GetxController
最终混入了 GetLifeCycleBase
,所以这里相当于调用了 GetxController
的 onStart
方法。
总结: 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, 即永久保留,然后调用了 GetInstance
的 create
方法,源码如下:
1 | void create<S>( InstanceBuilderCallback<S> builder, { String? tag, bool permanent = true, }) { _insert( isSingleton: false, name: tag, builder: builder, permanent: permanent, ); } |
GetInstance
的 create
也是调用的 _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 false
。GetxService
混入了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 的依赖注入。