- 浏览: 402063 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
sucheng2016:
请问需要引入哪些个jar呢?
利用CXF发布restful WebService的注意事项(服务器端) -
sucheng2016:
利用CXF发布restful WebService的注意事项(服务器端) -
Joyceitoyo:
我试了怎么不好使,着急找中, 希望给与帮助 22192214 ...
UIWebView自适应宽度 -
Alanoddsoff:
https://10.24.16.26:7002 这个地址访问 ...
unable to find valid certification path to requested target 的简单解决办法 -
meiyounini:
求源码分享。。。
利用CXF发布restful WebService的注意事项(客户端)
NSURLProtocol的官方定义。
An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.
https://developer.apple.com/reference/foundation/nsurlprotocol
其实NSURLProtocol这个东西的作用就是让我们在app的内部拦截一切url请求(注意,不只是webView内的请求,而是整个app内的所有请求),如果筛选出来自己感兴趣的东西去处理,不感兴趣的就放过去就是了。既然能拦截,那么我们至少能做两件事,第一是拦截现有的url请求,比如常用的http://。第二就是我们可以自定义url协议了,比如boris://
这个东西完全可以取代以前大家常用的js和navtive通信的JSBridge。
我们现在尝试做一个和h5的通信。
刚才说到了,任何的app内部的url请求都会被拦截,当然也包含了webView发出的http请求。假设我们和h5约定一个规则,带有特殊参数"_mobile_bridge=1"的请求,是需要native处理的。那么我们就需要拦截到_mobile_bridge=1这个关键字。
PS:这样比传统的JSBridge的好处是,如果h5是在其他浏览器打开的,_mobile_bridge=1并不会起到任何作用,因此h5可以在没有被拦截的时候继续请求,只有在我们的app被拦截才会被app处理,并且NSURLProtocol还可以返回response给发起者,那么对h5来说,他们调用我们native的功能,就像发了一个请求一样简单。这样对h5开发的同学来说非常简单。JSBridge则比较麻烦。
我们需要继承NSURLProtocol并实现:
拦截到我们想要处理的请求之后,可以通过- (void)startLoading方法来做任何想做的处理,比如把请求的url换一下,然后再重新请求。这些基本用法有很多文章在介绍,这里就不写来。现在来封装一下,让整个自定义协议功能对其他人来说变得更简单易用。
假设我们的需求是根据_mobile_bridge=1拦截之后,再根据url的path来决定做什么操作,比如/login就弹出native的登陆框 /share 就弹出分享框 成功登陆或分享后再返回一个response给h5。这样我们可以通过path来决定不同操作,可是不同的path可能是不同的开发者实现的,即使是一个人写的,我们也应该把所有path和对应操作独立出来以解耦。所有我们可以把path当作一个key,操作当作一个handler。
因此我们可以这样定义我们的URLProtocol:
BOURLProtocolHandleProtocol协议规定了需要使用者实现request的处理以及通过block返回response。
+ (void)registerHandler:(Class<BOURLProtocolHandleProtocol>)handlerClass
path:(NSString *)path;
这个注册方法支持了每一个path都可以注册一个自己的handler。
实现如下:
上边逻辑很简单,只是有一个静态的map来存储注册进来的path和handler的Class。
重点就要看刚才说到的startLoading,这个方法是处理拦截到的请求的。
上边的代码,先从_handlerMap找到path对应的Class,然后创建一个Class对应的Instance,然后调用Instance的handle方法,handle方法做完对应的操作,再返回response。
下边我们实现一个处理h5发来的alert请求的handler:
上边代码实现了alert的处理,弹出一个alert,根据用户点击的按钮来返回不同的response。
现在都实现好了,我们需要把这些东西注册到App中。这个非常简单:
上边第一行是在app里注册我们自定义的协议,这样才能拦截到请求。第二行是像我们自己的协议里注册path和处理器。
这样,不同业务组的不同开发者就可以单独注册自己的handler了。
demo地址在这里,https://github.com/82934162/BOURLProtocolDemo
引用
An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.
https://developer.apple.com/reference/foundation/nsurlprotocol
其实NSURLProtocol这个东西的作用就是让我们在app的内部拦截一切url请求(注意,不只是webView内的请求,而是整个app内的所有请求),如果筛选出来自己感兴趣的东西去处理,不感兴趣的就放过去就是了。既然能拦截,那么我们至少能做两件事,第一是拦截现有的url请求,比如常用的http://。第二就是我们可以自定义url协议了,比如boris://
这个东西完全可以取代以前大家常用的js和navtive通信的JSBridge。
我们现在尝试做一个和h5的通信。
刚才说到了,任何的app内部的url请求都会被拦截,当然也包含了webView发出的http请求。假设我们和h5约定一个规则,带有特殊参数"_mobile_bridge=1"的请求,是需要native处理的。那么我们就需要拦截到_mobile_bridge=1这个关键字。
PS:这样比传统的JSBridge的好处是,如果h5是在其他浏览器打开的,_mobile_bridge=1并不会起到任何作用,因此h5可以在没有被拦截的时候继续请求,只有在我们的app被拦截才会被app处理,并且NSURLProtocol还可以返回response给发起者,那么对h5来说,他们调用我们native的功能,就像发了一个请求一样简单。这样对h5开发的同学来说非常简单。JSBridge则比较麻烦。
我们需要继承NSURLProtocol并实现:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request { //如果请求已经被处理了,则不再重复处理 if ([NSURLProtocol propertyForKey:kBOURLProtocolHandled inRequest:request]) { return NO; } //拦截我们约定好的规则 if([request.URL.absoluteString containsString:@"_mobile_bridge=1"]) { return YES; } return NO; }
拦截到我们想要处理的请求之后,可以通过- (void)startLoading方法来做任何想做的处理,比如把请求的url换一下,然后再重新请求。这些基本用法有很多文章在介绍,这里就不写来。现在来封装一下,让整个自定义协议功能对其他人来说变得更简单易用。
假设我们的需求是根据_mobile_bridge=1拦截之后,再根据url的path来决定做什么操作,比如/login就弹出native的登陆框 /share 就弹出分享框 成功登陆或分享后再返回一个response给h5。这样我们可以通过path来决定不同操作,可是不同的path可能是不同的开发者实现的,即使是一个人写的,我们也应该把所有path和对应操作独立出来以解耦。所有我们可以把path当作一个key,操作当作一个handler。
因此我们可以这样定义我们的URLProtocol:
// // BOURLProtocol.h // OSBuyApp // // Created by Boris on 17/2/28. // Copyright © 2017年 Gome. All rights reserved. // #import <Foundation/Foundation.h> #import "BOURLProtocolResponse.h" typedef void (^BOURLProtocolBLock)(BOURLProtocolResponse *response); @protocol BOURLProtocolHandleProtocol <NSObject> /** 处理完也许,创建一个BOURLProtocolResponse,并调用block @param request 请求 @param block 回调 */ - (void)handleURLProtocolRequest:(NSURLRequest *)request block:(BOURLProtocolBLock)block; @end @interface BOURLProtocol : NSURLProtocol /** 注册处理器到协议 最好在应用启动的时候注册 每一次请求,都会根据path对应的Class来创建一个[新的实例] 因此建议不要和其他无关逻辑混在一起 调用实例的BOURLProtocolHandleProtocol方法做后续处理 @param handlerClass 实现了处理协议的Class @param path url中的path,如http://baidu.com/image/test?a=1 中的 /image/test */ + (void)registerHandler:(Class<BOURLProtocolHandleProtocol>)handlerClass path:(NSString *)path; /** 解除path的处理器 @param path path */ + (void)unregisterHandlerWithPath:(NSString *)path; @end
BOURLProtocolHandleProtocol协议规定了需要使用者实现request的处理以及通过block返回response。
+ (void)registerHandler:(Class<BOURLProtocolHandleProtocol>)handlerClass
path:(NSString *)path;
这个注册方法支持了每一个path都可以注册一个自己的handler。
实现如下:
static NSMutableDictionary *_handlerMap; @interface BOURLProtocol() @property(nonatomic,strong) id<BOURLProtocolHandleProtocol> handler; @end @implementation BOURLProtocol +(void)initialize { if(self == BOURLProtocol.class) { _handlerMap = [NSMutableDictionary dictionary]; } } #pragma mark - Public + (void)registerHandler:(Class<BOURLProtocolHandleProtocol>)handlerClass path:(NSString *)path { @synchronized (_handlerMap) { [_handlerMap setObject:handlerClass forKey:path]; } } + (void)unregisterHandlerWithPath:(NSString *)path { @synchronized (_handlerMap) { [_handlerMap removeObjectForKey:path]; } }
上边逻辑很简单,只是有一个静态的map来存储注册进来的path和handler的Class。
重点就要看刚才说到的startLoading,这个方法是处理拦截到的请求的。
/** 在该方法里做path的判断和对应的逻辑 */ - (void)startLoading { NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy]; [BOURLProtocol setProperty:@(YES) forKey:kBOURLProtocolHandled inRequest:mutableReqeust]; self.handler = nil; @synchronized (_handlerMap) { for(NSString *key in [_handlerMap allKeys]) { if([mutableReqeust.URL.path isEqualToString:key]) { Class class = [_handlerMap objectForKey:key]; self.handler = [[class alloc]init]; break; } } } BOURLProtocolResponse *protocolResponse = nil; if(![self.handler respondsToSelector:@selector(handleURLProtocolRequest:block:)]) { protocolResponse = [[BOURLProtocolResponse alloc]init]; protocolResponse.code = 400; protocolResponse.errorMessage = @"No response"; [self handleProtocolResponse:protocolResponse]; } else { [self.handler handleURLProtocolRequest:mutableReqeust block:^(BOURLProtocolResponse *response) { [self handleProtocolResponse:response]; }]; } } - (void)handleProtocolResponse:(BOURLProtocolResponse *)protocolResponse { NSString *str = [protocolResponse jsonString]; NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary * headerFields = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%ld",data.length], @"Content-Length", @"text/json",@"Content-Type", nil]; NSHTTPURLResponse * response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];//statusCode == 200 [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [self.client URLProtocol:self didLoadData:data]; [self.client URLProtocolDidFinishLoading:self]; self.handler = 0; }
上边的代码,先从_handlerMap找到path对应的Class,然后创建一个Class对应的Instance,然后调用Instance的handle方法,handle方法做完对应的操作,再返回response。
下边我们实现一个处理h5发来的alert请求的handler:
#import "TestProtocolHandler.h" #import "AppDelegate.h" @implementation TestProtocolHandler - (void)handleURLProtocolRequest:(NSURLRequest *)request block:(BOURLProtocolBLock)block { UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { BOURLProtocolResponse *response = [[BOURLProtocolResponse alloc]init]; response.data = @{@"result":@"cancel"}; response.code = 200; response.errorMessage = @""; block(response); }]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { BOURLProtocolResponse *response = [[BOURLProtocolResponse alloc]init]; response.data = @{@"result":@"ok"}; response.code = 200; response.errorMessage = @""; block(response); }]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"BOURLProtocolDemo" message:@"BOURLProtocolDemo" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:cancelAction]; [alert addAction:okAction]; UIViewController *vc = [[[[UIApplication sharedApplication ] delegate] window]rootViewController]; [vc presentViewController:alert animated:YES completion:nil]; } @end
上边代码实现了alert的处理,弹出一个alert,根据用户点击的按钮来返回不同的response。
现在都实现好了,我们需要把这些东西注册到App中。这个非常简单:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [NSURLProtocol registerClass:[BOURLProtocol class]]; [BOURLProtocol registerHandler:[TestProtocolHandler class] path:@"/alert"]; return YES; }
上边第一行是在app里注册我们自定义的协议,这样才能拦截到请求。第二行是像我们自己的协议里注册path和处理器。
这样,不同业务组的不同开发者就可以单独注册自己的handler了。
demo地址在这里,https://github.com/82934162/BOURLProtocolDemo
发表评论
-
ARC下KVO注意事项
2015-12-07 11:40 618假设我们对A ViewController(暂时叫AVC)进行 ... -
iOS多线程访问非线程安全对象的crash
2015-05-27 16:39 3984多个线程在访问同一个非线程安全对象时,有可能会crash. 非 ... -
iOS today插件毛玻璃效果
2015-01-30 17:31 2815这里说的毛玻璃不是背景,而是文字以及按钮的毛玻璃效果.比如下图 ... -
UIWebView自适应宽度
2014-02-27 19:28 46892webview在手机上显示的内容经常非常宽,显示出来之后要 ... -
iOS7相机隐私判断
2013-12-19 17:31 9401装了iOS7的ip5的隐私设置里多了相机这一项(ip4装i ... -
iOS7相机隐私判断
2013-12-19 17:31 0装了iOS7的ip5的隐私设置里多了相机这一项(ip4装i ... -
XMPP登陆互踢
2013-02-20 16:16 10038如果JID的resource缺省 ... -
可展开的UITableView (附源码)
2012-08-28 11:44 17689由于工作需要,写了一个UITableView的子类,简单的实现 ... -
UIView加圆角会卡的解决办法
2012-07-18 20:22 3632做iOS开发的时候有可能经常碰到莫名其妙的滑动不顺畅(在UIS ... -
【转】CATransition的动画效果类型及实现方法
2012-06-26 15:42 1666实现iphone漂亮的动画效果主要有两种方法,一种是UIVie ... -
【转】iphone开发----计算MKMapView的缩放等级
2012-03-28 17:26 7554IOS的sdk中MKMapView并没有提供得到当前地图zoo ... -
viewWillDisappear,viewWillAppear不调用的解决方法
2012-03-01 12:28 27082产生原因是用了UINavigationController. ... -
【原】iOS通过http post上传图片
2012-01-09 19:51 85224由于iOS无法通过html表单 ... -
iOS真机测试,为Provisioning添加设备
2011-12-19 19:54 25614-------------添加设备到provisioning- ... -
禁止UIWebView的拖动
2011-11-28 17:50 5237UIWebView的自视图中的第一个,是和UIWebView同 ... -
OC将http转义字符转化为中文
2011-11-23 18:52 2825result = [result stringByRepl ... -
Objective-C Unicode 转换成中文【转】
2011-11-23 18:49 5226+ (NSString *)replaceUnic ...
相关推荐
Android DataStore使用和封装
Android实战——Retrofit2的使用和封装
Android经常会用到网络请求,对okhttp网络请求框架的使用和封装
使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的OpenGL.zip使用C++封装的...
Android实战——okhttp的使用和封装
Reachability 网络状态的使用和封装,更加方便使用
uniapp封装websocket,使用简单,下载复制进项目即可使用,统一使用json收发消息,区分业务类型。 1、使用方法,在需要使用的页面导入改文件(也可以挂载全局) 2、onLoad方法调用this.socketObj = new WebSocketIO...
使用gpac库封装H264和H265格式的视频流和AAC格式的音频流,输出mp4文件。版权声明:转载请注明出处,如有错漏之处,敬请指正。 https://blog.csdn.net/weixin_43549602/article/details/84571906
json的简单应用 使用json封装数据 html源代码 一个增加删除数据的例子
MVP模式的框架,封装了轮播图banner的使用,公告栏的封装,许多潮流的三方库的引用
使用hibernate封装方法,显现使用一个dao,service,impl来关联两个表的操作,实现,增删改查基本操作
Appium使用po实例和封装打包,包含断言、po封装,还有几个例子。专栏地址:https://blog.csdn.net/niubitianping/category_6414306.html
BaseActivity和toolBar简单使用和封装,可以添加菜单项去除重复代码等,方便开发继承使用
后端功能和实现都比较简单,没什么好说的,本文档重点要说的是前端的封装。 对于数据字典,前端展现往往有三种常用的形式,下拉列表、单选按钮组和复选框组,其中,最常用是下拉列表,这里就以下拉列表的实现为例来...
使用Promise封装wx.request wx.setStorage封装,实现设置过期时间 示例代码已上传、注意文件路径问题
PcbLib文件类型、直插元器件、贴片、Altium Designer封装库 变压器封装PCB文件3D封装Altium Designer库的型号类型如下,总有你需要的一款: DA10XC、DA10XMC、EE42-15V 8+8、EE-35-V、EE-40、EE-42、EE-42V、EE-42X2...
c# Redis使用及封装类,记得先装redis及启用。封装类内为单例可以直接使用。
PcbLib文件类型、贴片库、Altium Designer封装库 DFN封装库PCB文件3D封装Altium Designer库的型号类型如下,总有你需要的一款: DFN-10_L3.0-W3.0-P0.50、PG-TDSON-8_5x6MM、TDFN-6_L2.0-W2.0-P0.65、TDFN-6_L2.0-W...
使用vitamio封装的播放器
逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装规格逻辑封装...