NSURLProtocol无法截获NSURLSession解决方案
前言
在做知乎日报的项目时,在其设置界面中有一个选项,当开启时,可以在移动网络状况下不下载图片。这个需求的解决方案有两种,一种是在每一个使用网络图片的地方,在下载前进行判断,如果当前网络为移动蜂窝网络,且开启了在移动网络状况下不下载图片
的选项时,放弃图片的下载;还有一种方案就是直接在NSURLProtocol
中进行截获,在+ (BOOL)canInitWithRequest:(NSURLRequest *)request
中进行判断,当request.URL
请求为图片,并开启不下载图片选项,且当前为移动网络状况下,截获该请求。在知乎日报项目中,我使用的第二种,既子类NSURLProtocol
来实现请求拦截。
NSURLProtocol实现图片下载拦截
一开始,觉得思路很简单,直接创建PictureBlockURLProtocol
类,其是NSURLProtocol
的子类,在Implement
实现中定义如下:
1 | //获取当前网络状态 |
定义完PictureBlockURLProtocol
类后,在AppDelegate
类的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
方法中调用[NSURLProtocol registerClass:[PictureBlockURLProtocol class]]
方法进行注册,运行一下,看看效果,发现,当使用AFNetwork、SDWebImageCache等第三方库的图片请求并没有被截获到,而UIWebView
可以,这就奇怪了,不过一想,UIWebView
使用的是NSURLConnection
,而AFNetWork
、SDWebCache
等已经废弃使用NSURLConnection
,转而使用NSURLSession
,难道是由于这个问题造成的?
在查看NSURLSession
相关类的文档时(如下图),从文档可以看到,如果需要支持自定义的NSURLProtocol
,需要将自定义的NSURLProtocol
子类赋给NSURLSessionConfiguration
的protocolClasses
属性。所以,如果需要NSURLProtocol
来截获NSURLSession
发出的请求,需要每一个NSURLSession
在创建时配置的NSURLSessionConfiguration
类的protocolClasses
属性附上自定义的NSURLProtocol
。
根据上图的文档,注意到,对于后台Sessions,是不支持自定义的NSURLProtocol
的。
通过查看AFNetwork、SDWebImageCache的源代码,其创建的Session时关联的NSURLSessionConfiguration
为defaultSessionConfiguration
。这样,解决方案就有两种,一种是直接改AFNetwork、SDWebImageCache等库的源代码,在其创建的NSURLSessionConfiguration
实例中将自定义的NSURLProtocol
赋给其protocolClasses
属性;另一种方案,使用Method Swizzling方法,由于AFNetwork、SDWebImageCache在创建时使用的是[NSURLSessionConfiguration defaultSessionConfiguration]
,所以可以替换defaultSessionConfiguration
实现。首先,创建NSURLSessionConfiguration
的类别,代码如下:
1 | + (NSURLSessionConfiguration *)zw_defaultSessionConfiguration{ |
如上代码所示,在创建的每一个NSURLSessionConfiguration
实例中,添加自定义的NSURLProtocol
类到其protocolClasses
属性,运行后,发现问题解决了,可以截获NSURLSession
发出的请求了。
Done!!!!