前言
从前一篇 文章 ,咱们环绕 “
移动客户端架构规划
” 这个话题打开评论的过程中,咱们做了根本的总述,而且针对架构规划
的大话题简扼地区别为五个部分:
模块化开发
组件化开发
二进制化处理
规划办法
架构规划与架构演进
咱们将在本篇文章,进一步对模块化开发
这个话题 深化打开 评论
咱们先简略回忆一下,在前面的总述中介绍 移动端模块化开发
之前咱们介绍的部分常识
项目周期简述
一个项目从立项到开发流程根本能够扼要概括为:
- 开发一个软件体系,首先需求
确定需求
- 并依据需求对软件体系进行必定的
分层规划
- 在层次区别的基础上进行
模块区别
- 模块区别往后便是
挑选组件
用于集成完结模块 - 集成->开发->测验->布置上线……
- 在
模块区别
之后,咱们需求做的作业便是模块挑选
或模块规划
-
模块挑选1
: 公司的现存开发套件的模块,依据已有模块的基础上进行恰当改造迭代 -
模块挑选2
: 收购 厂商的SDK 服务,如前面提及的 mPaaS 蚂蚁金融云服务等 -
模块挑选3
: 去获取开源第三方模块,并依据此项目需求,进行恰当改造 -
模块挑选4
: 从头规划开发
-
模块化开发:
-
【模块化开发】 指的是:
将一个
体系或运用程序区别
为多个功用模块
-
每个模块
具有独立的功用
和接口
,而且能够单独开发
、测验
和保护
-
【模块化开发】 的
意图
是为了进步代码复用性
、可保护性
和可扩展性
,以及下降开发
和测验的复杂度
和难度
。
组件化开发:
-
【组件化开发】 则是
在模块化开发的基础上
,将模块进一步区别为更小的组件
-
每个组件
都有自己的界面
和事务逻辑
,而且能够独立布置和升级
。 -
【组件化开发】 的意图是为了
进一步进步
代码复用性和可保护性,一起还能够完结更多的代码并行开发
和更灵敏的运用程序构建
和发布
。 -
模块化开发
和组件化开发
都是为了进步软件开发的功率和质量 - 但
组件化开发
相关于模块化开发
愈加细粒度和 灵敏,能够更好地满意大型运用程序/软件体系的的需求(换言之,一个模块能够集成若干组件,以完结模块的功用) - 个人总结,从规划上来看
- 模块强调责任(内聚、别离)
- 组件强调复用
模块规划:
无论是组件开发仍是模块开发,咱们都需求对其进行必定的规划作业,尽量防止不必要地重复作业和回避一些开发危险。
咱们能够经过StarUML这个建模东西进行软件的规划作业,StarUML支撑:
-
类图
、时序图
、用例图
等十几种图形办法; - 能够经过我 这篇文章初步了解StarUML
- 能够经过 官网 进一步学习StarUML的运用
总述
本文介绍的内容适用于项目完结了需求确认
,软件分层规划
之后的阶段。
在完结了软件分层架构规划之后,需求将模块区别归类,进一步合理规划软件架构,并凭借模块办理结构
,完结App
的模块化开发
以到达App的快速集成
项目落地的技能计划。
本文内容结构
模块分类:
罗列市面上盛行的不同类型几款App,对模块进行区别
- 依照【
事务领域
】区别;- 依照【
功用服务
】区别;- ……
模块区别
介绍模块区别的战略
模块分类
是区别的结果模块区别
是分类的战略了解几种模块化计划
- 罗列几种模块化计划
- 剖析不同计划的好坏
模块功用规划
- 参考的模块化才能
- 定制模块化才能
- 理论剖析
- 规划模块功用
模块化开发实践
- 开发模块办理结构
- 开发模块,实践模块办理与集成
一、模块的三级分类
-
- 金融-付出宝事例:
1. 【第一级模块
】: 运用级模块
以【金融-
付出宝
】为例剖析:
模块区别:
-
【
第一级模块
】: 运用级模块:- 主页
- 理财
- 日子
- 音讯
- 我
2. 【第二级模块
】: 事务服务模块
以主页为例
- 主页模块
点击主页的更多
进口,进入运用中心
,咱们能够看到若干事务功用模块,付出宝很好地为他们做了归类: -
【便民日子】:
- 事务模块:
充值中心
、日子缴费
、交管12123
…
- 事务模块:
-
【购物娱乐】:
- 事务模块:
饿了么
、彩票
、淘宝
…
- 事务模块:
-
【财富办理】:
- 事务模块:
花呗
、借呗
、基金
、股票
…
- 事务模块:
-
【教育公益】:
- 事务模块:
蚂蚁森林
、运动
…
- 事务模块:
3. 【第三级模块
】: 功用模块
以便民日子大类的充值中心为例
- 事务模块:
充值中心
-
事务1
: 话费充值 -
事务2
: 流量充值 - …
-
咱们经常运用付出宝,就不难发现, 简直一切的 服务型 事务模块,都离不开一个功用,那便是 付出
此处 的 付出功用
便是 付出功用模块
付出宝的实质便是 移动付出东西
, 其它 商业性事务 都是 环绕其 即时、快捷的 电子付出功用
向广阔用户 供给 日子中的各类服务的。
付出宝
只需一个功用模块
?
那如此说来,是不是付出宝就只需一个功用模块, 付出模块
呢?
答案必定是 否定
的!!!
付出宝完结其付出模块
的功用,最起码完结了以下几个功用:
网络通讯功用
加密功用
- 绑定银行卡,还用到了
OCR辨认功用
、绑定验证时短信服务功用
- 开启快捷付出时的
人脸辨认功用
- 还有为了 监控 运用 运营状况, 处处都要用到的,
埋点功用
- …
朋友们,若你还分不太清各级模块, 你也能够试着找一些运用来进行模块区别,欢迎留言,咱们一起探讨!
其他事例剖析引荐运用:
-
- 电商: 淘宝
-
- 教育: 中公教育、六分钟英语
-
- 医疗: 微医
-
- 车联网: 广汽传祺、哈啰出行&&嘀嗒出行
-
- 通讯: 中国移动
-
- 东西: AudioTools
-
- 娱乐: 抖音、网易云音乐
-
- 物联网: 海尔家居、华为手环
-
- 日子: 美团、菜鸟裹裹
- …
二、模块区别战略
1. 区别战略
咱们从模块的三级分类事例中,能够总结得出结论:
- 模块ke分三大类:
运用级模块
、事务级模块
、功用级模块
-
运用级模块
:
个数取决于App自身的规划者,是选用由几大运用模块的丰厚版 或是 单一运用模块的简约风格- 可 集成 若干
事务级模块
- 有必要集成
用户模块
:运用 用户数据进行 事务交互 -
单运用模块App
示例: 打车服务途径:嘀嗒出行 -
多运用模块App
示例: 打车服务途径:哈啰出行
- 可 集成 若干
-
事务级模块
:- 在
运用级模块
下,有若干个事务级模块
-
事务级模块
示例: -
事务级模块
事务模块便是 公司 商业活动的各种途径- 用户的消费途径(于企业主而言)
- 广告途径(于企业主而言)
- 运用途径(于用户而言)
- …..
- 其 完结, 至少依靠 一个
功用级模块
- 其 完结, 需依靠 必定的事务UI组件
- 其 完结, 可依据事务场景需求定制
- 规划 事务服务
- 规划 服务事务页面
- 规划 场景交互
- 在
-
功用级模块
:- 小功用,自身可独立供给功用服务
- 大功用,须依靠 若干小功用
- 大功用 便是 小功用组
2. 常见的模块归类
依照【运用级】区别:
-
付出宝:
- 主页
- 理财
- 日子
- 音讯
- 我的
-
淘宝:
- 主页
- 逛逛
- 音讯
- 购物车
- 我的
-
高德地图:
- 主页
- 附近
- 音讯
- 打车
- 我的
-
美团:
- 主页
- 优选
- 音讯
- 购物车
- 我的
-
抖音:
- 主页
- 朋友
- 发明(符号模块:+号)
- 音讯
- 我的
-
哈啰出行:
- 主页
- 车主
- 逛逛
- 钱包
- 我的
-
嘀嗒出行:
- 乘客运用模块
- 车主运用模块
- …….
依照【事务服务】区别:
- 【事务模块】: 金融服务、电商服务、咨询事务、通讯事务、物联网事务等
- 【金融事务】: 基金事务、理财事务、保险事务、存款事务、贷款事务、汇款事务等
- 【电商事务】: 物流事务、下单事务、产品墙、商品概况展示与交互等
- 【通讯事务】: 智能客服、音视频通讯、无人机控制等
- 【物联网事务】…..
- ….
依照【功用级】区别:
- 【服务模块】:第三方途径服务、第二方途径服务、公司途径服务
- LBS、社会化共享、广告服务、推送服务、即时通讯服务、埋点服务、电子付出服务等
- 【加密模块】:
- 公司方加密(前后端算法校验): 算法加密
- MD5
- RSA
- AES
- DES
- GMx
- Hash
- …
- 二方加密途径服务(需收购): 证书加密、蓝牙Sim盾、Okta双重验证
- 本地加密生物信息采集: 人脸辨认、手势辨认、面容辨认
- 公司方加密(前后端算法校验): 算法加密
- 【通讯模块】: 网络通信、无线物联网通讯
- BLE通讯+iBeacon通讯、WIFI通讯等
- https/http网络通讯、socket/websocket通讯等
- 【硬件特性】: 拍照功用、OCR辨认功用、人脸辨认功用、音视频功用、陀螺仪等传感器等
- ……
3. 了解相关的技能概念
以及命名标准
约定
3.1 顶层概念
概念 | |
---|---|
Application |
运用程序: 模块化开发中咱们用SubApp 作为运用模块 的关键词 |
Feature |
特性: 模块化开发feature 作为事务模块 的关键词。咱们开发上线一个新事务模块常常也称之为新特性
|
Capability |
才能: 模块化开发capability 作为功用模块 的关键词 |
Component |
组件: 模块化开发component 作为事务组件 和功用组件 的关键词 |
Plugin |
插件: 模块化开发plugin 作为二方库 和三方库 、公司途径库SDK 等 服务/功用插件 的关键词。常见的服务:authentication(OAuth授权登录 )、bot protection(防机器人爬虫 )等 |
Foundation Libraries | low-level open source and commercial libraries |
3.2 二级概念
Feature | 事务模块命名举例: |
---|---|
Finance |
金融事务: feature.finance
|
Commerce |
电商事务: feature.commerce
|
Life |
日子服务: feature.life
|
… |
XXX服务: feature.xxx
|
3.3 三级概念
Capability | 功用模块命名举例: |
---|---|
Live |
直播功用: capability.live
|
LBS |
LBS功用: capability.lbs
|
Network |
网络功用: capability.network
|
Persistence |
数据缓存功用: capability.persistence
|
Analytics |
埋点剖析功用: capability.analytics
|
… |
XXX功用: capability.xxx
|
四级概念
关于组件的概念以及相关的区别办理规矩,会在讲解组件的姊妹篇文章,进行深化论述,本次不铺打开来介绍
Component | 组件命名举例: |
---|---|
Product carousels | component.productCarousels |
Size pickers | component.sizePickers |
Story cards | component.storyCards |
… |
XXX组件: component.xxx
|
4. 小结
咱们学高数的时分,总能听到教授想念那几句华罗庚的经典语录:
-
数缺形时少直观,形少量时难入微;
数形结合各样好
,阻隔分居万事休
这句话的意思是, 直观的 图形映像 + 逻辑化的数学公式映像 彼此结合 才能 更好地知道了解且相对回忆深刻。
4.1 架构分层图
所以,咱们无妨在这里来搞一个 具象化 的 小结:
- 仍然以
付出宝为例:
三级模块区别的细节,同学们能够回到section1回忆一下 -
付出宝架构分层图:
-
运用层:
事务模块
-
服务层:
事务服务功用模块
-
容器层:
结构服务功用模块
-
结构层:
Native App Framework
、H5 App Framework
是归于蚂蚁团队 对组件封装 的中间层介质,为 服务层、运用层 供给 调用结构层的接口
-
运用层:
-
区别结构Tree:
- App | - - - - feature = [feature1 = Capability1 + Component1]+[feature2 = Capability1 + Component1]... | - - - - - - - - Capability、Component: 【Capability = Plugin + Capability完结封装】、【Capability = 纯Capability】、【Capability = Component+Component+Component+Component...+ Capability完结封装】
4.2 模块区别层级图:
-
运用结构分级结构:
- 超级App
- 事务模块1
- 事务组件1
- 【UI组件+功用模块】组合1
- 【通用UI组件+功用模块】
- 【UI组件+功用模块】组合2
- 【定制化UI组件+功用模块】
- 功用模块1
- 插件1
- 插件2
- …
- 功用模块2
- …
- 功用模块1
- 【定制化UI组件+功用模块】
- …
- 【UI组件+功用模块】组合1
- 事务组件2
- …
- 事务组件1
- 事务模块2
- ……
- 事务模块1
- 超级App
三、了解几种模块处理计划
1. 模块间有复杂的依靠联系:
一个 APP 有多个模块,模块之间会通信,彼此调用;
- 例如微信读书有 书本概况 想法列表 阅览器 发现卡片 等等模块,这些模块会彼此调用
- 例如 书本概况要调起阅览器和想法列表,阅览器要调起想法列表和书本概况,等等,一般咱们是怎样调用呢,以阅览器为例,会这样写:
#import "WRBookDetailViewController.h" #import "WRReviewViewController.h" @implementation WRReadingViewController - (void)gotoDetail { WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:self.bookId]; [self.navigationController pushViewController:detailVC animated:YES]; } - (void)gotoReview { WRReviewViewController *reviewVC = [[WRReviewViewController alloc] initWithBookId:self.bookId reviewType:1]; [self.navigationController pushViewController:reviewVC animated:YES]; } @end
- 看起来挺好,这样做简略明了,没有多余的东西,项目初期引荐这样快速开发,但到了项目越来越庞大,这种办法会有什么问题呢?
- 清楚明了,每个模块都离不开其他模块
彼此引证,彼此依靠,耦合严峻: - 模块之间这样严峻耦合,对
测验
/编译
/开发功率
/后续扩展
都有一些坏处;
2. 模块间彼此通讯的意图:
模块间彼此通讯的意图,总结就以下几个:
- 获取模块实例
页面跳转
页面传值
- 运用模块的
事务服务
/功用服务
- 传递
响应者链
信号- 捕获时机,履行相关逻辑
- …
3. 那怎么解耦呢?
按软件工程的思路,咱们一看就知道应该加一个中间层
为了处理模块彼此依靠耦合严峻,广阔码友们做过的一些尝试:
加了中间层后,必定程度脱节解耦后的几种依靠办法:
咱们且称中间层
为 Mediator
,Mediator的责任
便是模块间引证/通讯的意图。咱们无妨进一步笼统总结之为,在完结不同模块解耦的前提下,到达:
- 1、获取模块实例
- 2、在模块间”音讯传递”
那么这里有几个问题,咱们需求答复清楚:
-
Mediator
怎么完结音讯传递
? -
模块A
只跟Mediator
通信,怎么获取模块B
的接口? -
模块间 的解耦 是经过
Mediator
来完结的,那模块
和Mediator
的彼此依靠,怎么削弱 乃至破除?
4. 经典解耦计划介绍
4.1 路由服务注册绑定办法:URL+HandlerBlock路由表办理
1.) 代表结构 MGJRouter
[MGJRouter工程目录]
|
|-[MGJRouter]
| |-MGJRouter.h.m
|-[MGJRouterDemo]
| |-[Actions]
| | |-Target_A.h.m
| |-DemoModuleADetailViewController.h.m
|
|-AppDelegate.h.m
|-DemoDetailViewController.h.m
|-DemoListViewController.h.m
2.) 音讯传递的中间件
-
[
MGJRouter
]
MGJRouter
是担任音讯传递的中间件 -
音讯传递办法:
-
register url
注册模块- 经过
url
绑定 一个 操作绑定Block -
url
格局 = 协议头://模块/参数列表
- 经过
-
open url
翻开模块
-
-
传递音讯的中间件源码API:
#import <Foundation/Foundation.h> extern NSString *const MGJRouterParameterURL; extern NSString *const MGJRouterParameterCompletion; extern NSString *const MGJRouterParameterUserInfo; /** * routerParameters 里内置的几个参数会用到上面界说的 string */ typedef void (^MGJRouterHandler)(NSDictionary *routerParameters); @interface MGJRouter : NSObject /** * 注册 URLPattern 对应的 Handler,在 handler 中能够初始化 VC,然后对 VC 做各种操作 * * **@param** URLPattern 带上 scheme,如 mgj://beauty/:id * **@param** handler 该 block 会传一个字典,包含了注册的 URL 中对应的变量。 * 假如注册的 URL 为 mgj://beauty/:id 那么,就会传一个 @{@"id": 4} 这样的字典过来 */ + (void)registerURLPattern:(NSString *)URLPattern toHandler:(MGJRouterHandler)handler; /** * 翻开此 URL * 会在已注册的 URL -> Handler 中寻找,假如找到,则履行 Handler * * **@param** URL 带 Scheme,如 mgj://beauty/3 */ + (void)openURL:(NSString *)URL; /** * 翻开此 URL,一起当操作完结时,履行额定的代码 * * **@param** URL 带 Scheme 的 URL,如 mgj://beauty/4 * **@param** completion URL 处理完结后的 callback,完结的断定跟具体的事务相关 */ + (void)openURL:(NSString *)URL completion:(void (^)(void))completion; /** * 翻开此 URL,带上附加信息,一起当操作完结时,履行额定的代码 * * **@param** URL 带 Scheme 的 URL,如 mgj://beauty/4 * **@param** parameters 附加参数 * **@param** completion URL 处理完结后的 callback,完结的断定跟具体的事务相关 */ + (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(void))completion; /** * 是否能够翻开URL * * **@param** URL * * **@return** */ + (BOOL)canOpenURL:(NSString *)URL; /** * 调用此办法来拼接 urlpattern 和 parameters * * #**define MGJ_ROUTE_BEAUTY @"beauty/:id"** * [MGJRouter generateURLWithPattern:MGJ_ROUTE_BEAUTY, @[@13]]; * * * **@param** pattern url pattern 比方 @"beauty/:id" * **@param** parameters 一个数组,数量要跟 pattern 里的变量一起 * * **@return** */ + (NSString *)generateURLWithPattern:(NSString *)pattern parameters:(NSArray *)parameters; @end
3.) 小结:模块调用逻辑图+长处+缺陷
3.1) 模块调用逻辑图:
3.2) 长处:
- 运用
url-block
的计划确实能够处理模块间彼此引证
,到达解耦 - 蘑菇街专门用后台来办理路由
url
,一致办理- 处理了
iOS
和Android
的途径差异性
- 处理了
3.3) 缺陷:
- 需求有当地列出各个组件里有什么 URL 接口可供调用
- 蘑菇街做了个后台专门办理
- 每个组件
都需求初始化
,内存里需求保护url-block映射表
,组件多了会有内存问题// 注册一个组件(一般在发动的时分去注册) [MGJRouter registerURLPattern:@"mgj://detail?id=:id&name=:name" toHandler:^(NSDictionary *dic) { NSString * oneId = dic[@"id"]; NSString * name = dic[@"name"]; if (oneId && name) { //创立组件,并从字典拿到值 DetailComposite * detail = [[DetailComposite alloc] init]; detail.oneId = oneId; detail.name = name; // 履行组件的办法 [detail showComposite]; } }]; // 外界去调用 履行一个组件 [MGJRouter openURL:@"mgj://detail?id=5&name=leeDev"]; // 打印出: showComposite _ id = 5 ; name = leeDev
- 参数
无固定格局
,也需求有文档办理url
入参说明 - 且
url
的参数传递受到限制,只能传递常规的字符串参数,无法传递非常规参数,如UIImage
、NSData
等类型 - 没有区别
本地调用
和长途调用
的状况,尤其是长途调用,会因为url
参数受限,导致一些功用受限 - 组件自身依靠了
中间件
,且涣散注册使的耦合较多 url硬编
- …
4.2 Protocol-Class服务注册绑定办法
1.) 代表结构
针对计划一的问题,蘑菇街又提出了另一种组件化的计划:
- 便是经过
protocol
界说服务接口
- 组件经过
protocol
接口,来拜访完结接口的模块界说的服务:- 具体完结便是把
protocol
和class
做一个映射 - 一起在内存中保存一张映射表
- 运用的时分,就经过
protocol
找到对应的class
来获取需求的服务。
- 具体完结便是把
2.) 音讯传递的中间件
-
[
ModuleManager
]
ModuleManager
是担任音讯传递的中间件 -
音讯传递办法:
-
register Module
注册模块- 经过
protocol
提前绑定 一个 模块的完结class
- 发布
protocol
给一切模块
- 经过
-
open Module
翻开模块- 经过
protocol
获取 绑定的class
- 初始化
class
对应的组件
进行运用
- 经过
-
-
传递音讯的中间件源码API:
/* // 注册 [ModuleManager registerClass:ClassA forProtocol:ProtocolA] //调用 [ModuleManager classForProtocol:ProtocolA] */ @interface ModuleManager : NSObject + (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName; + (Class)classForProtocolName:(NSString *)protocolName; @end @interface ModuleManager() @property (nonatomic,strong) NSMutableDictionary * map; @end @implementation ModuleManager - (instancetype)init { if (self = [super init]) { self.map = [NSMutableDictionary dictionary]; } return self; } + (ModuleManager *)shareManager { static dispatch_once_t onceToken; static ModuleManager * router = nil; dispatch_once(&onceToken, ^{ if (router == nil) { router = [[ModuleManager alloc] init]; } }); return router; } + (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName { [self shareManager].map[protocolName] = className; } + (Class)classForProtocolName:(NSString *)protocolName { NSString * className = [self shareManager].map[protocolName]; return NSClassFromString(className); } @end
3.) 小结:模块调用逻辑图+长处+缺陷
3.1) 模块调用逻辑图
protocol-class
模块调用逻辑图:
规划思维和计划一相似,都是经过给组件加了一层wrapper
:
3.2) 长处:
- 处理了计划一中无法传递
非常规参数
的问题,使得组件间的调用更为便利 - 结合计划1+计划2,能够区别
本地调用
和长途调用
的状况 - 经过
Protocol
能够有用约束
组件通讯时的传参个数,参数类型等 - 用
plist映射表
保护,代替内存中保护映射表
,相对更便利办理 -
体系工作
接入ModuleManager
,进行办理- 体系的一些工作会有通知,比方
applicationDidBecomeActive
会有对应的UIApplicationDidBecomeActiveNotification
- 组件假如要做响应的话,只需监听这个体系通知即可
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSArray *modules = [[ModuleManager sharedInstance] allModules]; for (id module in modules) { if ([module respondsToSelector:_cmd]) { [module application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } } }
- 体系的一些工作会有通知,比方
3.3) 缺陷:
- 仍然没有处理
组件依靠中间件
的问题 -
组件的涣散调用
的问题。涣散调用仍然会导致必定的耦合 一切模块,仍然得提前初始化
- 关于 体系 工作的 办理,需求
遍历全部已注册模块
,项目体量大时,会有必定的性能损耗
- 关于 模块 办理 短缺归类 区别,只用一维key-Value联系,过于粗犷
- …
4.3 Target-Action反射办法:依据Runtime
+OC反射机制
+分类
1.) 代表结构 CTMediator
[CTMediator工程目录]
|
|-[CTMediator]
| |-CTMediator.h.m
| |-[Categories]
| |-[ModuleA]
| |-CTMediator+CTMediatorModuleAActions.h.m
|
|-[DemoModule]
| |-[Actions]
| | |-Target_A.h.m
| |-DemoModuleADetailViewController.h.m
|
|-AppDelegate.h.m
|-ViewController.h.m
2.) 音讯传递的中间件
-
[
CTMediator
]
CTMediator
是担任音讯传递
的中间件; -
音讯传递办法:
- 1、把需求开放给其它模块的API,封装操作到Target_XXXModule类
- 2、经过给中间件
CTMediator
添加分类,凭借分类调用当时模块发布的API- 调用的办法是凭借
Runtime
里边的”Target-Action
“反射机制 - 防止对模块文件的引证
- 调用的办法是凭借
-
传递音讯的中间件源码API:
// 长途App调用进口 - (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion; // 本地组件调用进口 - (id _Nullable )performTarget:(NSString * _Nullable)targetName action:(NSString * _Nullable)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget; // 开释 Target - (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName;
- 小结:
-
Target
+Action
+Params
办法; -
url
办法(实践上也是【Target
+Action
+Params
办法】,url解析之后就变成【Target
+Action
+Params
】) - 回来值 为
id
类型- 可能是被调用的
Target
实例 - 可能是被调用的
nil
- 可能是被调用的
布尔值
,代表Target-Action
履行的结果 - …
- 可能是被调用的
-
- 小结:
-
[
DemoModule
]
一个例子模块,假设咱们要从其他事务(ViewController.h.m)中跳转到这个事务模块。
在这个demo中,咱们的意图是从其他事务(ViewController.h.m中)跳转到DemoModule事务模块。
3.) 小结:模块调用时序图+长处+缺陷
3.1) 模块调用运转时时序图:
调用联系概述:
- 首先由ViewController.h.m建议调用请求给
CTMediator
-
CTMediator
经过runtime
去调用目标模块DemoModule
- 目标模块
DemoModule
依据参数创立自己的一个实例,并把这个实例回来给CTMediator
-
CTMediator
再把这个实例回来给ViewController.h.m - (此刻ViewController.h.m不需求知道这个实例的具体类型,只需求知道是UIViewController的子类),然后由ViewController.h.m决议以什么样的办法去展示DemoModule。
进一步细化DemoModule
内部的调用逻辑(过程4、5):
每个过程的相关代码:
-
过程1:
ViewController.m
建议调用请求给CTMediator
CTMediator
经过分类CTMediator+CTMediatorModuleAActions.m
露出ModuleA
的APIUIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];
-
过程2:
CTMediator+CTMediatorModuleAActions.m
经过界说好的参数调用CTMediator
//因为CTMediator+CTMediatorModuleAActions是CTMediator的扩展,所以能够直接运用self来调用CTMediator的完结 UIViewController *viewController = [self performTarget:kCTMediatorTargetA action:kCTMediatorActionNativFetchDetailViewController params:@{@"key":@"value"}];
-
过程3:
CTMediator
依据CTMediator+CTMediatorModuleAActions.m
传过来的Target
、Action
、params
建议实践调用。
这个调用联系是经过 OC的运转时机制Runtime
完结的。
所以此处并不需求在代码上依靠被调用者,假如被调用者不存在,也能够在运转时进行处理。return [target performSelector:action withObject:params];
-
过程4/5:
Target_A
创立一个DemoModuleADetailViewController
类型的实例(这个实例是Target_A
经过DemoModuleADetailViewController
类的alloc/init
创立的)。DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
-
过程6:
Target_A
回来创立的实例到CTMediator.m
(建议时是经过runtime,过程3),回来后CTMediator.m
并不知道这个实例的具体类型,也不会对这个类进行任何解析操作,所以CTMediator.m
跟回来的实例之间是没有任何引证联系的。 -
过程7:
CTMediator.m
回来过程6中得到的实例到CTMediator+CTMediatorModuleAActions.m
(建议时是过程2)。 -
过程8:
CTMediator+CTMediatorModuleAActions.m
会将过程7回来的实例当作UIViewController
处理
接下来会在运转时判别这个实例的类型是不是UIViewController(是不是UIViewController的子类)。
然后将得到的UIViewController交给调用者ViewController.m(由ViewController.m担任以何种办法进行展示)
3.2) 一切类的功用:
-
CTMediator.h.m功用:
- 指定目标(
target
,类名)+动作(action
,办法名),并供给一个字典类型的参数。 -
CTMediator.h.m
会判别target-action
是否能够调用.假如能够,则调用- 因为这一功用是经过
Runtime
动态完结的,所以在CTMediator.h.m
的完结中,不会依靠任何其他模块,也不需求知道target-action
的具体功用 - 只需
target-action
存在,就会被履行(target-action
具体的功用由DemoModule
自己担任)。
- 因为这一功用是经过
-
CTMediator.h
里实践供给了两个办法:- 别离处理
url办法的调用
和target-action办法的调用
- 其间,假如运用url办法,会自动把url转换成target-action。
- 别离处理
- 指定目标(
-
CTMediator+CTMediatorModuleAActions.h.m功用:
-
CTMediator
的扩展,用于办理跳转到DemoModule
模块的动作。 - 其他模块想要跳转到
DemoModule
模块时,经过调用这个类的办法来完结。 - 但是这个类中,并不真正去做跳转的动作,它仅仅对
CTMediator.h.m
类的封装, - 这样开发者就不需求关心运用
CTMediator.h.m
跳转到DemoModule
模块时具体需求的target
称号和action
称号了。 -
CTMediator.h.m
+CTMediator+CTMediatorModuleAActions.h.m
一起组成了一个面向DemoModule
的跳转,而且它不会在代码上依靠DemoModule
,DemoModule
是否供给了相应的跳转功用,只体现在运转时是否能够正常跳转。 - 至此,
CTMediator
这个中间层完结了完全的独立,其他模块不需求预先注册,CTMediator
也不需求知道其他模块的完结细节。 - 仅有的相关便是需求
CTMediator+CTMediatorModuleAActions.h.m
中写明正确的target
+action
和正确的参数
- 而且这些
action
和参数只依靠于Target_A.h.m
-
action
和参数的
正确性只会在运转时查看 - 假如
target
或action
不存在,能够在CTMediator.h.m
中进行相应的处理。 - 即:
CTMediator
不需求依靠任何模块就能够编译运转。
- 而且这些
-
-
Target_A.h.m
- 供给了跳转到
DemoModule
模块的对外接口
- 与
CTMediator+CTMediatorModuleAActions.h.m
彼此对应,能够说它只用来为CTMediator+CTMediatorModuleAActions.h.m
供给服务,所以在完结CTMediator+CTMediatorModuleAActions.h.m
时只需求参考Target_A.h.m
即可,足够简略以至于并不需求文档来辅佐描绘。 - 其他模块想跳转到这个模块时,不能直接经过
Target_A.h.m
完结,而是要经过CTMediator+CTMediatorModuleAActions.h.m
来完结。这样,就完结了模块间彼此不依靠,而且只需需求跳转到其他模块的当地,才需求依靠CTMediator
。
- 供给了跳转到
3.3) 长处:
-
模块
经过中间件
解析,再由中间件发送音讯通讯 - 中间件经过
runtime
接口解耦 - 经过
target-action
简化写法,调用简略便利 - 代码
自动补全
和编译时查看
都仍然有用 - 经过
category
感官上别离组件接口代码
3.4) 缺陷:
-
Category
存在重名覆盖的危险,需求经过开发标准以及一些查看机制来规避 - 处理
模块间
彼此 引证 耦合严峻的 问题, 但 一套API 需求必定的重复的封装,对编程提效,不是很友爱- 每添加一个模块,就要弥补一个
Category
完结,一个TargetModule
完结,模块多了,这个得想办法恰当削减 -
模块A
想要调用模块B
的一套API
,模块B
就得自己模块内部完结一遍,且经过必定的办法 露出给模块A
,
- 每添加一个模块,就要弥补一个
-
Mediator
内部存在必定的参数硬编
- 项目体量大的时分,具有必定的 保护性 难度
- 对开发功率有必定影响
- 并不友爱,且写法欠高雅
- 命名规矩并不美观且相对复杂:每个
Target
都得加Target_A
,每个Action
都得加Action_
- 调用
Mediator
API 的 回来值 是什么 无法不清晰知晓,很不友爱,还得编写必定的类型查看
代码 - …
4.4 模块服务办理东西:模块服务注册+URL-HandlerBlock路由注册表计划
1.) 代表结构 :BeeHive
[BeeHive工程目录]
|
|-[BeeHive]
| |-BHAnnotation.h.m
| |-BHModuleProtocol.h
| |-BHModuleManager.h.m
| |
| |-BHRouter.h.m
| |
| |-BHServiceProtocol.h
| |-BHServiceManager.h.m
| |
| |-BHAppDelegate.h.m
| |-BHContext.h.m
| |-BHConfig.h.m
| |-BeeHive.h.m
| |-BeeHive.bundle
| | | - BeeHive.plist
| | | - BHService.plist
| |
| |-BHTimeProfiler.h.m
| |-BHWatchDog.h.m
| |
| |- BHDefines.h
| |- BHCommon.h
| |
2.) 音讯传递的中间件
-
[
BeeHive
]-
BHModuleManager
是担任 本运用内音讯传递
的中间件; -
BHRouter
是担任 跨运用服务音讯传递
的中间件;
-
-
音讯传递办法:
经过处理Event
编写各个事务模块能够完结插件化编程
各事务模块之间没有任何依靠,core
与module
之间经过event
交互,完结了插件阻隔。- 依据接口的完结
Service
拜访办法(Java spring
结构完结)-
BHModuleManager
-
音讯分类
+模块音讯界说
+模块注册
+模块调用
-
BeeHive
模块办理东西把工作区别为三类进行办理:
体系级工作
、运用级工作
、事务自界说工作
-
体系工作
通常是Application
生命周期工作 - 在
体系工作
的基础之上,扩展了运用的通用工作
例如modSetup
、modInit
等,能够用于编码完结各插件模块的设置与初始化 - 假如觉得
体系工作
、通用工作
不足以满意需求,能够经过承继BHAppdelegate
来扩展当时运用的事务自界说工作
(BHAppdelgate
内原本就封装了对体系工作、通用工作的处理)
-
-
模块注册
之后就能够 给 其它模块 进行调用 - 能够经过给 模块办理中间件
BHModuleManager
发送 另一个模块
的音讯调用另一个模块 - 也能够经过 [[BeeHive shareInstance] createService:@protocol(XXXServiceProtocol)] 直接获取一个服务
-
-
-
- 依据跨运用完结的
URL Route
办法(iPhone
App
之间的互访)-
BHRouter
相似于 蘑菇街 的URL-HandlerBlock
计划 - 支撑
App间
互访,也支撑Hybrid开发时,经过训练拜访web事务包
(相似于小程序技能)
-
- 依据接口的完结
2.1) 了解其结构规划
1、完结特性
- 插件化的模块开发运转结构
- 模块具体完结与接口调用别离
- 模块生命周期办理,扩展了运用的体系工作
2、规划准则
- 因为依据
Spring
的Service
理念,尽管能够使模块间的具体完结与接口解耦,但无法防止对接口类的依靠联系。 - 为什么不运用
invoke
以及动态链接库技能完结对接口完结的解耦,相似Apache
的DSO
的办法?- 主要是考虑学习本钱难度
- 以及动态调用完结无法在编译查看阶段检测接口参数变更等问题
- 动态技能需求更高的编程门槛要求。
3、项目名来源
-
BeeHive
灵感来源于蜂窝 - 蜂窝是世界上高度模块化的工程结构,六边形的规划能带来无限扩张的可能
- 所以作者用了
BeeHive
来做为这个项意图命名
4、模块生命周期的工作
BeeHive
会给每个模块供给生命周期工作,用于与BeeHive
宿主环境进行必要信息交互,感知模块生命周期的改变。
2.2)了解结构工作分层
工作分为三种类型:
- 体系工作
- 通用工作
- 事务自界说工作
1、体系工作
体系工作通常是Application
生命周期工作,例如DidBecomeActive
、WillEnterBackground
等。
体系工作根本作业流如下:
2、通用工作
在体系工作的基础之上,扩展了运用的通用工作,例如modSetup
、modInit
等,能够用于编码完结各插件模块的设置与初始化。
扩展的通用工作如下:
3、事务自界说工作
假如觉得体系工作、通用工作不足以满意需求,作者还将工作封装简化成BHAppdelgate
,你能够经过承继 BHAppdelegate
来扩展自己的工作。
2.3) 模块注册办法
模块注册的办法有静态注册与动态注册两种。
1、 静态注册
经过在BeeHive.plist
文件中注册符合BHModuleProtocol
协议模块类:
2、 动态注册
@implementation HomeModule
BH_EXPORT_MODULE() // 声明该类为模块进口
@end
在模块进口类完结中 运用BH_EXPORT_MODULE()
宏声明该类为模块进口完结类。
3、异步加载模块
假如设置模块导出为BH_EXPORT_MODULE(YES)
,则会在发动之后第一屏内容展示之前异步履行模块的初始化,能够优化发动时时间消耗。
2.4) 编程开发运用
BHModuleProtocol
为各个模块供给了每个模块能够Hook
的函数,用于完结插件逻辑以及代码完结。
1、设置环境变量
经过context.env
能够判别咱们的运用环境状况来决议咱们怎么装备咱们的运用。
-(void)modSetup:(BHContext *)context{
switch (context.env) {
case BHEnvironmentDev:
//....初始化开发环境
break;
case BHEnvironmentProd:
//....初始化出产环境
default:
break;
}
}
2、模块初始化
假如模块有需求发动时初始化的逻辑,能够在modInit
里编写,例如模块注册一个外部模块能够拜访的Service
接口
-(void)modInit:(BHContext *)context {
//注册模块的接口服务
[[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
}
3、处理体系工作
体系的工作会被传递给每个模块,让每个模块自己决议编写事务处理逻辑,比方3D-Touch
功用
-(void)modQuickAction:(BHContext *)context{
[self process:context.shortcutItem handler:context.scompletionHandler];
}
4、模间调用
经过处理Event
编写各个事务模块能够完结插件化编程,各事务模块之间没有任何依靠,core
与module
之间经过event
交互,完结了插件阻隔。但有时分咱们需求模块间的彼此调用某些功用来协同完结功用。
结构供给了两种办法的接口拜访办法:
- 依据接口的完结
Service
拜访办法(Java spring
结构完结) - 依据跨运用完结的
URL Route
办法(iPhone
App
之间的互访)
5、 界说接口
以为HomeServiceProtocol
为例。
@protocol HomeServiceProtocol <NSObject, BHServiceProtocol>
- (void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName;
@end
6、注册
Service
有两种办法:
-
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>
7、调用
Service
#import "BHService.h"
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
8、单例与多例
关于有些场景下,咱们拜访每个声明Service
的目标,期望目标能保留一些状况,那咱们需求声明这个Service
目标是一个单例目标。
咱们只需求在Service
目标中完结工作函数声明
-(BOOL) singleton{
return YES;
}
经过createService
获取的目标则为单例目标,假如完结上面函数回来的是NO
,则createService
回来的是多例。
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
9、上下文环境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; }
3.) 小结:作业流图+架构图+核心类联系图
+长处+缺陷
3.1) 作业流图+架构图+核心类联系图
-
体系工作根本作业流:
-
扩展的通用工作+体系工作根本作业流:
-
根本架构图:
-
BeeHive具体架构图:
-
核心类联系图:
3.2) 一切类的功用如下:
-
BHModuleManager
- 担任一切工作(
体系级工作
、运用级工作
、事务自界说工作
)的音讯传递 -
BHModuleManager
对外发布的API 如下://模型等级,用于分层规划 typedef NS_ENUM(NSUInteger, BHModuleLevel){ BHModuleBasic = 0, BHModuleNormal = 1 }; typedef NS_ENUM(NSInteger, BHModuleEventType){ /// #pragma mark - 1-通用运用级工作 BHMSetupEvent = 0, BHMInitEvent, BHMTearDownEvent, BHMSplashEvent, /// #pragma mark - 2-体系级工作 BHMQuickActionEvent, BHMWillResignActiveEvent, BHMDidEnterBackgroundEvent, BHMWillEnterForegroundEvent, BHMDidBecomeActiveEvent, BHMWillTerminateEvent, BHMUnmountEvent, // 运用交互 BHMOpenURLEvent, // 内存警告 BHMDidReceiveMemoryWarningEvent, // 本地推送、长途推送 BHMDidFailToRegisterForRemoteNotificationsEvent, BHMDidRegisterForRemoteNotificationsEvent, BHMDidReceiveRemoteNotificationEvent, BHMDidReceiveLocalNotificationEvent, BHMWillPresentNotificationEvent, BHMDidReceiveNotificationResponseEvent, // Widget工作 BHMWillContinueUserActivityEvent, BHMContinueUserActivityEvent, BHMDidFailToContinueUserActivityEvent, BHMDidUpdateUserActivityEvent, // 手表工作 BHMHandleWatchKitExtensionRequestEvent, /// #pragma mark - 3-事务自界说工作 BHMDidCustomEvent = 1000 }; @class BHModule; @interface BHModuleManager : NSObject + (instancetype)sharedManager; // If you do not comply with set Level protocol, the default Normal - (void)registerDynamicModule:(Class)moduleClass; - (void)registerDynamicModule:(Class)moduleClass shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent; - (void)unRegisterDynamicModule:(Class)moduleClass; - (void)loadLocalModules; - (void)registedAllModules; - (void)registerCustomEvent:(NSInteger)eventType withModuleInstance:(id)moduleInstance andSelectorStr:(NSString *)selectorStr; - (void)triggerEvent:(NSInteger)eventType; - (void)triggerEvent:(NSInteger)eventType withCustomParam:(NSDictionary *)customParam; @end
-
BHModuleManager
经过调用triggerEvent
API来处理一切的音讯工作传递:- (void)triggerEvent:(BHModuleEventType)eventType { switch (eventType) { case BHMSetupEvent: [self handleModuleEvent:kSetupSelector]; break; case BHMInitEvent: //special [self handleModulesInitEvent]; break; case BHMTearDownEvent: //special [self handleModulesTearDownEvent]; break; case BHMSplashEvent: [self handleModuleEvent:kSplashSeletor]; break; case BHMWillResignActiveEvent: [self handleModuleEvent:kWillResignActiveSelector]; break; case BHMDidEnterBackgroundEvent: [self handleModuleEvent:kDidEnterBackgroundSelector]; break; case BHMWillEnterForegroundEvent: [self handleModuleEvent:kWillEnterForegroundSelector]; break; case BHMDidBecomeActiveEvent: [self handleModuleEvent:kDidBecomeActiveSelector]; break; case BHMWillTerminateEvent: [self handleModuleEvent:kWillTerminateSelector]; break; case BHMUnmountEvent: [self handleModuleEvent:kUnmountEventSelector]; break; case BHMOpenURLEvent: [self handleModuleEvent:kOpenURLSelector]; break; case BHMDidReceiveMemoryWarningEvent: [self handleModuleEvent:kDidReceiveMemoryWarningSelector]; break; case BHMDidReceiveRemoteNotificationEvent: [self handleModuleEvent:kDidReceiveRemoteNotificationsSelector]; break; case BHMDidFailToRegisterForRemoteNotificationsEvent: [self handleModuleEvent:kFailToRegisterForRemoteNotificationsSelector]; break; case BHMDidRegisterForRemoteNotificationsEvent: [self handleModuleEvent:kDidRegisterForRemoteNotificationsSelector]; break; case BHMDidReceiveLocalNotificationEvent: [self handleModuleEvent:kDidReceiveLocalNotificationsSelector]; break; case BHMWillContinueUserActivityEvent: [self handleModuleEvent:kWillContinueUserActivitySelector]; break; case BHMContinueUserActivityEvent: [self handleModuleEvent:kContinueUserActivitySelector]; break; case BHMDidFailToContinueUserActivityEvent: [self handleModuleEvent:kFailToContinueUserActivitySelector]; break; case BHMDidUpdateUserActivityEvent: [self handleModuleEvent:kDidUpdateContinueUserActivitySelector]; break; case BHMQuickActionEvent: [self handleModuleEvent:kQuickActionSelector]; break; default: [BHContext shareInstance].customEvent = eventType; [self handleModuleEvent:kAppCustomSelector]; break; } }
-
1.1 体系工作办理:
-
BHAppDelegate
引进BHAppDelegate
接收体系工作
把原本体系工作回调监听(也便是原本Appdelegate遵守的UIApplicationDelegate协议办法)纳入模块工作办理: - 体系工作通常是
Application
生命周期工作,例如DidBecomeActive
、WillEnterBackground
等 - 体系
工作参数
都被当作环境变量缓存了起来,一切模块同享
- 体系工作根本作业流如下:
-
体系工作音讯传递源码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[BHModuleManager sharedManager] triggerEvent:BHMSetupEvent]; [[BHModuleManager sharedManager] triggerEvent:BHMInitEvent]; dispatch_async(dispatch_get_main_queue(), ^{ [[BHModuleManager sharedManager] triggerEvent:BHMSplashEvent]; }); #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0f) { [UNUserNotificationCenter currentNotificationCenter].delegate = self; } #endif #ifdef DEBUG [[BHTimeProfiler sharedTimeProfiler] saveTimeProfileDataIntoFile:@"BeeHiveTimeProfiler"]; #endif return YES; } #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400 -(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler { [[BeeHive shareInstance].context.touchShortcutItem setShortcutItem: shortcutItem]; [[BeeHive shareInstance].context.touchShortcutItem setScompletionHandler: completionHandler]; [[BHModuleManager sharedManager] triggerEvent:BHMQuickActionEvent]; } #endif - (void)applicationWillResignActive:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMWillResignActiveEvent]; } - (void)applicationDidEnterBackground:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMDidEnterBackgroundEvent]; } - (void)applicationWillEnterForeground:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMWillEnterForegroundEvent]; } - (void)applicationDidBecomeActive:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMDidBecomeActiveEvent]; } - (void)applicationWillTerminate:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMWillTerminateEvent]; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { [[BeeHive shareInstance].context.openURLItem setOpenURL:url]; [[BeeHive shareInstance].context.openURLItem setSourceApplication:sourceApplication]; [[BeeHive shareInstance].context.openURLItem setAnnotation:annotation]; [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent]; return YES; } #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options { [[BeeHive shareInstance].context.openURLItem setOpenURL:url]; [[BeeHive shareInstance].context.openURLItem setOptions:options]; [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent]; return YES; } #endif - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveMemoryWarningEvent]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [[BeeHive shareInstance].context.notificationsItem setNotificationsError:error]; [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToRegisterForRemoteNotificationsEvent]; } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [[BeeHive shareInstance].context.notificationsItem setDeviceToken: deviceToken]; [[BHModuleManager sharedManager] triggerEvent:BHMDidRegisterForRemoteNotificationsEvent]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo]; [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo]; [[BeeHive shareInstance].context.notificationsItem setNotificationResultHander: completionHandler]; [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent]; } - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [[BeeHive shareInstance].context.notificationsItem setLocalNotification: notification]; [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveLocalNotificationEvent]; } #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80000 - (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity { if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){ [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity]; [[BHModuleManager sharedManager] triggerEvent:BHMDidUpdateUserActivityEvent]; } } - (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error { if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){ [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType]; [[BeeHive shareInstance].context.userActivityItem setUserActivityError: error]; [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToContinueUserActivityEvent]; } } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler { if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){ [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity]; [[BeeHive shareInstance].context.userActivityItem setRestorationHandler: restorationHandler]; [[BHModuleManager sharedManager] triggerEvent:BHMContinueUserActivityEvent]; } return YES; } - (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType { if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){ [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType]; [[BHModuleManager sharedManager] triggerEvent:BHMWillContinueUserActivityEvent]; } return YES; } #endif #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { [[BeeHive shareInstance].context.notificationsItem setNotification: notification]; [[BeeHive shareInstance].context.notificationsItem setNotificationPresentationOptionsHandler: completionHandler]; [[BeeHive shareInstance].context.notificationsItem setCenter:center]; [[BHModuleManager sharedManager] triggerEvent:BHMWillPresentNotificationEvent]; }; - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler { [[BeeHive shareInstance].context.notificationsItem setNotificationResponse: response]; [[BeeHive shareInstance].context.notificationsItem setNotificationCompletionHandler:completionHandler]; [[BeeHive shareInstance].context.notificationsItem setCenter:center]; [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveNotificationResponseEvent]; }; #endif
-
体系工作参数
都存在BHContext
的公开特点列表
-
- 担任一切工作(
-
1.2 通用运用级工作:
在体系工作的基础之上,扩展了运用的通用工作,例如modSetup
、modInit
、modSplash
,能够用于编码完结各插件模块的设置与初始化- 扩展的通用工作如下:
-
1.3 事务自界说工作:
- 假如
体系工作
、通用工作
仍不满意运用完结需求,咱们能够添加事务自界说工作
- 咱们能够经过承继
BHAppdelegate
来扩展自己的工作,在咱们自己的Appdelegate
注册更多的工作。(关于模块的注册,咱们在后面再进一步深化了解) - 自界说的工作的type值便是
BHMDidCustomEvent >= 1000
- 在
BHModuleManager
内有一个API专门处理事务自界说工作
- (void)registerCustomEvent:(NSInteger)eventType withModuleInstance:(id)moduleInstance andSelectorStr:(NSString *)selectorStr { if (eventType < 1000) { return; } [self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr]; }
- 假如
- 在
BHModuleManager
中有2个特殊工作:-
BHMInitEvent
工作: 初始化Module模块的工作,关键代码
- (void)handleModulesInitEventForTarget:(id<BHModuleProtocol>)target withCustomParam:(NSDictionary *)customParam { BHContext *context = [BHContext shareInstance].copy;】 context.customParam = customParam; context.customEvent = BHMInitEvent; NSArray<id<BHModuleProtocol>> *moduleInstances; if (target) { moduleInstances = @[target]; } else { moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMInitEvent)]; } [moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) { __weak typeof(&*self) wself = self; void ( ^ bk )(void); bk = ^(){ __strong typeof(&*self) sself = wself; if (sself) { if ([moduleInstance respondsToSelector:@selector(modInit:)]) { [moduleInstance modInit:context]; } } }; [[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- modInit:", [moduleInstance class]]]; if ([moduleInstance respondsToSelector:@selector(async)]) { BOOL async = [moduleInstance async]; if (async) { dispatch_async(dispatch_get_main_queue(),^{ bk(); }); } else { bk(); } } else { bk(); } }]; }
- 遍历
BHModules
数组 - 顺次对每个
Module
实例调用modInit:
办法; - 这里会有
异步加载
的问题: - 假如moduleInstance重写了
async
办法,那么就会依据这个办法回来的值来进行是否异步加载的判别 - 默许不异步
-
modInit:
办法里边干许多工作:- 比方说
对环境的判别
,依据环境的不同初始化不同的办法:-(void)modInit:(BHContext *)context { switch (context.env) { case BHEnvironmentDev: //....初始化开发环境 break; case BHEnvironmentProd: //....初始化出产环境 default: break; } }
- 再比方,
注册一个事务服务
或初始化模块的某事务服务,给其传参
:-(void)modInit:(BHContext *)context { NSLog(@"模块初始化中"); NSLog(@"%@",context.moduleConfigName); // [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]]; id<TradeServiceProtocol> service = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)]; service.itemId = @"我是单例"; }
- …
- 比方说
- 遍历
-
BHMTearDownEvent
工作:卸载Module模块的工作,关键代码
:- (void)handleModulesTearDownEventForTarget:(id<BHModuleProtocol>)target withCustomParam:(NSDictionary *)customParam { BHContext *context = [BHContext shareInstance].copy; context.customParam = customParam; context.customEvent = BHMTearDownEvent; NSArray<id<BHModuleProtocol>> *moduleInstances; if (target) { moduleInstances = @[target]; } else { moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMTearDownEvent)]; } //Reverse Order to unload for (int i = (int)moduleInstances.count - 1; i >= 0; i--) { id<BHModuleProtocol> moduleInstance = [moduleInstances objectAtIndex:i]; if (moduleInstance && [moduleInstance respondsToSelector:@selector(modTearDown:)]) { [moduleInstance modTearDown:context]; } } }
- 因为
Module
是有优先级Level
,所以撤除的时分需求从低优先级开端拆
- 即数组逆序循环
- 对每个
Module
实例发送modTearDown:
工作即可
-
-
BHServiceManager
: 在BeeHive
内部专门用于办理服务
(注册
、反注册
、查询
)的东西类-
核心API:
#import <Foundation/Foundation.h> @class BHContext; @interface BHServiceManager : NSObject @property (nonatomic, assign) BOOL enableException; + (instancetype)sharedManager; - (void)registerLocalServices; - (void)registerService:(Protocol *)service implClass:(Class)implClass; - (id)createService:(Protocol *)service; - (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName; - (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache; - (id)getServiceInstanceFromServiceName:(NSString *)serviceName; - (void)removeServiceWithServiceName:(NSString *)serviceName; @end
-
核心API:
-
BHRouter
: 是一个专门担任处理URL-HandlerBlock
类 长途 APP工作的 插件-
对外发布的API和相应的介绍如下:
// 协议头Key static NSString *const BHRURLSchemeGlobalKey = @"URLGlobalScheme"; // 【路由表】工作分类: static NSString *const BHRURLHostCallService = @"call.service.beehive";//调用服务 static NSString *const BHRURLHostRegister = @"register.beehive";//注册服务 static NSString *const BHRURLHostJumpViewController = @"jump.vc.beehive";//跳转页面 //【路由URL规矩】: static NSString *const BHRURLSubPathSplitPattern = @".";//url途径分节点的标识 static NSString *const BHRURLQueryParamsKey = @"params";//url途径传参Key static NSString *const BHRURLFragmentViewControlerEnterModePush = @"push";//页面翻开的办法Push static NSString *const BHRURLFragmentViewControlerEnterModeModal = @"modal"; //页面翻开的办法Model typedef void(^BHRPathComponentCustomHandler)(NSDictionary<NSString *, id> *params);//HandlerBlock的类型界说 @interface BHRouter : NSObject - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; + (instancetype)globalRouter;//模块办理东西默许协议头 + (instancetype)routerForScheme:(NSString *)scheme;//新增一个协议头对应的路由表 + (void)unRegisterRouterForScheme:(NSString *)scheme;//开释一个某协议头的路由表 + (void)unRegisterAllRouters;//开释一切缓存好的路由表 //handler is a custom module or service solve function - (void)addPathComponent:(NSString *)pathComponentKey forClass:(Class)mClass; - (void)addPathComponent:(NSString *)pathComponentKey forClass:(Class)mClass handler:(BHRPathComponentCustomHandler)handler; - (void)removePathComponent:(NSString *)pathComponentKey; //url - > com.alibaba.beehive://call.service.beehive/pathComponentKey.protocolName.selector/...?params={}(value url encode) //url - > com.alibaba.beehive://register.beehive/pathComponentKey.protocolName/...?params={}(value url encode) //url - > com.alibaba.beehive://jump.vc.beehive/pathComponentKey.protocolName.push(modal)/...?params={}(value url encode)#push //params -> {pathComponentKey:{paramName:paramValue,...},...} //when call service, paramName = @1,@2,...(order of paramValue) + (BOOL)canOpenURL:(NSURL *)URL; + (BOOL)openURL:(NSURL *)URL; + (BOOL)openURL:(NSURL *)URL withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params; + (BOOL)openURL:(NSURL *)URL withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params andThen:(void(^)(NSString *pathComponentKey, id obj, id returnValue))then; @end
-
BHRouter
支撑依据一个新的协议头
,新增一个路由表 , 这是开发者 考虑到了 一套代码多套运用的场景!-
商家端
、买家端
一体的 电商 -
内部职工
版别 与To C
版别 一体的 运用 -
To B
版别 与To C
版别 一体的 运用 - 互联网金融银行银行ToC端,有时分会依据不同的地级市也有不同的版别,
多套运用一体
,可随时切换 - …
-
-
BHRouter
与 蘑菇街 的URL-HandlerBlock
不同的当地:-
BeeHive
把App内部工作和 跨运用工作 的 治理 分开了; -
BHRouter
专门担任长途工作
、跨运用工作
;
-
-
对外发布的API和相应的介绍如下:
-
BHContext
:单例
imp文件内部有两个字典,用于存储模块信息
(modulesByName
)和服务信息
(servicesByName
)- 发布一些
存储环境变量的特点
和和对服务办理三个API
如下:#import <Foundation/Foundation.h> #import "BHServiceProtocol.h" #import "BHConfig.h" #import "BHAppDelegate.h" typedef enum{ BHEnvironmentDev = 0, BHEnvironmentTest, BHEnvironmentStage, BHEnvironmentProd }BHEnvironmentType; @interface BHContext : NSObject <NSCopying> //global env @property(nonatomic, assign) BHEnvironmentType env; //global config @property(nonatomic, strong) BHConfig *config; //application appkey @property(nonatomic, strong) NSString *appkey; //customEvent>=1000 @property(nonatomic, assign) NSInteger customEvent; @property(nonatomic, strong) UIApplication *application; @property(nonatomic, strong) NSDictionary *launchOptions; @property(nonatomic, strong) NSString *moduleConfigName; @property(nonatomic, strong) NSString *serviceConfigName; //3D-Touch model #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400 @property (nonatomic, strong) BHShortcutItem *touchShortcutItem; #endif //OpenURL model @property (nonatomic, strong) BHOpenURLItem *openURLItem; //Notifications Remote or Local @property (nonatomic, strong) BHNotificationsItem *notificationsItem; //user Activity Model @property (nonatomic, strong) BHUserActivityItem *userActivityItem; //watch Model @property (nonatomic, strong) BHWatchItem *watchItem; //custom param @property (nonatomic, copy) NSDictionary *customParam; + (instancetype)shareInstance; - (void)addServiceWithImplInstance:(id)implInstance serviceName:(NSString *)serviceName; - (void)removeServiceWithServiceName:(NSString *)serviceName; - (id)getServiceInstanceFromServiceName:(NSString *)serviceName; @end
- 在
运用发动
的时分,能够初始化一些上下文信息
:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ [BHContext shareInstance].application = application; [BHContext shareInstance].launchOptions = launchOptions; [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默以为BeeHive.bundle/BeeHive.plist [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService"; [BeeHive shareInstance].enableException = YES; [[BeeHive shareInstance] setContext:[BHContext shareInstance]]; [[BHTimeProfiler sharedTimeProfiler] recordEventTime:@"BeeHive::super start launch"]; [super application:application didFinishLaunchingWithOptions:launchOptions]; ... return YES; }
- 发布一些
-
BHConfig
:单例
保存了一个config
的NSMutableDictionary字典
字典责任是保护一些动态的环境变量,作为BHContext
的弥补存在;API:#import <Foundation/Foundation.h> @interface BHConfig : NSObject + (instancetype)shareInstance; + (id)get:(NSString *)key; + (BOOL)has:(NSString *)key; + (void)add:(NSDictionary *)parameters; + (NSMutableDictionary *)getAll; + (NSString *)stringValue:(NSString *)key; + (NSDictionary *)dictionaryValue:(NSString *)key; + (NSInteger)integerValue:(NSString *)key; + (float)floatValue:(NSString *)key; + (BOOL)boolValue:(NSString *)key; + (NSArray *)arrayValue:(NSString *)key; + (void)set:(NSString *)key value:(id)value; + (void)set:(NSString *)key boolValue:(BOOL)value; + (void)set:(NSString *)key integerValue:(NSInteger)value; + (void)clear; @end
-
BeeHive
:结构作者用这个类来发布结构一切的插件API- 在
BeeHive
里边也有一个triggerCustomEvent:
办法便是用来处理这些工作的,尤其是处理自界说工作的:+ (void)triggerCustomEvent:(NSInteger)eventType { if(eventType < 1000) { return; } [[BHModuleManager sharedManager] triggerEvent:eventType]; }
-
BeeHive
公开的API:#import <Foundation/Foundation.h> #import "BHModuleProtocol.h" #import "BHContext.h" #import "BHAppDelegate.h" #import "BHModuleManager.h" #import "BHServiceManager.h" @interface BeeHive : NSObject //save application global context @property(nonatomic, strong) BHContext *context; @property (nonatomic, assign) BOOL enableException; + (instancetype)shareInstance; + (void)registerDynamicModule:(Class) moduleClass; - (id)createService:(Protocol *)proto; //Registration is recommended to use a static way - (void)registerService:(Protocol *)proto service:(Class) serviceClass; + (void)triggerCustomEvent:(NSInteger)eventType; @end
- 在
BeeHive
设置 相关着全局环境参数 的上下文目标
的时分,会默许加载本地服务列表中一切的服务和模块
- 在
-
BeeHive.bundle
bundle内部存储两个plist:-
BeeHive.plist
: -
moduleClasses
存储的是一组模块
信息,每个模块信息都用一个字段存储。字段注解:-
moduleClass
: 模块类名 -
moduleLevel
: 模块工作等级(Integer值,值越大。模块分层的辅佐值) -
modulePriority
: 模块工作优先级(Integer值,值越大。同级模块下,分工作的优先级的辅佐值)
-
-
BHService.plist
-
service
: 供给的服务露出的接口文件(协议名) -
impl
: 服务的具体完结类类名
-
-
-
BHTimeProfiler
:
BHTimeProfiler
便是用来进行核算时间性能方面的Profiler -
BHWatchDog
:
能够开一个线程,设置好handler,每隔一段时间就履行一个handler
3.3) 长处:
- 在
MGJRouter
、CTMediator
、BeeHive
三者间,归于最优异的结构 - 在模块化开发中,用
Context
办理环境变量,全局信息同享的计划优于用一个Constant文件办理 - 依据接口
Service
拜访,长处是能够编译时查看发现接口的变更,然后及时修正接口问题- 对模块办理进行
归类办理
,进步了工作的有条理性、可读性、可保护性- 模块办理分Level层级
- 同级 模块 分 优先级
- 经过 静态文件 plist 办理,添加了可保护性
- 凭借中间件,经过event的办法分发工作音讯,模块办理的接口规矩且一致
- 对模块办理进行
- 依据接口
URL Route
拜访- 支撑 添加 映射表,为事务模块进一步分级供给了可能性
- 支撑 添加 映射表,为单进程多运用开发供给了快捷性
-
URL Route
内部 完结,可拜访 模块服务 -
URL Route
完结,也用了Target-Action
- 模块注册办法有两种
-
静态注册
,更倾向于 运用开发 时运用,为运用保护供给便利性 -
动态注册
,更倾向于 SDK 开发 时运用,为代码封装供给便利性
-
- 接口简练,易上手
- …
3.4) 缺陷:
- 依据接口
Service
拜访,缺陷是需求依靠接口界说的头文件,经过模块添加得越多,保护接口界说也有必定作业量 -
BeeHive
模块办理功用全面,适合有必定体量的大运用项目。关于小项目仍是太重了,能够进一步拆分子库- 针对SDK开发区别
- 针对小项目拆分
- 针对大项目,选取结构其部分功用,作为代替计划拆分
- 针对不同
模块
的分级办理不行清晰,能够进一步区别办理 - …
四、模块与模块办理
至此,关于模块与模块办理功用。咱们结合前面的剖析与介绍,咱们能够做一下小结:
1. 运用项目、SDK等的模块区别
针对大项目:
- 进行模块三级分类:
三级模块+组件+插件``运用级模块
、事务级模块
、功用级模块
-
运用级模块
=事务级模块1
+事务级模块2
+事务级模块3
…… -
事务级模块
= 若干功用级模块
+ 若干UI组件
-
- 模块内部能够进一步区别组件:
-
功用级模块
=功用组件A
+功用组件B
+功用组件C
- …
-
针对小项目:
- 在小项目中【模块与组件】的概念
往往混合运用
,不怎么区别 - 模块区别的颗粒度不必太详尽,适宜即可; 参考:
二级模块+组件+插件
- 如:
运用级模块
、事务级模块
、功用组件
、UI组件
、第三方社会化共享插件
…
- 如:
针对SDK:
在SDK/功用模块Pod库内部区别往往是按一级模块
/二级模块
区别处理的:
-
功用模块
= 若干功用插件
+若干功用组件
+若干UI组件
-
事务服务模块
= 若干功用模块
+若干事务UI组件
2.模块的集成依靠:
- 壳工程集成运用级模块
- 运用级模块 依靠 事务级模块
- 事务级模块 依靠 功用模块组
- 功用模块 依靠 功用组件 + UI组件
3.模块的解耦开发与模块间的通讯办理:
- 工作分类:
- 体系工作
- 运用事务工作
- 自界说工作
- 由模块办理者中间件作为模块间音讯通讯的枢纽
模块间通讯的办法:- 1.URL+Router
- 担任 跨运用 服务 的 模块通讯
- 担任 Hybrid计划 的 本地H5 事务包 模块 的通讯(相似于微信的小程序)
- 担任 Hybrid计划 的 长途Web 事务页面 模块 的通讯
- …
- 2.依据接口的完结
Service
拜访
- 1.URL+Router
4.区别模块办理东西小功用模块
(为了便利组合,使其在适应【小项目、大项目、SDK开发】时,导入的办理东西不那么重)
- 通讯的办法区别为小模块
- 工作办理区别
- 注册办法区别
- 添加模块的办理层级
5.依据 阿里的 BeeHive 进行二次开发
在做这个模块办理东西的时分,我挑选 依据 阿里的 BeeHive 进行二次开发。原因如下:
-
功用相对完善且安稳:
阿里的模块办理东西现已历过若干个大项意图检验,功用相对完善且安稳
- 天猫、淘宝、付出宝等若干
-
下降学习本钱:
许多开发者在比较 探求 组件化开发的过程中,现已对BeeHive
这个东西相对了解, -
结构的姓名:
一个恰切的姓名,便是一个恰切的界说。
关于结构的准确界说,是一个优异的插件成功的重要因素-
BeeHive
这个称号把模块的拔插描绘的恰到好处:-
BeeHive
灵感来源于蜂窝 - 蜂窝是世界上高度模块化的工程结构,六边形的规划能带来无限扩张的可能
- 所以作者用了
BeeHive
来做模块化开发办理东西的命名
-
- 我本人很喜欢这个姓名,且现在没想到更好的称号代替ta,咱们不如直接借鉴 这个称号取名:
BeeHivePlus
-
Plus
在具备原本版别的功用责任之外,有进一步的要求: - 添加模块层级办理功用
- 运用级
- 事务级
- 功用级
- 添加子模块库区别,使其更灵巧应对「大项目」、「小项目」、「SDK开发」、「途径套件开发」
- 工作办理区别
- 注册办法区别
-
-
Capability
- 这个归于模块办理东西,是移动开发套件的一个模块化开发办理才能,我以为归于整体归于一个
Capability
- 而模块库区别的子库能够作为若干小组件/小插件
-
Plugin
: 若是直接选用 第三方开源库BeeHive
,咱们能够用Plugin
-
Component
: 咱们此次决议借鉴阿里的BeeHive
开发一个Swift
版别的BeeHivePlus
,所以这些应该区别为小功用组件
-
- 这个归于模块办理东西,是移动开发套件的一个模块化开发办理才能,我以为归于整体归于一个
- 运用
Swift开发
- Swift至今现已若干年了,ABI早已安稳,所以这次我决议运用Swift开发
- 相信用Swift开发的
BeeHivePlus
在代码上,必定会比BeeHive
更简练,更高雅
- 综上,总结一下命名:
-
完整功用的结构:
mpe.capability.BeeHivePlus
- 解释一下:
mpe
= Mobile Paas Engineering,“移动开发套件PaaS服务工程”- mpaas:= Mobile Paas,代表着移动开发套件PaaS服务。咱们自己发明一个结构是能够以为公司搭载MobilePaaS移动开发套件赋能公司和公司事务为愿景的
- Engineering: 工程
- capability: 模块办理东西,自身也是一个功用模块
- BeeHivePlus = 依据BeeHive才能基础上提出进一步的要求
- 解释一下:
-
区别的子库以及相关的责任补白:
-
模块通讯1:
mpe.component.urlRouter
跨运用服务音讯传递
、H5页面Web服务(小程序) -
模块通讯2:
mpe.component.moduleManager
事务自界说工作 -
服务办理模块:
mpe.capability.serviceManager
办理服务
(注册
、反注册
、查询
) -
音讯办理:
mpe.component.modEventMessageManager
体系级工作+运用级工作 -
环境与上下文装备:
mpe.component.modEnvironment
AppDelegate+Context+Config -
模块分层与模块分层办理分类:
mpe.component.modModifier
- 运用场景: SuperApp、NormalApp、SDK
- 静态注册: BeeHive.bundle
- 动态注册: BH_EXPORT_MODULE
- Module Level分级: Application、Feature、Capability
-
mpe.component.modModifier.superApp
= 静态注册 + 三级Module Level分级 + 依靠( 模块通讯1 + 模块通讯2 + 服务办理模块 + 音讯办理:) -
mpe.component.modModifier.normalApp
= 静态注册 + 二级Module Level分级 + 依靠(模块通讯2 + 服务办理模块 + 按需 添加) -
mpe.component.modModifier.sdk
= 动态注册 + 一级Module Level分级 + 依靠(按需 添加)
-
模块分层与模块分层办理分类:
mpe.component.modCommon
TimeProfiler+WatchDog+Common+Defines
-
模块通讯1:
-
完整功用的结构:
-
总结
本文要点小结
- 本篇文章以
付出宝
为事例,讲述了:模块的三级分类
、模块区别战略
- 紧接着介绍了模块化开发的意义,引出介绍了几款经典的
模块化处理计划
,并做了对比剖析 - 最终,针对剖析中提及的不足,提出了优化计划
本文没有打开的内容
- 本篇文章没有针对
模块化计划规划
打开开发实践
以及相关demo的介绍和共享 - 本文也没有进一步讲述
运用的分层规划
和组件化开发实战
- 一起本文在介绍几款模块化处理计划的过程中,没有进一步针对其间原理进行深化
关于
模块化计划开发实践
、运用的分层规划
和组件化开发
等内容,将在下一篇文章中体现
相关阅览
-
CTMediator
作者的博文:iOS运用架构谈-组件化计划 -
MGJRouter
作者的博文:蘑菇街 App 的组件化之路 -
BeeHive
作者的阿里团队博文:BeeHive:一个高雅但还在完善中的解耦结构
引荐阅览
- 移动研发途径mPaaS
Swift/OC混编 项意图 模块化实践
- iOS混编 模块化、组件化、经验指北
运用BeeHive
作为模块办理东西
- 阿里系的
手机天猫
,运用模块办理东西BeeHive
实践 模块化架构
手机天猫解耦之路 -
蜂鸟商家版
,老项目实践 模块化、组件化架构演进 事例
蜂鸟商家版 iOS 组件化 / 模块化实践总结
模块途径化办理东西
这个事例 介绍的 模块化 办理计划,是 搭建途径东西办理模块库
的计划
- 适用于
事务线广
且移动端运用多
作为 商业 的大公司
如京东:京东iOS客户端组件办理实践 - 适用于 供给
PaaS
或SaaS
服务的 云服务公司
如:阿里云途径供给的 移动研发途径mPaaS 便是这类
其它
- 模块化日常:奇特的 pod repo push 失利
- 模块化日常:CocoaPods 1.4.0 真好用(并不)
- 模块化日常:CocoaPods 库资源引证问题
- 模块化日常:重名类
- 模块化日常:耗时的发布
- 模块化日常:开源库与私有库重名
- 模块化日常:库间彼此依靠
专题系列文章
iOS架构规划
- 1.iOS架构规划|总述
- 2.iOS架构规划|iOS模块化开发 【模块的三级分类、模块区别战略、模块化计划、模块功用规划】
- 3.iOS架构规划|依靠包办理东西Cocoapods常用实践:组件库(本地库、长途库(公有、私有))创立、模板工程、区别、资源办理、优化等
- 4.iOS架构规划|iOS模块化开发实践、iOS组件化开发【组件办理、组件分类、组件区别、组件开发】(
待输出
) - 5.iOS架构规划|iOS开发包二进制化【.a静态库、.framework(静态库、动态库)、.dyld动态库、.xcfameworks等】
- 6.iOS架构规划|iOS开发-规划办法(
待输出
) - 7.iOS架构规划|架构规划与架构演进(
待输出
)
探求iOS底层原理
- 探求iOS底层原理|总述