GetIt容器管理
GetIt是Dart的一个容器管理开源库,pub地址为:https://pub.flutter-io.cn/packages/get_it。
GetIt特性
- 切换业务代码实现时无需修改对应的UI代码;
- 无需使用context。
原文:
- I was missing the ability to easily switch the implementation for a business object for a mocked one without changing the UI.
- The fact that you need a
BuildContext
to access your objects made it unusable to use them from inside the Business layer.
解决上面提到这种问题的方式有单例方式,或者IoC容器,单例方式的缺点是无法切换实现类(比如从api接口切换到Mock);IoC容器的缺点是大部分IoC的实现方式是基于反射,而Flutter不支持反射(Dart本身是支持反射的,但是Flutter不支持)。
简单实现:注册单例
最简单的方式的代码如下,通过registerSingleton注册一个单例:
final getIt = GetIt.instance;
void setup() {
getIt.registerSingleton<AppModel>(AppModel());
// Alternatively you could write it if you don't like global variables
GetIt.I.registerSingleton<AppModel>(AppModel());
}
去看GetIt里的源码,你会发现registerSingleton的定义如下:
void registerSingleton<T>(
T instance, {
String instanceName,
bool signalsReady,
}) {
_register<T, void, void>(
type: _ServiceFactoryType.constant,
instanceName: instanceName,
instance: instance,
isAsync: false,
shouldSignalReady: signalsReady ?? <T>[] is List<WillSignalReady>);
}
其实是直接调用了_register方法。重要的参数是factoryFunc,该方法可以看作是一个工厂构造方法,其实就是造一个T泛型的对象放到容器里。同时GetIt本身是单例实现的,通过容器_factories或_factoriesByName(不推荐使用,除非一个service存在多种实现类)成员存储已有的对象,如果重复注册会抛出ArgumentError错误。
/// stores all [_ServiceFactory] that get registered by Type
final _factories = Map<Type, _ServiceFactory<dynamic, dynamic, dynamic>>();
/// the ones that get registered by name.
final _factoriesByName =
Map<String, _ServiceFactory<dynamic, dynamic, dynamic>>();
_register方法首先做了业务判断,包括泛型是否匹配,参数校验以及是否重复注册同一对象。之后设置了一个_ServiceFactory类型工厂构造对象,存入到_factories或_factoriesByName中,这样的的好处是可以延迟对象创建的时间。只需要在get的时候才真正创建对象,从_ServiceFactory的getObject方法可以看到。
void _register<T, P1, P2>({
@required _ServiceFactoryType type,
FactoryFunc<T> factoryFunc,
FactoryFuncParam<T, P1, P2> factoryFuncParam,
FactoryFuncAsync<T> factoryFuncAsync,
FactoryFuncParamAsync<T, P1, P2> factoryFuncParamAsync,
T instance,
@required String instanceName,
@required bool isAsync,
Iterable<Type> dependsOn,
@required bool shouldSignalReady,
}) {
throwIf(
(!(const Object() is! T) && (instanceName == null)),
'GetIt: You have to provide either a type or a name. Did you accidentally do `var sl=GetIt.instance();` instead of var sl=GetIt.instance;',
);
throwIf(
(instanceName != null &&
(_factoriesByName.containsKey(instanceName) && !allowReassignment)),
ArgumentError("An object of name $instanceName is already registered"),
);
throwIf(
(instanceName == null &&
_factories.containsKey(T) &&
!allowReassignment),
ArgumentError("Type ${T.toString()} is already registered"));
final serviceFactory = _ServiceFactory<T, P1, P2>(
type,
creationFunction: factoryFunc,
creationFunctionParam: factoryFuncParam,
asyncCreationFunctionParam: factoryFuncParamAsync,
asyncCreationFunction: factoryFuncAsync,
instance: instance,
isAsync: isAsync,
instanceName: instanceName,
shouldSignalReady: shouldSignalReady,
);
if (instanceName == null) {
_factories[T] = serviceFactory;
} else {
_factoriesByName[instanceName] = serviceFactory;
}
// simple Singletons get creates immediately
if (type == _ServiceFactoryType.constant &&
!shouldSignalReady &&
!isAsync &&
(dependsOn?.isEmpty ?? false)) {
serviceFactory.instance = factoryFunc();
serviceFactory._readyCompleter.complete();
return;
}
//...
getObject方法(async版本的有async实现),可以看到根据工厂构造类型,调用了不同的工厂方法创建对象。而且如果对象已经创建过了则直接返回已有对象(除非强制设置alwaysNew属性为true)。
T getObject(dynamic param1, dynamic param2) {
assert(
!(factoryType != _ServiceFactoryType.alwaysNew &&
(param1 != null || param2 != null)),
'You can only pass parameters to factories!');
try {
switch (factoryType) {
case _ServiceFactoryType.alwaysNew:
if (creationFunctionParam != null) {
// param1.runtimeType == param1Type should use 'is' but Dart does
// not support this comparison. For the time being it is therefore
// disabled
// assert(
// param1 == null || param1.runtimeType == param1Type,
// 'Incompatible Type passed as param1\n'
// 'expected: $param1Type actual: ${param1.runtimeType}');
// assert(
// param2 == null || param2.runtimeType == param2Type,
// 'Incompatible Type passed as param2\n'
// 'expected: $param2Type actual: ${param2.runtimeType}');
return creationFunctionParam(param1 as P1, param2 as P2);
} else {
return creationFunction();
}
break;
case _ServiceFactoryType.constant:
return instance as T;
break;
case _ServiceFactoryType.lazy:
if (instance == null) {
instance = creationFunction();
_readyCompleter.complete();
}
return instance as T;
break;
default:
throw (StateError('Impossible factoryType'));
}
} catch (e, s) {
print("Error while creating ${T.toString()}");
print('Stack trace:\n $s');
rethrow;
}
}
延迟单例注册
延迟单例注册的方式和注册单例一样,区别在于此时传递的不是对象,而是一个构造对象的方法。在getObject的时候,如果该实例对象不存在,则会调用构造对象方法生成一个,从而达到按需取用对象的目的。延迟单例注册调用registerLazySingleton方法,registerLazySingleton再调用_register方法,代码如下。
void registerLazySingleton<T>(FactoryFunc<T> func, {String instanceName}) {
_register<T, void, void>(
type: _ServiceFactoryType.lazy,
instanceName: instanceName,
factoryFunc: func,
isAsync: false,
shouldSignalReady: false);
}
可以看到与registerSingleton不同,registerSingleton的参数是instance,即创建好的对象实例,而registerLazySingleton是传递了一个工厂方法factoryFunc用于构造所需对象。
使用容器的示例代码:GitHub代码