「这是我参与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
源码很简略,假如 name
为 null
则直接回来依靠目标的类型称号,假如不为 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
目标,然后判别是否为 null
且 isDirty
是否为 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 不为 null
且 permanent
为 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
是一个 mixin
而 GetxController
终究混入了 GetLifeCycleBase
,所以这儿相当于调用了 GetxController
的 onStart
办法。
总结: 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, 即永久保存,然后调用了 GetInstance
的 create
办法,源码如下:
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 的路由办理,则会在页面毁掉时调用该办法而无需手动调用,源码如下:
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 的依靠注入。