BeeHive 是用于 iOS 的 App 模块化编程的框架实现方案

2016-11-23 10:42:44 +08:00
 newkengsir

BeeHive

http://www.code4app.com/forum.php?mod=viewthread&tid=11674&extra=page%3D1

https://github.com/alibaba/BeeHive


0. 概述

BeeHive是用于iOSApp模块化编程的框架实现方案,吸收了Spring框架Service的理念来实现模块间的API耦合。

0.1 基本架构

0.2 实现特性

0.3 设计原则

因为基于SpringService理念,虽然可以使模块间的具体实现与接口解耦,但无法避免对接口类的依赖关系。

为什么不使用invoke以及动态链接库技术实现对接口实现的解耦,类似ApacheDSO的方式?

主要是考虑学习成本难度以及动态调用实现无法在编译检查阶段检测接口参数变更等问题,动态技术需要更高的编程门槛要求。

0.4 项目名来源

BeeHive灵感来源于蜂窝。蜂窝是世界上高度模块化的工程结构,六边形的设计能带来无限扩张的可能。所以我们用了BeeHive来做为这个项目的命名。

1 模块生命周期的事件

BeeHive会给每个模块提供生命周期事件,用于与BeeHive宿主环境进行必要信息交互,感知模块生命周期的变化。

事件分为三种类型:

1.1 系统事件

系统事件通常是Application生命周期事件,例如DidBecomeActiveWillEnterBackground等。

系统事件基本工作流如下:

1.2 通用事件

在系统事件的基础之上,扩展了应用的通用事件,例如modSetupmodInit等,可以用于编码实现各插件模块的设置与初始化。

扩展的通用事件如下:

1.3 业务自定义事件

如果觉得系统事件、通用事件不足以满足需要,我们还将事件封装简化成BHAppdelgate,你可以通过继承 BHAppdelegate来扩展自己的事件。

2. 模块注册

模块注册的方式有静态注册与动态注册两种。

2.1 静态注册

通过在BeeHive.plist文件中注册符合BHModuleProtocol协议模块类:

2.2 动态注册

@implementation HomeModule

BH_EXPORT_MODULE()  // 声明该类为模块入口

@end

在模块入口类实现中 使用BH_EXPORT_MODULE()宏声明该类为模块入口实现类。

2.3 异步加载

如果设置模块导出为BH_EXPORT_MODULE(YES),则会在启动之后第一屏内容展现之前异步执行模块的初始化,可以优化启动时时间消耗。

3. 编程开发

BHModuleProtocol为各个模块提供了每个模块可以Hook的函数,用于实现插件逻辑以及代码实现。

3.1 设置环境变量

通过context.env可以判断我们的应用环境状态来决定我们如何配置我们的应用。

-(void)modSetup:(BHContext *)context
{
    switch (context.env) {
        case BHEnvironmentDev:
        //....初始化开发环境
        break;
        case BHEnvironmentProd:
        //....初始化生产环境
        default:
        break;
    }
}

3.2 模块初始化

如果模块有需要启动时初始化的逻辑,可以在modInit里编写,例如模块注册一个外部模块可以访问的Service接口

-(void)modInit:(BHContext *)context
{
    //注册模块的接口服务
    [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
}

3.3 处理系统事件

系统的事件会被传递给每个模块,让每个模块自己决定编写业务处理逻辑,比如3D-Touch功能

-(void)modQuickAction:(BHContext *)context
{
    [self process:context.shortcutItem handler:context.scompletionHandler];
}

3.4 模间调用

通过处理Event编写各个业务模块可以实现插件化编程,各业务模块之间没有任何依赖,coremodule之间通过event交互,实现了插件隔离。但有时候我们需要模块间的相互调用某些功能来协同完成功能。

通常会有三种形式的接口访问形式:

  1. 基于接口的实现Service访问方式(Java spring框架实现)
  2. 基于函数调用约定实现的Export Method(PHPextensionReactNatve的扩展机制)
  3. 基于跨应用实现的URL Route模式(iPhone App之间的互访)

我们目前实现了第一种方式,后续会逐步实现后两种方式。

基于接口Service访问的优点是可以编译时检查发现接口的变更,从而及时修正接口问题。缺点是需要依赖接口定义的头文件,通过模块增加得越多,维护接口定义的也有一定工作量。

3.4.1 定义接口

以为HomeServiceProtocol为例。

@protocol HomeServiceProtocol <NSObject, BHServiceProtocol>

- (void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName;

@end

3.4.2 注册Service

有三种方式:

声明式注册

@implementation HomeService

BH_EXPORT_SERVICE()

@end

API注册

[[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];

BHService.plist注册

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>HomeServiceProtocol</key>
        <string>BHViewController</string>
    </dict>
</plist>

3.4.3 调用Service

#import "BHService.h"

id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

3.5 单例与多例

对于有些场景下,我们访问每个声明Service的对象,希望对象能保留一些状态,那我们需要声明这个Service对象是一个单例对象。

我们只需要在Service对象中实现事件函数

声明

-(BOOL) singleton
{
    return YES;
}

通过createService获取的对象则为单例对象,如果实现上面函数返回的是NO,则createService返回的是多例。

id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

3.6 上下文环境 Context

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [BHContext shareInstance].env = BHEnvironmentDev; //定义应用的运行开发环境
    [BHContext shareInstance].application = application;
    [BHContext shareInstance].launchOptions = launchOptions;
    [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/CustomModulePlist";//可选,默认为 BeeHive.bundle/BeeHive.plist
    [BHContext shareInstance].serviceConfigName =  @"BeeHive.bundle/CustomServicePlist";//可选,默认为 BeeHive.bundle/BHService.plist
    [[BeeHive shareInstance] setContext:[BHContext shareInstance]];

    [super application:application didFinishLaunchingWithOptions:launchOptions];

    id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

    if ([homeVc isKindOfClass:[UIViewController class]]) {
        UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:(UIViewController*)homeVc];

        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        self.window.rootViewController = navCtrl;
        [self.window makeKeyAndVisible];
    }

    return YES;
}

更多细节可以参考 Example 用例。

4. 集成方式

cocoapods

pod "BeeHive", '1.0.0'

5. 作者

6. 开源许可证

BeeHive is available under the GPL license. See the LICENSE file for more info.

3527 次点击
所在节点    iOS
11 条回复
so898
2016-11-23 10:54:59 +08:00
G …… GPL ……

看看算了……
queuey
2016-11-23 11:06:33 +08:00
GPL 阿里果然是中国三大巨头。领先 Facebook100 年
kera0a
2016-11-23 11:13:38 +08:00
GPL 还怎么用啊。
不过可以学习下
eddiechen
2016-11-23 12:32:05 +08:00
留个脚印,纯看下, iOS 一直没找到好用的框架
fengyunSmlie
2016-11-23 13:21:55 +08:00
同留脚印 iOS 搭框架 一直没想到好的
DingSoung
2016-11-23 14:04:01 +08:00
MARK
dorentus
2016-11-23 14:58:34 +08:00
GPL 玩个屁啊
newkengsir
2016-11-23 15:53:02 +08:00
newkengsir
2016-11-23 15:53:21 +08:00
func didReceive(_ response: UNNotificationResponse,
completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {

if response.actionIdentifier == Identifiers.cancelAction {
let request = response.notification.request

let identifiers = [request.identifier]

// Remove future notifications that have been scheduled
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)

// Remove any notifications that have already been delivered so we're not cluttering up the user's notification center
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)

// Visual feedback that notification has been cancelled
speakerLabel.text = "🔇"
speakerLabel.cancelShake()

completion(.doNotDismiss)
}
else {
completion(.dismiss)
}
}
newkengsir
2016-11-23 15:53:34 +08:00
```
func didReceive(_ response: UNNotificationResponse,
completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {

if response.actionIdentifier == Identifiers.cancelAction {
let request = response.notification.request

let identifiers = [request.identifier]

// Remove future notifications that have been scheduled
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)

// Remove any notifications that have already been delivered so we're not cluttering up the user's notification center
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)

// Visual feedback that notification has been cancelled
speakerLabel.text = "🔇"
speakerLabel.cancelShake()

completion(.doNotDismiss)
}
else {
completion(.dismiss)
}
}
```
expkzb
2016-11-23 21:50:49 +08:00
真没啥必要

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/322579

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX