开篇语:
由于业务需求,我们采用了WKWebView和其它view混合布局的展现方案。如果你的WKWebView个数不多,例如同一个页面不超过3个WKWebView,是很难发现这个瓶颈问题。不是内存占用太多,是init确实占用太多主线程时间。如果你尝试异步初始化WKWebView发现是不行的,init必须在主线程中执行。这就导致如果一个页面同时需要多个WKWebView,会产生卡顿问题(当然,新款设备CPU性能强悍,和老设备对比非常明显)。
方案:
船到桥头自然直,车到山前必有路…… 既然躲不过,那就直接面对吧!解决方法就是预初始化N个WKWebView备用,当使用的时候,直接设置HTML。那,预初始化的时候不会卡主线程吗?当然会,不过我们可以将这个过程放到页面加载(网络请求转圈圈)的地方,也就是牺牲用户网速体验,提升页面渲染速度。总体下来,我感觉加载变流畅了呢?,毕竟更多的人已经习惯了网络加载缓慢,反而更关心页面流畅度。况且,根据实际测试,初始化20个WKWebView的时间也就1秒--1.5秒(都是针对老设备而言的),新款设备基本体验不出差别。
说了这么多,无非就是写一个WKWebView池,必要的时间点初始化N个WKWebView,用的时候直接拿即可。如果池中的WKWebView不够用了,那就用一个初始化一个,反正都在主线程,没必要再预初始化更多了。
实现:
简单实现,一个单例管理所有预初始化的WKWebView。
HXWKWebViewPool.h
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
@interface HXWKWebViewPool : NSObject
+ (instancetype)sharedInstance;
/**
预初始化若干WKWebView
@param count 个数
*/
- (void)prepareWithCount:(NSUInteger)count;
/**
从池中获取一个WKWebView
@return WKWebView
*/
- (WKWebView *)getWKWebViewFromPool;
@end
HXWKWebViewPool.m
#import "HXWKWebViewPool.h"
@interface HXWKWebViewPool ()
@property (nonatomic) NSUInteger initialViewsMaxCount; //最多初始化的个数
@property (nonatomic) NSMutableArray *preloadedViews;
@end
@implementation HXWKWebViewPool
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static HXWKWebViewPool *instance = nil;
dispatch_once(&onceToken,^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
return [self sharedInstance];
}
- (instancetype)init
{
self = [super init];
if (self) {
self.initialViewsMaxCount = 20;
self.preloadedViews = [NSMutableArray arrayWithCapacity:self.initialViewsMaxCount];
}
return self;
}
/**
预初始化若干WKWebView
@param count 个数
*/
- (void)prepareWithCount:(NSUInteger)count {
NSTimeInterval start = CACurrentMediaTime();
// Actually does nothing, only initialization must be called.
while (self.preloadedViews.count < MIN(count,self.initialViewsMaxCount)) {
id preloadedView = [self createPreloadedView];
if (preloadedView) {
[self.preloadedViews addObject:preloadedView];
} else {
break;
}
}
NSTimeInterval delta = CACurrentMediaTime() - start;
NSLog(@"=======初始化耗时:%f", delta);
}
/**
从池中获取一个WKWebView
@return WKWebView
*/
- (WKWebView *)getWKWebViewFromPool {
if (!self.preloadedViews.count) {
NSLog(@"不够啦!");
return [self createPreloadedView];
} else {
id preloadedView = self.preloadedViews.firstObject;
[self.preloadedViews removeObject:preloadedView];
return preloadedView;
}
}
/**
创建一个WKWebView
@return WKWebView
*/
- (WKWebView *)createPreloadedView {
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
wkWebConfig.userContentController = wkUController;
WKWebView *wkWebView = [[WKWebView alloc]initWithFrame:CGRectZero configuration:wkWebConfig];
//根据自己的业务需求初始化WKWebView
wkWebView.opaque = NO;
wkWebView.scrollView.scrollEnabled = NO;
wkWebView.scrollView.showsVerticalScrollIndicator = NO;
wkWebView.scrollView.scrollsToTop = NO;
wkWebView.scrollView.userInteractionEnabled = NO;
if (@available(iOS 11.0,*)) {
wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
wkWebView.scrollView.bounces = NO;
wkWebView.backgroundColor = [UIColor clearColor];
return wkWebView;
}
@end
使用的时候,根据业务需求提前预初始化 prepareWithCount:多个WKWebView。使用是时候直接调用单例的getWKWebViewFromPool 得到一个WKWebView即可。
实际测试中,发现页面渲染实际缩短了一倍,旧款设备提升明显。
相关文章:让你的WKWebView支持自动布局----Auto Layout
|