在开发中有这个需求,选择若干个播放资源,然后将基本资源信息发送给js做html的展示,其中包含播放列表和播放区域。
最初的讨论是为了加快开发速度,不走java支持,将需要的数据打包成参数字典的形式来累加到url上,比如:
//dic为已经打包好的字典类型的数据结构,首先把dic转换成data
NSData jsonData = [NSJSONSerializationdataWithJSONObject:dic options:0 error:nil];
//url string is the base play page url
NSMutableString *urlString = 获取baseurl;
//然后将data转换成string
[urlString appendString:[[NSStringalloc]initWithData:jsonDataencoding:NSUTF8StringEncoding]];
//然后进行特殊字符,空格,中文等的转译工作
NSString *finalUrlString = [[urlStringstringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSetURLQueryAllowedCharacterSet]] mutableCopy];
}
//然后把dic作为url?后面的参数部分进行传递
NSMutableURLRequest *request = [[NSMutableURLRequestalloc]initWithURL:[NSURLURLWithString:finalUrlString]];
[self.webViewloadRequest: request];
//js就会收到请求,让后进行解析数据和重新组装
这样实现不需要走java,不需要开新接口,但是问题就来了,当测试的时候请求的数据参数长度达到了一定的长度后,js出错,无法渲染出html页面,此时手动截短url长度,打开浏览器,可以粘贴成功并且可以访问了,说明的确是url长度超限了。就不同的浏览器url超限的长度是不同的,而且即便统一浏览器不同的版本url长度限制页存在差异,比如:(如果参数包含中文,那么所占用的字节更多)
Firefox:
对于Firefox1.5.x,地址栏能显示的URL最大长度是65,536个字符,但实际上有效的URL最大长度不少于100,000个字符。
对于Firefox 3.0.5,mozilla官方论坛上有人测试其URL长度限制为65,000个字符。
也有人说Firefox可以支持URL高达2Gbyte的长度(参考),在data URL中可以运用到这样大数据量的URL。dataURL是一种URL本身包含了实际数据的URL,比如一个图片、一个HTML网页或者全部的数据、代码等等。仅有Firefox支持dataURL。
data URL示例:
This is a data URL ">This is a test |
Safari:
Safari最少支持80,000个字符长度的URL。
Opera:
Opera官方网站上说,Opera并没有强制限制URL的长度。
网友测试Opera 9支持最少190,000个字都长度的URL,并且Opera9的地址栏可以显示、编辑、复制和粘贴完整的URL串。
那么这种方式就是存在隐患的。因此考虑使用java接口或者其他方式。由于pc端使用的localstorage,那么看看wkwebview是否也可以使用localstorage传递参数。答案是肯定的。
于是在didFinishNavigation中添加对应的删除和设置localstorage的操作。发现一个很奇怪的问题,js和app端数据读取的同步时机上总是有问题,导致页面要不就显示旧的内容,要不就什么也显示不出来。折腾半天。最后讨论中突然想起了android和ios中的webview finish并不是真正的监听到了网页最终加载完成,可能此时页面还没有最终完全的加载和渲染出来。于是会有时机上的不对。怎么办呢?因为虽然app不知道确切的finish loading的时间,但是有人知道呀,就是js呀。于是最终的方案确定了,在js finish后通知native我加载完成了,可以给我数据了,于是native此时再删除旧有的localstorage,然后set新的localstorage,并通知js set 完成了,因为js只负责取,他也不知道什么时候真正set成功了。于是需要native 告诉他可以取了,虽然有点儿麻烦,但是最终解决了问题。代码如下:
WKWebViewConfiguration *config = [WKWebViewConfigurationnew];
//添加js调用native的方法
[config.userContentControlleraddScriptMessageHandler:selfname:@"finish"];
self.webView = [[WKWebViewalloc] initWithFrame:self.frameconfiguration:config];
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message{
NSDictionary *sentData = (NSDictionary*)message.body;
NSString *methodName = [sentDataobjectForKey:@"method"];
if([methodName isEqualToString:@"finish"]) {
NSString *removeUserContent = @"localStorage.removeItem('params')";
//移除旧有的localstorage [self.webView evaluateJavaScript:removeUserContent completionHandler:^(id _Nullable obj, NSError * _Nullableerror) {
if (error == nil) {
//设置localstorage
NSString *jsString = [NSString stringWithFormat:@"localStorage.setItem('params', '%@')", self.finalJsonString];
[self.webView evaluateJavaScript:jsString completionHandler:^(id _Nullable obj, NSError * _Nullableerror) {
if (error == nil) {
//调用js端的方法告知已经设置成功了,可以get了 [self.webView evaluateJavaScript:@"succed()" completionHandler:^(id _Nullable script, NSError* _Nullable error) {
if (error == nil) {
} else {
}
}];
}
}];
} else {
}
}];
}
}
经过查找也同时有其他的kvo解决方式,代码如下:
[self.webView addObserver:self forKeyPath: @"loading" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
id observeObj = object;
if (![observeObj isEqual:self.webView]) {
return;
}
if ([keyPath isEqualToString:@"loading"]) {
//loading keypath
NSLog(@"start loading");
BOOL didChange = [[change valueForKeyPath:NSKeyValueChangeNewKey] boolValue];
if (didChange) {
NSLog(@"webview did change");
} else {
NSLog(@"webview loading finished"); // self.webView.loading 为0
//在这里可以做对应的加载完毕后的处理了
}
注意:需要在addobserver后适时地去remove掉这个observer,特别注意当html有刷新机制时会反复调用这一系列状态,所以需要在执行完第一次的处理后就立即将observer移除掉。否则的话可以在退出这个界面的时候remove掉这个observer。
综上所述:还是使用第一种解决方式,让js端告知加载完成了。