一、布景
在做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)}');
}
类似于Class
,Method
,Field
便是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 别人没写我自己创立
- 创立服务接口类
ILoginService
,完成自IService
- 完成详细服务类,继承自刚才的接口
ILoginService
,而且用@ServiceBindAnnotation
注解来进行绑定 - 经过
flutter pub run build_runner build
来生成代码,生成后的文件是service_provider.service.dart
中,假如生成有问题能够先flutter pub run build_runner clean
然后再flutter pub run build_runner clean
- 运用
ServiceProvider.getService<ILoginService>()
来获取实例并调用办法即可
3.3 源码地址
最新的版本号是v1.0.2
,能够在pub.dev
中查找service_provider_dart
源码地址,有爱好的同学能够看看。