概念
移动开发平台(Mobile PaaS,简称 mPaaS)是源于支付宝 App 的移动开发平台,为移动开发、测试、运营及运维提供云到端的一站式解决方案,能有效降低技术门槛、减少研发成本、提升开发效率,协助企业快速搭建稳定高质量的移动 App。
主要功能
- 3 大研发框架:Native 开发框架、Kylin H5 开发框架、小程序开发框架。
- 20 多个功能性组件,例如网关服务、埋点分析、热修复、用户反馈、消息推送、离线包、国际化、扫码等。
- 100 多个 UI 控件,包括 AntUI 和 AntMobile。
主要应用
设备标识 :简单快捷地获取设备 ID,快速定位到特定设备
H5 容器 :在 Native 代码中快速加载 H5 页面
社交分享 :快速便捷地将信息共享到各个渠道
接入
mPaaS iOS 框架是源自支付宝客户端的开发框架。该框架直接接管应用的生命周期,负责整个应用 启动托管、应用生命周期管理。同时基于 Framework 的设计思想,将业务隔离成相对独立的模块,着力于追求模块之间的高內聚、低耦合。参考 mPaaS 框架介绍 获取更多信息。
不适用一般的AppDelegate,而使用DTFrameworkInterface来控制整个生命周期。
以下为main.m中接入。
int main(int argc, char * argv[]) {
[MPAnalysisHelper enableCrashReporterService]; // USE MPAAS CRASH REPORTER
@autoreleasepool {
// 开启闪退上报
[MPAnalysisHelper enableCrashReporterService];
#ifdef HOTPATCHEXIST
NSString *path;
APSecBufferRef buf;
int ret;
path = [[NSBundle mainBundle] pathForResource:@"pubkey" ofType:@"pem"];
APSecInitPublicKey([path UTF8String]); // 读取并初始化公钥
buf = APSecGetPKS(); // 读取公钥的签名
ret = APSecVerifyFile([path UTF8String], buf->data, buf->length); // 验证公钥文件自身是否符合签名
free(buf);
if (ret != 0) {
MSCLog(@"The public key is modified.");
}
#endif
return UIApplicationMain(argc, argv, @"DFApplication", @"DFClientDelegate"); // NOW USE MPAAS FRAMEWORK
}
}
目录结构
- mpaas_sdk.config:当前工程添加的模块信息,包括版本、添加时间、资源文件等,由 mPaaS 插件自动维护,不得手动修改。
- mPaaSDemo-mPaaS-Headers.h:当前工程依赖的 mPaaS 模块的头文件,由 mPaaS 插件自动维护,不得手动修改。
- mPaaSDemo-Prefix.pch:当前工程 pch 文件的引用,会自动将 mPaaSdemo-mPaaS-Headers.h 加入 mPaaS 模块的头文件。
- APMobileFramework:mpaas 框架的生命周期管理的 category 文件。
- mPaas:MPaaSInterface 的 category 文件。
- meta.config:从 mPaaS 控制台下载的云端元数据。
- yw_1222.jpg:通过元数据中的 base64code 字段生成的无线保镖验签图片,在移动网关验签时使用。如不需要移动网关功能,可删除此图片。
- Resources & Frameworks:mPaaS 模块的资源文件和二进制文件目录,是当前工程所有 Targets 使用的 mPaaS 模块的并集,由 mPaaS 插件自动维护,不得手动修改。
mPaas框架详解介绍
启动托管
通过程序 main 函数的替换,直接接管应用的生命周期,整个启动的过程如下:
main -> DFClientDelegate -> 打开 Launcher 应用
应用生命周期管理
mPaaS 框架接入之后,完全替代了 AppDelegate 的角色,整个应用的生命周期由框架进行管理,但是用户依然可以实现应用生命周期各个阶段对应的代理方法,UIApplicationDelegate 中的所有代理方法,框架都提供了等价的接入方式,只需要在 Category 中覆盖对应的方法即可。
从前台到后台,从后台到前台,以及收到消息通知等都有等同方法。
应用模块划分
mPaaS 框架内定义了微应用和服务的概念来进行模块间的划分。其中,以是否有 UI 界面作为标准,Framework 将不同的模块划分为 微应用 和 服务,通过 框架上下文 进行微应用与服务的生命周期管理。
微应用
在基于 mPaaS iOS 框架开发应用的过程中,一般会将带有 UI 界面的独立业务设置为一个微应用(如支付宝中的转账、手机充值等),与其他的业务隔离开,在微应用内实现自身业务逻辑。要添加一个微应用,您需要添加微应用模板代码,并注册微应用。
微应用在MobileRuntime.plist 中注册后,通过框架进行统一管理。包括注册delegate
、description
以及name
。然后继承DTMicroApplicationDelegate
协议的自定义类DemoAppDelegate
。以及继承自DTViewController
自定义视图控制器DemoAppController
。
服务
在基于 mPaaS iOS 框架开发应用的过程中,没有 UI 界面且通用的功能,可以设置为服务(如登录服务),在整个 App 运行期可以方便地被其他微应用或服务获取。添加一个服务,您需要添加服务模板代码,并注册服务。
注册方式同微应用。实现一个提供协议接口方法的DemoService
,以及遵循该协议的实现类DemoServiceImp
。
管理微应用和服务
管理微应用
- 基于 mPaaS iOS 框架,可以根据微应用的 name,快速查找到此微应用,并在当前微应用中启动另一个微应用
- (void)pushSubApp2
{
[DTContextGet() startApplication:@"20000002" params:@{} launchMode:kDTMicroApplicationLaunchModePushWithAnimation];
}
- 微应用堆栈中上层的微应用,可以快速跳转到堆栈底部的根应用:
- (void)exitToLauncher
{
// 因为 Launcher 在下层,所以再启动 Launcher 实际是退出上层所有的 App,回到 Launcher
[DTContextGet() startApplication:@"Launcher" params:nil animated:kDTMicroApplicationLaunchModePushNoAnimation];
}
- 快速退出当前微应用:
- (void)exitSelf
{
[[DTContextGet() currentApplication] exitAnimated:YES];
}
- 快速退出已启动的应用:
- (void)exitApp2
{
// 当前顶层应用是 app3,但是可以强行把 app2 和它的窗口都退出。
[[DTContextGet() findApplicationByName:@"20000002"] forceExit];
}
管理服务
基于 mPaaS iOS 框架,可以快速在当前微应用中启动另一个服务。
- (void)findService
{
id<DemoService> service = [DTContextGet() findServiceByName:@"DemoService"];
[service doTask];
}
一个业务场景的微应用,使用一个delegate,注册的name使用其根VC。一个微应用中可以有多个vc,其中微应用内的VC跳转使用正常的push、pop模式。微应用间的跳转,才使用管理微应用的跳转模式。
而服务主要是一些不包含ui的业务逻辑抽象,比如请求一些数据,保存一些数据,以及获取标识符登录功能等。
主要功能
移动网关
移动网关服务(Mobile Gateway Service,MGS)是移动开发平台(mPaaS)提供的连接移动客户端与服务端的组件产品。该组件简化了移动端与服务端的数据协议和通讯协议,能够显著提升开发效率和网络通讯效率。
(就是封装的网络请求框架。提供服务端框架代码,生成客户端SDK)
特点
- 自动生成客户端的 RPC 调用代码,用户不需要关心网络通信、协议以及使用的数据格式。(封装好)
- 将服务端返回的数据自动反解生成 Objective-C 对象,无需额外编码。(省去自己截码)
- 提供数据压缩、缓存、批量调用等增强服务。
- 支持 RPC 拦截器,实现定制化的请求与处理。
- 实行统一的安全加密机制和防篡改的请求签名验证机制。
网络请求框架APMobileNetwork
MPRpcInterface 提供异步请求。
/**
初始化 MGS 组件,在调用第一个 RPC 之前必须调用,建议在 App 启动时较早时机调用
*/
+ (void)initRpc;
/**
* 异步block,completion会在主线程回调
*
* @param block 相关操作
* @param completion block完成后的处理
*
* @return DTRpcAsyncCaller
*/
+ (DTRpcAsyncCaller *)callAsyncBlock:(void (^)(void))block completion:(void (^)(void))completion;
DTRpcClient 提供具体的网络请求方法。
+ (DTRpcClient *)defaultClient;
/**
* 根据指定的 \code DTRpcMethod 执行一个 RPC 请求。
*
* @param method 一个 \code DTRpcCode 类型的实例,描述了 RPC 请求的相头信息。
* @param params RPC 请求需要的参数。
* @param field 添加到request里面的head
* @param responseBlock 回调方法
*
* @return 如果请求成功,返回指定类型的对象,否则返回 nil。
*/
- (id)executeMethod:(DTRpcMethod *)method params:(NSArray *)params requestHeaderField:(NSDictionary*)field responseHeaderFields:(void (^)(NSDictionary* allHeaderFields))responseBlock;
结合使用
[MPRpcInterface callAsyncBlock:^{
@try{
DTRpcConfig *config = [[DTRpcClient defaultClient] configForScope:kDTRpcConfigScopeGlobal];//初始化
config.isAMRPC = YES;
DTRpcMethod *method = [[DTRpcMethod alloc] init];
method.operationType = self.operationType;//请求地址
method.checkLogin = NO;
method.signCheck = YES;//加签
method.returnType = @"@\"NSDictionary\"";
method.timeoutInterval = 30.0f;
result = [[DTRpcClient defaultClient] executeMethod:method params:@[dict?dict:[NSNull null]] requestHeaderField:self.realReqHeadJson responseHeaderFields:nil];
}
@catch (NSException *exception) {
self.exceptionMsg=exception;
}
} completion:^{
// NSString *status = [result objectForKey:@"STATUS"];
if ([result isKindOfClass:[NSDictionary class]]&&successBlock) {
successBlock(result);//成功回调
}else if (self.exceptionMsg&&errorBlock){
errorBlock(self.exceptionMsg);
}else {
if(failBlock){failBlock(result);}
}
}];
+ (NSDictionary *)getPublicHeader;//请求头
+ (NSString *)getIPAddress;//网络ip
+ (NSString*)getNetReachability;//获取网络状态
数据同步
数据同步是 mPaaS 平台的一个核心基础服务组件。数据同步源自蚂蚁集团内面向移动应用、从服务端到客户端进行海量数据推送的全链路解决方案 — SYNC。该组件提供了一个安全的基于传输控制协议(Transmission Control Protocol,简称 TCP)和安全套接层(Secure Sockets Layer,简称 SSL)的数据通道,能够及时、准确、有序地将服务器端的业务数据主动地同步(SYNC)到客户端 App。
传统的远程过程调用(Remote Procedure Call,简称 RPC)已立足互联网行业几十年,也能满足绝大部分业务场景和功能需求。但在现阶段,随着移动互联网的全面普及和发展,无论是 App 的规模还是用户对于 App 的要求都已进入了一个新的阶段。传统的 RPC 请求因其自身的特性,存在许多的不足:
- 客户端在特定的场景下需要调用 RPC 请求来获取最新的数据,而服务端(云端)实际没有或仅有少量数据发生变化。
- 在客户端启动时,不同的业务模块、业务功能因设计上的独立,需要分别进行 RPC 请求来完成各自业务的数据拉取。
- 客户端无法及时感知服务端发生的数据变化,只能通过定时轮询 RPC 接口的方式来刷新数据。
- 传统 RPC 大多基于 HTTP(S) 的短连接进行数据交互,连接上即使使用 keepalive 等特性也无法长期保持连接,无法做到链路持续复用。请求创建连接、证书交换、加解密等对网络耗时及性能都会代来不小的损耗
代码示例
为实现监听同步业务的逻辑,您需要创建一个类,最好是常驻内存的服务来一直监听 Sync 消息。在下面的示例中,创建了一个 MySyncService 类来监听同步业务的逻辑。
#import <MPMssAdapter/MPSyncInterface.h>
#define SYNC_BIZ_NAME "SYNC-TRADE-DATA";
@implementation MySyncService
+ (instancetype)sharedInstance
{
static MySyncService *bizService;
static dispatch_once_t llSOnceToken;
dispatch_once(&llSOnceToken, ^{
bizService = [[self alloc] init];
});
return bizService;
}
-(instancetype)init
{
self = [super init];
if (self) {
[MPSyncInterface initSync];
BOOL registerSingleDeviceSync = [MPSyncInterface registerSyncBizWithName:SYNC_BIZ_NAME syncObserver:self selector:@selector(revSyncBizNotification:)];
[MPSyncInterface bindUserWithSessionId:@"SESSION_DEMO"]; // 此处的 User 对应控制台下发命令时,所需要填写的 userId, 需要和 MPaaSInterface 的 userId 函数中配置的值相对应;sessionId 是客户端携带的授权 token,userId 和 sessionId 都是用户登录系统返回的数据,当 userId 和 sessionId 变化时,需要重新调用此函数才能保证长连接的建立正确。
}
return self;
}
-(void)revSyncBizNotification:(NSNotification*)notify
{
NSDictionary *userInfo = notify.userInfo;
dispatch_async(dispatch_get_main_queue(), ^{
//业务数据处理
[MySyncService handleSyncData:userInfo];
//回调 SyncSDK,表示业务数据已经处理
[MPSyncInterface responseMessageNotify:userInfo];
});
}
+(void)handleSyncData:(NSDictionary *)userInfo
{
NSString * stringOp = userInfo[@"op"];
NSArray *op = [NSJSONSerialization JSONObjectWithData:[stringOp dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
if([op isKindOfClass:[NSArray class]]){
[op enumerateObjectsUsingBlock:^(NSDictionary * item, NSUInteger idx, BOOL *stop) {
if([item isKindOfClass:[NSDictionary class]]){
NSString * plString = item[@"pl"];//业务数据 payload
if(item[@"isB"]){
NSData *dataPl = [[NSData alloc] initWithBase64EncodedString:plString options:kNilOptions];
NSString *pl = [[NSString alloc] initWithData:dataPl encoding:NSUTF8StringEncoding];
NSLog(@"biz payload data:%@,string:%@",dataPl,pl);
}else{
NSLog(@"biz payload:%@",plString);
}
}
}];
}
}
-(void)dealloc
{
BOOL unRegisterSingleDeviceSync = [MPSyncInterface unRegisterSyncBizWithName:SYNC_BIZ_NAME syncObserver:[MySyncService sharedInstance]];
[MPSyncInterface removeSyncNotificationObserver:self];
}
@end
类似于推送。不过不同于传统推送,阿里的实现方式更适用于大量数据。
常用api
+(void)initSync; //初始化
+(MPSyncNetConnectType)connectStatus;//查看链接状态
+(BOOL)registerSyncBizWithName:(NSString *)bizName syncObserver:(id)observer selector:(SEL)selector;//注册监听服务端接口名,以及监听回调方法
+(BOOL)unRegisterSyncBizWithName:(NSString *)bizName syncObserver:(id)observer;//解除注册监听
+(void)bindUserWithSessionId:(NSString *)sessionId;//绑定用户信息
+(void)unBindUser;//解绑
移动分析
阿里行为分析。
埋点实现较为简单。可使用swizzilng交换方式统一埋点。
实时发布
在 iOS 开发领域,由于 AppStore 审核标准严格、审核周期长、效率低,应用的发版速度极慢,因此能快速修复线上严重 Bug 而无需发布新版本的热修复方法对于 iOS 应用来说就显得尤其重要。
mPaaS 提供的 Hotpatch 热修复技术,在 Runtime 运行时特性的基础上,通过 JS 替换原有的 Objective-C 方法,从而达到修复线上 bug 的目的。目前包含的能力如下:
添加类,修改类(包括添加实例方法、类方法、属性,修改方法实现等)。
调用任意 Objective-C 类方法,访问成员变量。
使用 block、struct、GCD 等高级语法。
回滚操作热生效,被替换的方法能即时恢复。
脚本执行时机控制(启动前主线程运行、启动完成之后子线程运行)。
完善的安全加密、签名验证系统。
热修复的具体使用
- 接入sdk。
- 使用原生
Objective-C
代码解决bug。 - 用在线工具 JSPatchConvertor 将
Objective-C
代码转为 JS 脚本 - 验证转化后 JS 脚本(假设为 Test.js)的正确性,可以将写好的 JS 文件拖入到工程中,并在程序启动时(推荐在 didFinishLaunchingWithOptions 中)手动调用 JS 脚本,验证问题是否被正确修复
NSString *file = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"Test.js"]]; [MPDynamicInterface runDynamicLocalFile:file];
- 为了安全,加密。
- 使用mPass工具生成热修复资源包。
- 发布热修复资源包,已集成热修复的客户端源码可自行下载并替换修复。
UI组件
H5容器和离线包
H5 容器是一款移动端 Hybrid 解决方案 SDK(Nebula SDK)。提供了良好的外部扩展功能,拥有功能插件化、事件机制、JSAPI 定制和 H5App 推送更新管理能力。
离线包 是将包括 HTML、JavaScript、CSS 等页面内静态资源打包到一个压缩包内。预先下载该离线包到本地,然后通过客户端打开,直接从本地加载离线包,从而最大程度地摆脱网络环境对 H5 页面的影响。
渲染过程
当 H5 容器发出资源请求时,其访问本地资源或线上资源所使用的 URL 是一致的。
H5 容器会先截获该请求,截获请求后,发生如下情况:
- 如果本地有资源可以满足该请求的话,H5 容器会使用本地资源。
- 如果没有可以满足请求的本地资源,H5 容器会使用线上资源。