一、布景

在做Flutter侧事务开发时,不同同学会担任不同的事务模块,也都会运用一些类似于登录,跳转等非事务的根底功用,假如不把这些功用笼统出来一致管理的话,事务之间会耦合严重,不便于后续事务的拆分和演进,基于此完成服务解耦功用,类似于Java中的SPI

二、怎么做

服务解耦便是需求将笼统和完成别离,也便是详细的事务咱们需求笼统成一个接口,详细的事务来完成这个接口,最主要的是咱们暴露给事务方的需求是笼统也便是接口,隐藏咱们完成类的逻辑,那么就可看出来需求处理以下两个问题:

  • 笼统 → 完成,怎么绑定
  • 事务方怎么获取

详细完成咱们能够参照Android处理此问题的办法,能够运用注解+代码生成的办法来完成,dart中也供给了注解;

那么以上两步的处理方案便是绑定能够运用注解来做
事务方怎么获取能够经过编译期代码生成,一致的ServiceProvider来供给服务。

2.1 笼统和服务的关联

下文都以登录为比如:

咱们先将服务的接口和完成别离,这个很好做,笼统的接口如下:

/// 登录接口
abstract class ILoginService implements IService {
  void login();
  void logout();
}
/// 登录完成,这儿运用了注解进行绑定
@ServiceBindAnnotation(ILoginService)
class LoginService implements ILoginService {
  @override
  void init() {}
  @override
  void getUserInfo() {
    print('getUserInfo');
  }
  @override
  void login() {
    print('login');
  }
}

2.2 怎么获取

上面说了能够经过编译期代码生成,有以下几个办法:

  • 自己依照格局来字符串生成文件
  • 运用mustache4dart2模板代码生成
  • 运用code_builder类似于ASM的办法

第1种办法自己写格局处理等不好做,也没有代码相关约束,加上后期保护本钱高的原因所以不挑选第一种;第2中办法是闲鱼的路由结构运用的开源库,可是现在不支持空安全;所以最终挑选了第3种办法,它是flutter官方供给的,功用强大,可是没有过多地介绍和api文档不行完善,只能在开发中逐步探索。

会集的代码生成部分,咱们直接看代码:

String write(BuildStep buildStep) {
    DartEmitter emitter = DartEmitter.scoped();
    var library = Library((b) {
      b.body.addAll([
        //生成类
        Class((builder) {
          builder.name = "ServiceProviderInternal";
          builder.fields.addAll([
            Field((fieldBuilder) {
              fieldBuilder.name = "_cache";
              fieldBuilder.type = refer("Map<Type, IService>");
              fieldBuilder.modifier = FieldModifier.var$;
              fieldBuilder.assignment = const Code("{}");
              fieldBuilder.modifier = FieldModifier.final$;
            })
          ]);
          builder.methods.addAll(
            //生成办法
            [
              Method((methodBuilder) {
                methodBuilder.name = "getService";
                methodBuilder.body = _generatorGetService(eachServiceInfoMaps);
                methodBuilder.types.add(refer('T extends IService'));
                methodBuilder.returns = refer('T');
              }),
              Method((methodBuilder) {
                methodBuilder.name = "_getService";
                methodBuilder.body = _generatorInnerGetService();
                methodBuilder.types.add(refer('T extends IService'));
                methodBuilder.requiredParameters.add(Parameter((builder) {
                  builder.name = "impl";
                  builder.type = refer('IService');
                }));
                methodBuilder.returns = refer('T');
              })
            ],
          );
        }),
      ]);
      //导入引证
      b.directives.clear();
      b.directives.addAll(allImportSet);
    });
    return DartFormatter().format('${library.accept(emitter)}');
  }

类似于ClassMethodField便是code_builder供给生成类的api,基本运用看注释就能够了;

根据以上分析再加上编译期注解生成,咱们看一下最终生成的代码:

// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// Generator: ServiceProvider
// **************************************************************************
import 'package:service_provider/interface/service_interface.dart';
import 'package:service_provider_sample/sample/login.service.service.dart';
import 'package:service_provider_sample/sample/login_service.service.find.dart';
class ServiceProviderInternal {
  final Map<Type, IService> _cache = {};
  T getService<T extends IService>() {
    switch (T) {
      case ILoginService:
        return _getService<T>(LoginService());
      default:
        throw Exception("ServiceProviderInternal not find service!!!!!");
    }
  }

  T _getService<T extends IService>(IService impl) {
    if (!_cache.containsKey(T)) {
      _cache[T] = impl;
      impl.init();
    }
    return _cache[T] as T;
  }
}

三、运用办法

那我作为事务方怎么用呢?补白:详细运用能够直接看源码中的实例程序即可

3.1 别人写好了我直接用

直接运用运用ServiceProvider.getService<ILoginService>()来获取实例并调用办法即可,ILoginService换成需求的接口。

3.2 别人没写我自己创立

  1. 创立服务接口类ILoginService,完成自IService
  2. 完成详细服务类,继承自刚才的接口ILoginService,而且用@ServiceBindAnnotation注解来进行绑定
  3. 经过 flutter pub run build_runner build 来生成代码,生成后的文件是 service_provider.service.dart 中,假如生成有问题能够先 flutter pub run build_runner clean 然后再 flutter pub run build_runner clean
  4. 运用ServiceProvider.getService<ILoginService>()来获取实例并调用办法即可

3.3 源码地址

最新的版本号是v1.0.2,能够在pub.dev中查找service_provider_dart

源码地址,有爱好的同学能够看看。