7.2、sparrow plugin设计
1/// 服务转换函数定义,通过构建[ServiceSettings]模型,经过ServiceBuilder转换成[Widget]统一管理 2typedef ServiceBuilder = Widget Function(ServiceSettings settings); 3 4/// 动态列表Cell卡片转换函数定义,通过构建[CardSettings]模型,CardBuilder[Widget]统一管理 5typedef CardBuilder = Widget Function(CardSettings settings); 6 7/// 页面路由转换函数定义,通过构建[RouteSettings]模型,RouteBuilder[Widget]统一管理 8typedef RouteBuilder = Widget Function(RouteSettings settings);
sparrow只需要管理ServiceBuilder、CardBuilder、RouteBuilder三种Widget即可,由于这三种Widget是由xxxSettings模型驱动,Builder包含了相关的单一功能所需要的全部信息。
下文重点以ServiceBuilder管理为例,描述以下管理流程:
-
服务注册(iOS、Android、dart)
-
函数执行转发
-
参数模型转换
-
通道数据mock
-
native端管理搜索Service
7.2.1、服务注册
1///注册 2registerServiceBuilder('action:/bike/getOrderList', 3 (settings) => BikeSerive(serviceSettings: settings)); 4 5 ///实现 6 Future<dynamic> doAction() async{ 7 if ("getOrderList" == this.serviceSettings.action) { 8 /// 执行action逻辑 9 } 10 return Future.value('callback...'); 11 }iOS端:
1@SparrowMethodMod(PlatformServive,platform) 2 @Sparrow_export_method(@selector(getUser:)) 3- (void)getUser:(NSDictionary *)user 4{ JYFlutterResult callback = user[kSparrowMethodCallBackKey]; 5 callback(@"native_callback:用户数据模型。。。"); 6 }
Android端:
1@SparrowService("platform") 2public class PlatformService implements BaseFlutterService { 3@SparrowMethod("getUser") 4 public void getUserInfo(ServiceEntity serviceEntity) { 5serviceEntity 6.getCallback() 7.onSuccess("native_callback:用户数据模型。。。"); 8 } 9}iOS端的@SparrowMethodMod(PlatformServive,platform)以及Android端的@SparrowService("platform")中的platform属于模块服务别名,用于标识服务所属模块,native端模块服务实现如果出现散落各处,也是通过此别名进行内聚。
dart端使用:
1//通过返回值 2var orderList = SparrowManager.doAction('action:/bike/getOrderList', actionParams); 3 4//用户行为采集(action:/track/reportAnalytics)await 5ReportAnalytics.reportAnalytics(categoryId,eventId,params); 6 7 //设备信息(action:/system/getSystemInfo) 8var systemInfo = System.getSystemInfo();
7.2.2、函数执行转发
1class Analytics { 2static Future<dynamic> reportAnalytics( 3 {Map<String, String> params} 4 ) async { 5 var nativeParams = ...; 6 return await SparrowManager 7 .doAction("action:/track/reportAnalytics", nativeParams); 8 } 9}
sparrow提供函数、url两种方式的服务调用,函数调用解决编译期检查,内部转换成方便管理的url调用,服务初始化采用懒加载形式对公共模型以及上下文进行集中处理。
7.2.3、通道数据mock
1var args = { 2 'method': 'getUserInfo' 3}; 4 5 var res = { 6 "code":SparrowCode.Pass.index, 7 "msg":"msg", 8 "data": {"name":"mock数据"} 9}; 10///更改指定参数的服务调用,返回指定res 11 ChannelMockUtil.instance.createMock(args, res);
更改指定参数的服务调用,返回指定res,在sparrow内部通过改写原有通道的methodCallHandler,在改写通道数据流向的同时建立备用通道,保持未mock数据正常传输。
7.2.4、native端管理搜索Service
iOS端的SparrowInvocationConfig管理着一组模块服务,编译器通过注解的形式
1//1、编译期把模块注入到__DATA 2#define SparrowMethodDATA(sectname) __attribute((used, section("__DATA,"#sectname" "))) 3 4 #define SparrowMethodMod(name,alias) \ 5class NSObject; char * k##name##_mod SparrowMethodDATA(SparrowService) = "{ \""#name"\" : \""#alias"\"}"; 6 7 //2、加载image时搜索__DATA注入类的指定方法 8#define Sparrow_export_method(method) SPARROW_EXPORT_METHOD_INTERNAL(method,fm_export_method_) 9 10 #define SPARROW_EXPORT_METHOD_INTERNAL(method, token) \ 11classNSObject; \ 12+ (NSString *)FM_CONCAT_WRAPPER(token, __LINE__) { \ 13 return NSStringFromSelector(method); \ 14}
-
编译期把模块名以及对应的别名注入到 section __DATA
-
加载image时搜索section __DATA注入模块类类的打了标记的方法
-
为每个模块创建SparrowInvocationConfig,供dart传入数据命中
Android端使用AutoService注解在编译期将SparrowInvocationService标识的类写入到META-INF/services/com.hellobike.sparrow.bases.ISparrowInvocationService 文件中,并在运行时读出,供dart传入数据命中。
7.3、plume package设计
plume的设计初衷是把基础服务从sparrow解耦出来,plume包含了一个native App提供给dart端所有的基础服务,在plume内部对传入数据进行计算加工,封闭服务实现细节。原生app内部通过Module、Service功能划分,并通过pod、maven管理,功能相对集中独立且完整,plume会通过sparrow提供的通道跟这些模块服务一一对应,从而隐藏iOS、Android的实现细节,统一标准的业务架构,提供一致的基础服务定义。
7.3.1、基础服务总览
目前哈啰出行基础服务分为以下项,通过pod、maven管理,功能相对集中独立且完整,plume会通过sparrow提供的通道跟这些服务一一对应,从而隐藏iOS、Android的实现细节,统一标准的业务架构,提供一致的基础服务定义。
7.4、native服务转换层设计
native服务转换层设计核心问题把dart传入path携带的信息二维展开并命中到合适的方法上、通过模型转换解决层与层的依赖问题。
7.4.1 iOS native端设计
服务的实现部分通过硬编码是很难处理的,因此做了一层转换处理。
以网络服务为例子:需要实现一个post函数,供dart层使用:
1@Sparrow_export_method(@selector(post:)) 2- (void)post:(NSDictionary *)dict{ 3 //实现 4}
转换层处理之后:
1@Sparrow_export_method(@selector(post:params:success:failure:)) 2- (void)post:(NSString *)url 3 params:(NSDictionary *)params 4 success:(SparrowNetworkSuccess)success 5 failure:(SparrowNetworkFailure)failure { 6 //实现 7}
转换层原理:
-
定义一层协议:
@protocol SparrowNetworkServive <NSObject> - (void)post:(NSString *)url params:(NSDictionary *)params success:(SparrowNetworkSuccess)success failure:(SparrowNetworkFailure)failure; @end
需要实现网络层服务,需要继承 SparrowNetworkServive 协议。
-
创建转发类:
SparrowNetworkInvocation 类使用@SparrowServiceProtocol宏和SparrowNetworkServive协议进行绑定
SparrowNetworkInvocation类在invocation函数中显式调用post函数。
容器做了一层强代码约束,降低错误率。@SparrowServiceProtocol(SparrowNetworkServive,SparrowNetworkInvocation)@implementation SparrowNetworkInvocation- (id)invocation:(SEL)action target:(NSObject *)target params:(NSDictionary *)params { NSObject<SparrowNetworkServive> *servive = (NSObject<SparrowNetworkServive> *)target; if (action == @selector(post:params:success:failure:)) { NSString *url = [self _url:params]; JYFlutterResult result = params[kSparrowMethodCallBackKey]; [servive post:url params:[self _params:params] success:[self _successResult:result] failure:[self _failureResult:result]]; return @(0); }}@end
1@SparrowServiceProtocol(SparrowNetworkServive,SparrowNetworkInvocation)
2@implementation SparrowNetworkInvocation
3- (id)invocation:(SEL)action target:(NSObject *)target params:(
4NSDictionary *)params { NSObject<SparrowNetworkServive> *servive = (NSObject<SparrowNetworkServive> *)target;
5 if (action == @selector(post:params:success:failure:)) {
6 NSString *url = [self _url:params];
7 JYFlutterResult result = params[kSparrowMethodCallBackKey];
8 [servive post:url
9 params:[self _params:params]
10 success:[self _successResult:result]
11 failure:[self _failureResult:result]];
12 return @(0);
13 }
14}
15@end
7.4.2 Android native端设计
使用@SparrowInvocationService("network")标记当前类是转换service,其中的"network"表示对@SparrowService标识的service进行转换;需要实现ISparrowInvocationService接口,以便对转换层接口进行统一。
1@Override 2public void invoke(Object targetObj, String action, final ServiceEntity serviceEntity){}
invoke即是转换服务类必须实现的方法,其中的targetObj是被转换的service实例,这里需要自定义接口,让该service实例实现,转换层将dart传递过的参数等进行转换后通过该自定义接口调起service对应方法;action是需要执行的动作;serviceEntity对参数、回调、context等进行包装。
1// 标识为转换service 2@SparrowInvocationService("tangram") 3public class SparrowTangramInvocation implements ISparrowInvocationService { 4 @Override 5 public void invoke(Object targetObj, String action, ServiceEntity serviceEntity) { 6 // 被转换的service需要实现自定义接口 7 ISparrowTangramService service = (ISparrowTangramService)targetObj; 8 Map<String,String> params = serviceEntity.getParams(); 9 // 根据action判断dart层的调用动作 10 if (Actions.getABTestIsEnable.equals(action)) { 11 //参数处理 12 //调起接口方法 13 service.getABTestIsEnable(xx,xx); 14 } 15 } 16}
8、后记
容器技术的出现改变了软件交付的思维,在解决研发效率、一致的性能体验、动态化的同时,sparrow也承载Flutter业务的稳定性指标、安全高效的指标,后续sparrow也就继续深入Android热修复、多媒体资源共享等方面的探索和验证...