公元前
之前还是学生时代的时候给社团们学弟学妹们介绍iOS编程的时候,简单的准备了图灵ios培训第一周(使用UIWebView创建简易浏览器),
NSURL *url =[NSURL URLWithString:urlString];
NSLog(urlString);
NSURLRequest *request =[NSURLRequest requestWithURL:url];
[webView loadRequest:request];
运用的就是上面的这种三行代码搞定一切的网页加载的方法。
公元后
后来在项目中遇到了使用UIWebView控件时,理所当然的愚蠢的
用了里面的方法完成了Boss的需求,但后期测试的时候,在肾4上
以及用Charles工具模拟慢网速的时候发现这样做的用户体验不太
好,主要的问题就是当该网页内容非常多的时候,在2G网络和移
动3G网络的时候,出现加载太慢,出现卡顿的现象。甚至在旧机
器上会出现崩溃的现象。
分析一下原因,主要由以下几种原因:
1.旧的手机CPU性能内存较差,一下占用率太高。(PS:后来
iOS8之后苹果出了新的WebKit框架WKWebKit,性能提升了不
少,建议不需要适配iOS8以下的可以考虑尝试)
2.页面内容较多,数据量庞大,即使是新款手机也扛不住呀(最
典型的应该是天猫商城App的首页啦,下拉了几分钟还没有到达尽
头,一下子加载全部数据,手机肯定扛不住呀)
3. 运用上面那种人人都会的没技术含量的代码是在主线程里面进
行的,数据量大,网速不行,会一值在加载,影响用户进行其他
操作
那怎样解决上边的问题呢?
换手机?别逗了一个肾机依旧那么贵,NO Pass
加载页面的时候做上缓存,甚至分段的展示数据(先只加载一部分数据,随着用户下拉再逐步加载)
既然担心数据量多造成在主线程调用会卡死,那就想想办法另外开辟线程加载数据。
权衡之后
上边的代码方法是万万行不通的最常用的还是想想办法另辟蹊径的开辟新航线:
这里我们可以用到常用多线程四种方法中的一种:
NSOperationQueue 操作队列中进行编程
1.创建一个队列并初始化:
static NSOperationQueue *queue;
queue=[[NSOperationQueue alloc]init];
2.创建操作对象并封装要执行的任务
NSInvocationOperation *op=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoadWeb) object:nil];
将对象添加到队列中
[queue addOperation:op];
3.开辟一个新的线程,实现执行的任务,获取从服务器上加载的数据,并存储在NSData中
-(void)downLoadWeb
{
NSURL *url=[NSURL URLWithString:@"http://·········.php"];
NSError *error;
NSString *strData=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
NSData *data=[strData dataUsingEncoding:NSUTF8StringEncoding];
if (data !=nil) {
[self performSelectorOnMainThread:@selector(downLoad_completed:) withObject:data waitUntilDone:NO];
}
else
{
NSLog(@"error when download:%@",error);
}
}
4.判断从服务器中正确的获得数据后,再返回主线程中进行数据的加载(为啥要返回主线程,因为苹果规定数据加载到控件上必须在主线程上进行,防止多个线程修改控件引发崩溃和莫名其妙的问题)
-(void)downLoad_completed:(NSData *)data
{
NSURL *url=[NSURL URLWithString:@"http://·········.php"];
NSString *nameType=[self mimeType:url];
NSLog(@"%@",nameType);
[webView loadData:data MIMEType:nameType textEncodingName:@"UTF-8" baseURL:url];
}
上面中用到了UIWebView的
loadData:<#(nonnull NSData *)#> MIMEType:<#(nonnull NSString *)#> textEncodingName:<#(nonnull NSString *)#> baseURL:<#(nonnull NSURL *)#>
//第一个诶参数是一个NSData
//第二个参数是MIMEType
//第三个参数是编码格式
//第四个相对地址。
其中第二参数需要调用一下下面方法,获取指定URL的MIMEType类型
#pragma mark 获取指定URL的MIMEType类型
- (NSString *)mimeType:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//使用同步方法后去MIMEType
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
return response.MIMEType;
}
这里返回的是text/html
第四个参数是传的URL的地址,当时我尝试着赋值nil后发现网页里面的图片就不能正确的显示出来
好了,介绍好了步骤后反思一下为什么这么做比较好的用户体验
1.首先我们运用了多线程加载数据,不影响用户操作其他数据
我还故意调皮的在downLoadWeb中加上下面的代码:
for (int i=1; i<100000000; i++) {
NSLog(@"我卡到你了吗?");
}
测试结果完全不影响用户操作其他地方,要是按原始的三行代
码搞定UIWebView就会出现一直卡的悲催体验。
2.我们先在子线程中把数据加载到NSData中,再 通过loadData:函
数进行加载,相当于进行了本地数据的读取操作,本地读取的速
度是远远大于网络获取的。
3.对于网页数据基本保持不变的,我们完全可以 用数据库存储
NSData里面的数据,下次进入免去了下载的过 程。这在三行代码
的方法是完全行不通的。