Flutter圈子Android开发Flutter中文社区

GetIt容器管理

2020-05-15  本文已影响0人  岛上码农

GetIt是Dart的一个容器管理开源库,pub地址为:https://pub.flutter-io.cn/packages/get_it

GetIt特性

  1. 切换业务代码实现时无需修改对应的UI代码;
  2. 无需使用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代码

上一篇 下一篇

猜你喜欢

热点阅读