流量消耗过快原因
- 接口返回中包含无用的数据
- 无用的请求(多次同时请求同一个接口)
- 图片和数据未作缓存处理
- 图片过大
- App和后台交互的协议种类
- 暂时只了解到这么多,欢迎补充
解决方案
基于我们项目的情况,我们目前只能从图片解决。因为项目中部分界面使用Html 5展示的,无法使用SDWebImage进行缓存,进而会多次请求图片造成流量浪费。在之前做过图片的格式转换和裁剪,但是最后都打不到预期效果。直到查阅到WebP,又很多公司都在用WebP格式,比如淘宝、Facebook、腾讯等。WebP的优点不做赘述,自己看下维基百科的介绍。
- 对图片使用WebP格式
- 截取Html,获取图片地址,本地使用SDWebImage下载图片,并缓存图片,下载后的图片和原地址一一替换。
引用WebP
- Pods
OnerydeMacBook-Pro:onery-Blog onery$ pod 'SDWebImage/WebP'
- 手动导入
- 首先下载WebP
- 将SDWebImage和下载好的WebP导入工程
- 此时SDWebImage尚不支持WebP,还需要做一点操作:Build Settings –> Preprocessor,在Preprocessor中添加SD_WEBP=1
##截取Html中的图片
###NSURLCache
NSURLCache提供的是内存以及磁盘的综合缓存机制,NSURLCache
只会对你的get
请求进行缓存,默认是512kb的内存缓存空间和10MB的磁盘缓存空间,可以通过代码查看和设置大小:
- 查看磁盘和内存缓存空间
NSLog(@"磁盘缓存空间大小:%lu",(unsigned long)[[NSURLCache sharedURLCache] diskCapacity]);
//log:磁盘缓存空间大小:10000000
NSLog(@"内存缓存空间大小:%lu",(unsigned long)[[NSURLCache sharedURLCache] memoryCapacity]);
//log:内存缓存空间大小:512000
- 设置磁盘和内存缓存空间
设置存储空间需要在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
方法中设置
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
###NSURLProtocol
NSURLProtocol能够让你去重新定义苹果的URL加载系统 (URL Loading System)的行为,URL Loading System里有许多类用于处理URL请求,比如NSURL,NSURLRequest,NSURLConnection和NSURLSession等,最重要的是NSURLProtocol可以拦截到WebView发出的请求,当URL Loading System使用NSURLRequest去获取资源的时候,会创建一个NSURLProtocol子类的实例,不应该直接实例化一个NSURLProtocol,NSURLProtocol看起来像是一个协议,但其实这是一个类,而且必须使用该类的子类
,并且需要在APPDelegate
中被注册。
通过NSURLProtocol我们可以作如下操作:
- 重定向网络请求(更改请求指向到指定的地址)
- 不使用网络请求,使用本地的缓存
- 自定义请求返回结果
- 设置全局网络请求
子类化NSURLProtocol
@interface YJURLProtocol : NSURLProtocol
@end
在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中注册,注册成功后,才能够处理所有URL Loading system的请求。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[NSURLProtocol registerClass:[YJURLProtocol class]];
return YES;
}
实现代理方法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
//如果不是UPYUN的地址,就不做拦截,有系统自动处理
if ([[request.URL absoluteString] rangeOfString:@"upaiyun"].location == NSNotFound) {
return NO;
}
//在我们项目中只处理http和https请求
NSString *scheme = [[request URL] scheme];
if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
[scheme caseInsensitiveCompare:@"https"] == NSOrderedSame))
{
//看看是否已经处理过了,防止无限次处理,耗费资源
if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
return NO;
}
NSString *agent = [request valueForHTTPHeaderField:@"User-Agent"];
// 只过滤UIWebview里边的加载图片请求
if ([agent rangeOfString:@"AppleWebKit"].location != NSNotFound &&
[request.URL isImageURL]) {
return YES;
}
}
return NO;
}
//用来判断链接时候是图片地址
- (BOOL)isImageURL {
NSArray *extensions = @[@"jpg", @"jpeg", @"png",@"webp"];
for (NSString *extension in extensions) {
if ([self.absoluteString.lowercaseString rangeOfString:extension options:NSCaseInsensitiveSearch].location != NSNotFound){
return YES;
}
}
return NO;
}
调用NSURLProtocol的startLoading
方法
- (void)startLoading {
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
//标示改request已经处理过了,防止无限循环
[NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
NSString *URLString = [self.request.URL absoluteString];
// 重定义请求地址
if ([URLString rangeOfString:@"format"].location == NSNotFound) {
URLString = [[BFWebImageHelper imageStringToURL:URLString width:0 height:0] absoluteString];
// if ([URLString rangeOfString:@"!"].location == NSNotFound) {
// // 为图请求地址加载web格式
// URLString = [NSString stringWithFormat:@"%@!/format/webp",URLString];
// }
// // 为图请求地址加载web格式
// URLString = [NSString stringWithFormat:@"%@/format/webp",URLString];
}
else if ([URLString rangeOfString:@"format/jpg"].location != NSNotFound) {
URLString = [[BFWebImageHelper imageStringToURL:URLString width:0 height:0] absoluteString];
// // 将format为jpg改为webp格式
// URLString = [URLString stringByReplacingOccurrencesOfString:@"format/jpg" withString:@"format/webp"];
}
else {
self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];
return;
}
NSURL *url = [NSURL URLWithString:URLString];
[[SDWebImageManager sharedManager] downloadImageWithURL:url
options:SDWebImageAllowInvalidSSLCertificates
progress:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf handleLoadImgWithURL:imageURL image:imageURL];
}];
}
- (void)handleLoadImgWithURL:(NSURL *)imgUrl image:(UIImage *)image{
NSData *data;
// 是否以png结尾
if ([imgUrl.absoluteString.lowercaseString hasSuffix:@".png"]) {
data = UIImagePNGRepresentation(image);
} else {
data = UIImageJPEGRepresentation(image, 1);
}
if (!self.client) {
return ;
}
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
采用如上方案后,Webp为我们节省了至少40%流量,NSURLProtocol在WebP的基础又节省了10%左右,同是呈献给用户的图片与未优化前没有肉眼可以发现的区别,总的来说本次优化比较成功。 以上是我们项目的经验,如有不对欢迎指正~。
-
Previous
OC与JS代码交互原理(混合开发,包含UIWebView、WKWebView) -
Next
解决Framework not found IOSurface for architecture arm64.md