iOS WKWebView 小程序页面图片加载优化

这篇文章介绍在WKWebView页面内加载网络图片时的优化方案;相比直接在H5页面内请求加载一个网络图片,如果能通过原生端的图片框架完成图片的下载与缓存,再把图片数据回传给H5页面显示,那不管是在对缓存的控制提高加载性能上,还是在对下载到的图片数据进行其他处理的空间上,都会有更大的便捷性和效率;

这里第一个要解决的问题是拦截H5页面内的图片加载请求,然后走原生端上的图片处理逻辑,再把数据回传给H5页面;WKWebViewWKURLSchemeHandler机制可以完成针对特定请求的拦截,我们只需要和H5页面内的请求约定一个特定的scheme即可,原生端在判断到对应的scheme后,即做请求拦截,并在原生端处理完图片的下载、缓存等逻辑后在把图片数据回传给H5页面显示;如下所示:

/// 开启特定scheme请求拦截,并在处理后回传数据给H5页面
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask  API_AVAILABLE(ios(11.0)) {
    [super webView:webView startURLSchemeTask:urlSchemeTask];

    NSURLComponents *componets = [[NSURLComponents alloc] initWithString:url.absoluteString];
    NSString *scheme = componets.scheme;
 
    //判断到特定约定的scheme,开启拦截
    if (scheme && [scheme isEqualToString:@"customWebImageScheme"]) {
       
        //开始端上拦截后的图片处理
        SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageAvoidDecodeImage;
        [[SDWebImageManager sharedManager] loadImageWithURL:realWebPUrl options:options progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
              //数据回传给H5页面
               [self responseWithUrlSchemeTask:urlSchemeTask mimeType:MIMEType data:data error:error];
    }];

        return YES;
    }
    return NO;
}

// 回传给H5页面下载处理后的NSData数据
- (void)responseWithUrlSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
                                          mimeType:(NSString *)mimeType
                                                   data:(NSData *)data
                                                   error:(NSError *)error API_AVAILABLE(ios(11.0)) {
    if (!urlSchemeTask || !urlSchemeTask.request || !urlSchemeTask.request.URL) {
        return;
    }
   
    if (error) {
        [urlSchemeTask didFailWithError:error];
    } else {
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL
                                                            MIMEType:mimeType
                                               expectedContentLength:data.length
                                                    textEncodingName:nil];
        [urlSchemeTask didReceiveResponse:response];
        [urlSchemeTask didReceiveData:data ?: [NSData new]];
        [urlSchemeTask didFinish];
    }
}


- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask API_AVAILABLE(ios(11.0)) {
    
}

优化过程

在这个过程中,图片的下载缓存处理过程全部都转移到端上的图片处理框架上了,以上代码示例使用的是SDWebImage(端上有很多其他的图片处理框架、流程基本上是一致的),可以发现,我们回传给H5页面显示的数据只需要NSData类型的数据(如上代码所示: [urlSchemeTask didReceiveData:data ?: [NSData new]];);

在这种场景下,默认的图片处理框架并不会只返回我们需要的NSData类型的数据,而是会抛出UIImage、NSData两个类型的数据对象;也就是在SDWebImage内部实际上在下载到图片的Data数据后,还做了一层UIImage对象转化的操作 (这一步中还涉及到了图片的解码操作),且SDWebImage内部的内存缓存实现方式 实际上缓存的是UIImage对象;这种情况下,我们需要的NSData类型的数据 实际上需要每次访问磁盘缓存才能获取到,这会带来如下几个问题:

  • UIImage:NSData转UIImage是不必要的,且这个过程中涉及的图片解码操作是比较耗费性能的
  • Cache:内存缓存无法被使用,读缓存时需要磁盘IO,影响效率和性能
  • 耗电:频繁的磁盘IO影响耗电;

图片的解码操作经测试和图片的大小有关,经过简单测试单张图片的解码耗时通常在0.1~1.0ms以内,SDWebImage内对图片的解码操作如下所示:

image.png

在下载完网络图片后,就会在一个子线程中开始做图片的解码处理,处理完成后再把UIImage和NSData数据一起抛出;

这些操作在这种场景下都是不必要的(我们只需要回传给H5页面NSData数据),因此我们优化的主要方向是怎么取消掉这些不必要的操作,让图片的拦截加载更有效率,提高加载性能的同时又不影响SDWebImage的默认处理逻辑;总结需要优化的点包括以下几处:

  • 转码优化:直接使用下载后的NSData数据,取消掉不需要的图片解码操作
  • Cache优化:内存缓存的读取效率通常至少是磁盘的10倍以上;修改内存缓存对象,从UIImage改成NSData;
  • 耗电优化:使用内存缓存,避免缓存命中时的频繁IO;

有了特定的优化方向后,我们只需要针对这几个目标对使用到的图片处理框架做适配改造就可以了,针对性的去处或修改掉我们不适用的操作;与此同时,还需要考虑到在改造时与原框架的兼容性;

与原框架的兼容性主要体现在我们的修改不能影响到默认的图片库功能,原因是在应用内可能还有需要用到框架默认能力的场景;文章最后会给出一个优化后实现的demo,可以直接集成使用;

最后

如果你的应用内拦截H5页面的图片请求的场景并不多,则可以直接使用图片处理框架的默认能力,在处理图片不多的情况下优化改进带来的提升也许并不必要;我们公司内部在实现小程序引擎时,使用的是这种拦截方式处理的图片加载;因此对我们来说小程序应用里的很多页面的图片加载请求会很多,在默认使用SDWebImage框架的端上图片处理时,有必要对这部分加载做一个改进优化;

这里在对SDWebImage默认的实现方式做一个补充说明,端上的图片处理框架本质上是从原生端的应用场景出发的,默认的逻辑都是针对原生端的图片加载做的优化;在原生应用上我们主要是通过UIImageView、UIButton等UI控件来加载显示图片的,这些控件都通过直接设置UIImage对象来加载图片,由此默认的UIImage内存缓存在这种场景下是高效的,图片的解码操作也能避免主线程解码的性能阻塞;但是在与H5的交互上,这些操作的契合程度就不那么高了,才需要我们有针对性的做一些改造优化,以提高图片的加载性能;

Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容