前言
BeeHive 是阿里用于iOS的 App 模块化编程的框架实现方案,吸收了 Spring 框架 Service 的理念来实现模块间的 API 耦合。
项目结构
项目模块
├── BHAnnotation.h
├── BHAnnotation.m
├── BHAppDelegate.h
├── BHAppDelegate.m
├── BHCommon.h
├── BHConfig.h
├── BHConfig.m
├── BHContext.h
├── BHContext.m
├── BHDefines.h
├── BHModuleManager.h
├── BHModuleManager.m
├── BHModuleProtocol.h
├── BHServiceManager.h
├── BHServiceManager.m
├── BHServiceProtocol.h
├── BHTimeProfiler.h
├── BHTimeProfiler.m
├── BHWatchDog.h
├── BHWatchDog.m
├── BeeHive.h
└── BeeHive.m
模块解读
BeeHive:是框架的门面,将几个组件封装在这个类里面组合,向外提供统一的接口。
BHAnnotation:注解类,提供一些简便的宏
BHAppDelegate:AppDelegate 的替换类,提供一些启动,进入后台的消息分发到模块的功能。
BHCommon:Log 功能(暂时,也许会增加)
BHConfig:提供全局的配置功能,类似于 NSUserDefaults 的功能
BHContext:框架的上下文对象,可以配置环境(dev, stage等),提供全局配置对象 BHConfig,还有一些关于 APP 上下文的功能。
BHModuleManager:模块的管理类,包括读取,加载,注册模块等功能。
BHModuleProtocol:要成为一个模块需要遵守的协议。
BHServiceManager:服务,也就是模块间的通信用服务来解决。
BHServiceProtocol:要成为服务需要遵守的协议。
BHTimeProfiler:提供性能监控的功能
BHWatchDog:检测线程的卡顿功能。
如何解耦
在 BeeHive 中一个个对象被分成模块(Module),模块之间相互独立,通过接口来调用(Service)来让模块间不相互之间依赖,通过暴露的接口来规范调用机制,但是过多的接口也会造成维护问题。
模块
想让自己的对象变成 Module 只需要遵守协议 BHModuleProtocol
。BHModuleProtocol
协议中包含了一个模块的声明周期的回调,和注册的方法,如提供 AppDelegate 周期回调的事件,以及模块初始化,配置入口等
/// 一部分事件
- (void)basicModuleLevel;
- (BOOL)async;
- (void)modSetUp:(BHContext *)context;
- (void)modInit:(BHContext *)context;
- (void)modSplash:(BHContext *)context;
- (void)modQuickAction:(BHContext *)context;
- (void)modTearDown:(BHContext *)context;
- (void)modWillResignActive:(BHContext *)context;
- (void)modDidEnterBackground:(BHContext *)context;
- (void)modWillEnterForeground:(BHContext *)context;
- (void)modDidBecomeActive:(BHContext *)context;
- (void)modWillTerminate:(BHContext *)context;
- (void)modUnmount:(BHContext *)context;
此外,模块注册方法提供从 plist 注册,使用宏BH_EXPORT_MODULE
注册 2 种注册方法。
模块的注册
模块的注册主要集中在 BHModuleManager
这个对象单例中。
读取 plist 的模块
- (void)loadLocalModules
{
NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
return;
}
NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
NSArray *modulesArray = [moduleList objectForKey:kModuleArrayKey];
[self.BHModules addObjectsFromArray:modulesArray];
}
动态注册的方式,也就是宏注册
- (void)addModuleFromObject:(id)object
{
Class class;
NSString *moduleName = nil;
if (object) {
class = object;
moduleName = NSStringFromClass(class);
} else {
return ;
}
if ([class conformsToProtocol:@protocol(BHModuleProtocol)]) {
NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary];
BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)];
int levelInt = 1;
if (responseBasicLevel) {
levelInt = 0;
}
[moduleInfo setObject:@(levelInt) forKey:kModuleInfoLevelKey];
if (moduleName) {
[moduleInfo setObject:moduleName forKey:kModuleInfoNameKey];
}
[self.BHModules addObject:moduleInfo];
}
}
这个类除注册之外,还提供了事件的转发,这部分代码比较简单,想了解可以查看源代码。
接口 (Service)
接口规范了统一的调用方式,避免了直接依赖来调用对象方法,但是这样的做法如果对象多了,接口也会变得很多,维护接口也是一个巨大的工作量。
定义一个接口需要遵守BHServiceProtocol
协议,此协议主要是为了模块的单例实现提供服务。
接口注册部分也分为 2 种方式,一种是 plist,一种是代码注册。
用 plist 注册:
- (void)registerLocalServices
{
NSString *serviceConfigName = [BHContext shareInstance].serviceConfigName;
NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist"];
if (!plistPath) {
return;
}
NSArray *serviceList = [[NSArray alloc] initWithContentsOfFile:plistPath];
[self.lock lock];
for (NSDictionary *dict in serviceList) {
NSString *protocolKey = [dict objectForKey:@"service"];
NSString *protocolImplClass = [dict objectForKey:@"impl"];
if (protocolKey.length > 0 && protocolImplClass.length > 0) {
[self.allServicesDict addEntriesFromDictionary:@{protocolKey:protocolImplClass}];
}
}
[self.lock unlock];
}
使用代码也就是类似
[[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];
实现如下
- (void)registerService:(Protocol *)service implClass:(Class)implClass
{
NSParameterAssert(service != nil);
NSParameterAssert(implClass != nil);
if (![implClass conformsToProtocol:service] && self.enableException) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ module does not comply with %@ protocol", NSStringFromClass(implClass), NSStringFromProtocol(service)] userInfo:nil];
}
if ([self checkValidService:service] && self.enableException) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil];
}
NSString *key = NSStringFromProtocol(service);
NSString *value = NSStringFromClass(implClass);
if (key.length > 0 && value.length > 0) {
[self.lock lock];
[self.allServicesDict addEntriesFromDictionary:@{key:value}];
[self.lock unlock];
}
}
模块之间的调用
既然模块有了,接口也有了,那么他们是怎么调用的呢?
首先为了解耦,BeeHive 使用接口来解耦,那么调用方式会如下:
d< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
此方法,可以指定一个接口来完成对接口后面的模块的调用。
那么模块是否可以成为单例,对外界调用呢?
当然是可以的,只需要在Service对象中实现事件函数
声明
-(BOOL) singleton
{
return YES;
}
通过createService获取的对象则为单例对象,如果实现上面函数返回的是NO,则createService返回的是多例。
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
好了,至此,BeeHive 的解耦的手法就是如此。
其他
BeeHive 还提供了环境变量的封装,BHContext 对象就是一个 APP 上下文的环境变量。想知道更详细的细节,您可以自己探索。