往期列表:
GetX为我们开发Flutter提供了三大功能:状态管理、依赖管理、路由管理,以及一些辅助工具方法。
我们本章主要追寻源码,去学习一下GetX中的依赖管理实现。
1. 简单用法
通过GetX的官方文档介绍,我们知道:在GetX中共享一个实例,是通过如下方式调用:
/// 只有当第一次使用Get.find<ApiMock>时,ApiMock才会被调用。
Get.put<ApiMock>(ApiMock());
/// 只有当第一次使用Get.find<ApiMock>时,ApiMock才会被调用。
Get.lazyPut<ApiMock>(() => ApiMock());
/// 如果你想注册一个异步实例,你可以使用Get.putAsync
Get.putAsync<YourAsyncClass>(() async => await YourAsyncClass())
Get.Create<SomeClass>(() => SomeClass());
// 获取已经存储的实例
final controller = Get.find<Controller>();
// 或者
Controller controller = Get.find();
/// 移除一个实例
/// 通常你不需要这样做,因为GetX已经删除了未使用的控制器。
Get.delete<Controller>();
以上方法就是GetX中存放实例、获取实例的方式。
下面我们看一下Get的类结构:
下面我们看一下具体源码:
// get_main.dart
class _GetImpl extends GetInterface {}
final Get = _GetImpl();
abstract class GetInterface {
SmartManagement smartManagement = SmartManagement.full;
String reference;
bool isLogEnable = true;
LogWriterCallback log = defaultLogWriterCallback;
}
extension Inst on GetInterface {
void lazyPut<S>(InstanceBuilderCallback<S> builder, {String tag, bool fenix = false}) {
GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
}
void printInstanceStack() {
GetInstance().printInstanceStack();
}
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,{String tag, bool permanent = false}) async => GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
void create<S>(InstanceBuilderCallback<S> builder, {String tag, bool permanent = true}) => GetInstance().create<S>(builder, tag: tag, permanent: permanent);
S find<S>({String tag}) => GetInstance().find<S>(tag: tag);
S put<S>(S dependency,
{String tag,
bool permanent = false,
InstanceBuilderCallback<S> builder}) => GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
bool reset({bool clearFactory = true, bool clearRouteBindings = true}) => GetInstance().reset(clearFactory: clearFactory, clearRouteBindings: clearRouteBindings);
Future<bool> delete<S>({String tag, bool force = false}) async =>
GetInstance().delete<S>(tag: tag, force: force);
bool isPrepared<S>({String tag}) => GetInstance().isPrepared<S>(tag: tag);
}
结合上图以及源码可知,其实Get.put等方法的真正实现是**GetInterface()**对象。看到这个调用方式,我们基本可以猜测GetInstance()必然是一个工厂构造方法,然后以单例的方式提供功能。
2. GetInstance()的实现
因为GetInstance的代码较长,我们将GetInstance的功能分为三个部分学习:添加实例、查找实例、辅助功能。
2.1. 添加实例
GetInstance提供了多种方式添加实例,主要分为:异步、同步、懒加载、注入、创建。
2.1.1. 异步添加
在GetInstance中,**putAsync()**方法提供了异步添加的功能,源码如下:
// builder类型
typedef AsyncInstanceBuilderCallback<S> = Future<S> Function();
Future<S> putAsync<S>(
AsyncInstanceBuilderCallback<S> builder, {
String tag,
bool permanent = false,
}) async {
return put<S>(await builder(), tag: tag, permanent: permanent);
}
由源码可知,putAsync()方法做了两件事情:
- 使用await获取执行builder(),获取异步结果。
- 将入参以及builder()结果传递给put方法。
由此可知,具体的添加逻辑,都是在put()方法中完成的。
2.1.2. 同步添加
在GetInstance中,**put()**方法提供了同步添加的功能,源码如下:
S put<S>(
S dependency, { String tag, bool permanent = false,}) {
_insert(
isSingleton: true, name: tag, permanent: permanent,
builder: (() => dependency));
return find<S>(tag: tag);
}
根据源码可见,put()方法调用了**_insert()**方法,并做了三件事情: * 转发参数:dependency、tag、permanent。 * 将插入类型标记为单例:isSingleton=true。 * 将dependency转化为builder的方法调用。
做完上述三件事情以后,通过调用**find()**方法,返回了插入的实例。
这里移除了一个builder参数,此参数官方已经废弃。
我们继续跟进**_insert()**方法,源码如下:
void _insert<S>({
bool isSingleton, String name, bool permanent = false,
InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
final key = _getKey(S, name);
_singl.putIfAbsent(
key,
() => _InstanceBuilderFactory<S>(
isSingleton, builder, permanent,
false, fenix, name,
),
);
}
根据源码可见,_insert()方法的builder入参为:InstanceBuilderCallback类型,他的定义如下:
typedef InstanceBuilderCallback<S> = S Function();
由此可知,builder只是简单的一个返回对象S的方法。这里没有直接入参S,而以builder的方式进行入参,是为了增强_insert()方法的通用性,使其在懒加载的方式中也可以使用。
在_insert()方法中,一共做了两件事情:根据泛型S以及tag生成key、将入参存放到map中。
- 根据泛型S以及入参tag,生成key。源码如下:
String _getKey(Type type, String name) {
return name == null ? type.toString() : type.toString() + name;
}
- 将入参组织成key-value的形式,存放到map中。 _singl是GetInstance类中的静态Map类型的成员变量,定义如下:
static final Map<String, _InstanceBuilderFactory> _singl = {};
put()方法调用_insert()方式时,传入了四个参数,忽略了fenix参数,则此参数默认为false。
在组合_InstanceBuilderFactory的时候,他需要6个参数,这里传入了四个put()方法透传过来的参数、一个默认fenix=false、一个直接传入的isInit=false。
所以从put()方法到保存_InstanceBuilderFactory的时候,一共使用了两个默认参数:fenix=false、isInit=false。
我们暂且记下,在后续分析查找和生命周期的时候在分析这两个参数的作用。
2.1.3. 懒加载方式添加
在GetInstance中,**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,
);
}
由源码可知,lazyPut()方法做了两个事情:
- 标记实例为单例:isSingleton=true。
- 如果fenix没有传入,则根据Get.smartManagement的值确定fenix的值。
由以上可知lazyPut()方法与put()方法的区别有以下几点:
- put()返回了实例,而lazyPut()没有返回值。这是因为懒加载的方式在当前是没有实例的。而put()中为了复用_insert(),只是简单的使用入参实例构建了builder。
- put()调用_insert()的时候,没有传入fenix,而lazyPut()会根据smartManagement来生成默认fenix。关于fenix的作用,我们后续再讨论。
2.1.4. 注入的方式添加
在GetInstance中,**injector()**方法提供了注入的方式添加实例的功能,源码如下:
void injector<S>(
InjectorBuilderCallback<S> fn, {
String tag, bool fenix = false,
}) {
lazyPut(() => fn(this),
tag: tag, fenix: fenix,
);
}
由源码可知,injector()中的fn参数不同于lazyPut方法,它需要GetInstance对象作为入参,由此可知injector()尝试提供多GetInstance实例的方式管理对象。只是根据上文我们知道GetInstance使用的是单例模式,所以暂时此方法并不能达到多实例的目的。
2.1.5. create方式添加
在GetInstance中,**create()**方法提供了另外一种同步添加实例的方式,源码如下:
void create<S>(
InstanceBuilderCallback<S> builder, {
String tag, bool permanent = true,
}) {
_insert(
isSingleton: false,
name: tag,
builder: builder,
permanent: permanent,
);
}
根据源码可知,create方法默认permanent=true,并且没有使用单例(isSingleton=false)。其他部分和put相同。
现在我们总结一下put、lazyPut、create三种添加方式的区别:
| put() | lazyPut() | create() | |
|---|---|---|---|
| 是否返回实例 | 是 | 否 | 否 |
| 是否为单例 | true | true | false |
| 默认permanent | false | false | true |
| 默认fenix | false | 根据smartManagement确定 | false |
2.2. 查找实例
**find()**方法提供了实例查找的功能,他的源码如下:
S find<S>({String tag}) {
final key = _getKey(S, tag);
if (isRegistered<S>(tag: tag)) {
if (_singl[key] == 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 ?? _singl[key].getDependency() as S;
} else {
throw '报错';
}
}
根据源码可知,find()做了如下的事情:
- 根据类型和tag获取key(他这里获取key的方法可以后置到if中会更高效)
- 判断当前key下的实例是否已经注册,如果没有注册则报错,否则继续。
- 查看_single对象中的key是否为null,如果为null则报错,否则继续。
- 调用_initDependencies()进行初始化。
- 如果_initDependencies()返回为空,则使用实例中的getDependency()进行初始化。
前三步比较好理解,第四步和第五步我们分别看一下他们的源码实现。
我们先看第五步getDependency()的实现。
2.2.1. _singl[key].getDependency()的实现
根据上文可知_singl对象的value类型为:_InstanceBuilderFactory。他提供了**getDependency()**方法,具体源码如下:
S getDependency() {
if (isSingleton) {
if (dependency == null) {
_showInitLog();
dependency = builderFunc();
}
return dependency;
} else {
return builderFunc();
}
}
由源码我们知道:
- 如果是单例,则检查当前持有的实例dependency是否为null
- 如果为null,则打印log,并执行传入的builder()方法构建实例。
- 否则,直接返回实例dependency
- 如果不是单例,直接执行builder()方法进行构建新的实例。
这里的builder()方法就是:在**lazyPut()**中传入的构建方法,在put()中构建的使用dependency作为返回参数方法。
put、lazyPut、create三种添加方式的区别:
| put() | lazyPut() | create() | |
|---|---|---|---|
| 是否返回实例 | 是 | 否 | 否 |
| 是否为单例 | true | true | false |
| 默认permanent | false | false | true |
| 默认fenix | false | 根据smartManagement确定 | false |
根据上表我们知道,在put()、lazyPut()中都会是单例,而以create()方式添加的实例,将会重复调用builder()进行生成。
2.2.1. _initDependencies()的实现
_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) {
_registerRouteInstance<S>(tag: name);
}
}
}
return i;
}
当_singl[key].isInit=false,会调用**_startController()**方法尝试初始化controller的生命周期。
根据上文对添加实例的学习我们知道,_singl[key].isInit参数在添加完成后,都为false。
那么,在第一次调用的时候,必然会执行_startController(),_startController()方法的实现如下:
S _startController<S>({String tag}) {
final key = _getKey(S, tag);
final i = _singl[key].getDependency() as S;
if (i is GetLifeCycleBase) {
if (i.onStart != null) {
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 && i.onDelete != null) {
_routesByCreate[Get.reference] ??= HashSet<Function>();
_routesByCreate[Get.reference].add(i.onDelete);
}
}
return i;
}
我们之前分析过getDependency()方法,他会返回dependency对象,也就是我们通过put添加的实例。
如果dependency是GetLifeCycleBase对象,则执行他的生命周期。在前文中我们分析GetxController的时候知道,他的继承关系如图:
所以综述前文可知,在GetBuilder中的initState的时候会调用find(),此时会触发GetxController的onStart()方法,再依次触发onInit()方法。
根据put()、create()差别的表我们知道,只有create()方法添加的实例不是单例,其他方法添加的实例都是单例。
所以以create()方式添加的实例,会将此实例的onDelete()方法添加到路由管理列表中,用来回收实例。后续在学习路由管理源码的时候,再详细看一下他的管理策略。
我们再回过头来看_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) {
_registerRouteInstance<S>(tag: name);
}
}
}
return i;
}
在执行完生命周期后,如果当前实例是单例(除了create()方式添加的实例),会将isInit标记为true,并且如果当前smartManagement!=onlyBuilder(默认为full),则将当前key以及路由信息添加到_routesKey对象中。
至于路由的管理,我们在后续文章中再一点点学习。
至此我们知道,在find()执行如下流程:
- 先查找对象
- 再尝试初始化声明周期。
- 如果有生命周期,再将create()添加的实例保存到_routesByCreate中进行管理。
- 如果不是通过create()方式添加的实例(isSingleton为true),并且当前smartManagement为onlyBuilder,则将当前key添加到_routesKey对象中进行管理。
2.3. 辅助功能
GetInstance对象还提供了一些辅助,主要分三类:删除与重载、路由管理、纯辅助。
2.3.1. 删除与重载实例
2.3.1.1. reset()方法
bool reset({bool clearFactory = true, bool clearRouteBindings = true}) {
// if (clearFactory) _factory.clear();
if (clearRouteBindings) _routesKey.clear();
_singl.clear();
return true;
}
由源码可知,reset()方法的clearFactory参数其实是无效的。
reset()方法会直接清除所有的实例,并且不触发声明周期。
2.3.1.2. delete()方法
bool delete<S>({String tag, String key, bool force = false}) {
final newKey = key ?? _getKey(S, tag);
if (!_singl.containsKey(newKey)) {
return false;
}
final builder = _singl[newKey];
if (builder.permanent && !force) {
return false;
}
final i = builder.dependency;
if (i is GetxServiceMixin && !force) {
return false;
}
if (i is GetLifeCycleBase && i.onDelete != null) {
i.onDelete();
}
if (builder.fenix) {
builder.dependency = null;
builder.isInit = false;
} else {
_singl.remove(newKey);
}
return true;
}
从delete()方法的源码可知,他的删除逻辑为:
- 如果_singl中不包含此key,则直接返回false。
- 如果对应实例是持久的(permanent=true),并且不是强制删除(force=false),则返回false。create()方式添加的实例,默认permanent=true。
- 如果对应实例是GetxServiceMixin类型,并且不是强制删除,则直接返回false。
- 如果对应实例是生命周期类型,并且onDelete不为null,则调用他的onDelete,执行生命周期。
- 如果他的fenix为true,则设置dependency=null,isInit=false。只有通过lazyPut()方式添加的实例,可以设置fenix,而如果没有设置fenix,则会根据smartManagement是否为keepFactory,设置默认fenix。所以,如果lazyPut()方式添加实例,并且设置了fenix,则在delete的时候不会删除依赖的生成器,只会清除依赖,在下次find()的时候,重新调用生成器获取依赖。
- 如果fenix为false,则直接移除实例。
- 所以如果入参force=true,只会影响permanent=true的实例删除和GetxServiceMixin类型的实例删除,而不会影响fenix=true的实例删除策略。
2.3.1.3. reloadAll()方法
void reloadAll({bool force = false}) {
_singl.forEach((key, value) {
if (value.permanent && !force) {
Get.log('Instance "$key" is permanent. Skipping reload');
} else {
value.dependency = null;
value.isInit = false;
Get.log('Instance "$key" was reloaded.');
}
});
}
由源码可知,**reloadAll()**方法会遍历所有实例,当force=false时,跳过permanent=true的实例。其他所有实例都清空依赖,重置isInit=false。
reload()方法和reloadAll()方法类似,只是重载指定key的依赖,这里不再赘述。
2.3.2. 路由管理
GetInstance类中只提供了一个方法用于配合路由管理,源码如下:
void removeDependencyByRoute(String routeName) {
final keysToRemove = <String>[];
_routesKey.forEach((key, value) {
if (value == routeName) {
keysToRemove.add(key);
}
});
if (_routesByCreate.containsKey(routeName)) {
for (final onClose in _routesByCreate[routeName]) {
if (onClose != null) {
onClose();
}
}
_routesByCreate[routeName].clear();
_routesByCreate.remove(routeName);
}
for (final element in keysToRemove) {
delete(key: element);
}
for (final element in keysToRemove) {
_routesKey?.remove(element);
}
keysToRemove.clear();
}
上述源码的执行逻辑为:
- 从_routesKey中遍历获取指定routeName的keys。根据之前对_registerRouteInstance()方法的分析我们知道,当查找实例的时候,如果当前实例是单例,并且smartManagement!=onlyBuilder(默认为full),则会以实例key为key,当前路由为value存储到_routesKey中。
- 如果_routesByCreate中包含当前routeName,则循环调用当前绑定的onClose()生命周期,并移除他们。在前文中知道,只有当新增实例为GetLifeCycleBase类型,并且通过create()方式添加的实例(isSingleton=false)才会将对象的onDelete()方法添加到_routesByCreate中。所以这里会触发执行那些不是单例的对象的onDelete()生命周期。之所以在这里删除,是因为不是单例的对象,并不会在_singl中管理,_singl中只是对依赖的生成管理,以及对单例管理。
- 对查找到的所有keys执行delete操作,delete()方法我们前面已经学习过,这里不再赘述。
- 最后将_routesKey中的对应keys删除,并清空keys。
2.3.3. 纯辅助
GetInstance还提供了一些纯辅助方法,如下:
// 提供GetInstance()()的调用方式?
T call<T>()
// 打印当前在_routeKey中管理的内容,一个routeKey可能对应多个实例Key
void printInstanceStack()
// 是否已经注册
bool isRegistered<S>({String tag})
// 是否已经准备好:只有当包含指定实例,并且实例还没有被初始化,则为准备好。以create()方式添加的实例必然返回false,以put方式添加的实例也必然返回false,以lazyPut()方式添加的实例,在没有被find()之前,此方法会返回true。
bool isPrepared<S>({String tag})
3. 总结
Get.put()类似的调用,都会转发到GetInstance()对象中,此对象使用了工厂构造方法,会始终保持单例。
GetInstance类中提供了添加、删除、查找依赖的方式。
同时在查找或删除依赖的过程中,会根据是否是生命周期对象而触发相应生命周期方法。
在查找对象的过程中,会根据一些条件,将实例对象添加到_routesKey、_routesByCreate中,配合路由管理,动态回收依赖。
三种添加方法的区别如图:
| put() | lazyPut() | create() | |
|---|---|---|---|
| 是否返回实例 | 是 | 否 | 否 |
| 是否为单例 | true | true | false |
| 默认permanent | false | false | true |
| 默认fenix | false | 根据smartManagement确定 | false |
create()方法添加的依赖比较特殊,他不是单例形式,所以他的onDelete()生命周期依靠route的remove时机触发。他默认是持久的,所以在delete()删除时,如果没有指定force,则不会真正删除。
通过lazyPut()方式添加的实例,是单例,所以在delete的时候,会自动触发生命周期。但是如果当前smartManagement为keepFactory的方式,则默认delete()的时候不会删除依赖构造方法,只是删除实例,并在下次find()的时候,重新初始化实例。