异步下载图片的综合应用

Posted Neo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步下载图片的综合应用相关的知识,希望对你有一定的参考价值。

1.要解决的问题.

给单元格设置从网络上下载的图片.

2.思路:

>1.先同步下载

  bug:下载是一个耗时操作,会阻塞主线程

>2.异步下载图片

  bug:图片发生错行.

    >从网络上请求下来的图片与单元格不匹配.为什么会这样?根本原因是因为重用单元格.

如图所示.

技术分享

  tableView上有3个单元格.每个单元格有图片,文字.文字是通过plist加载,当向上滚动单元格,让,单元格1滚出界面,那么就会加载第4个单元格,第四个单元格仍然是新创建的,因为当单元格1尚未滚出视图,单元格4已经出来一部分,是不可能重用的.当单元格4完全加载出来,单元格1完全滚出界面,缓存池中就有了可重用单元格,当加载第5个单元格,就不会重写创建单元格,而是到缓存池中找,于是把第1个单元格,放到第5个单元格的位置,如果此时并不根据indexPath设置第五个单元格的数据,那么就会发生单元格重用,第5个单元格和第1个单元格完全一样,因此,通常为了解决单元格重用,需要根据indexPath重新设置单元格5的数据.

  因为从网络上请求数据是比较耗时的,可能会发生这样一种情况:

  单元格1的图片尚未请求到,就已经滚出界面,单元格5重用了单元格1的数据,通过根据indexPath获取plist文件中对应的文字数据,可以方式重用导致的问题.单元格同样要发送请求到网络上请求自己的图片,但是如果单元格1请求图片比较慢,那么,单元格5会先设置自己的图片,然后又设置单元格1的图片,于是发生覆盖.导致图片与文字不匹配.

那么plist设置文字会发生这种状况么?首先,我觉得如果从plist文件中加载数据也像从网络中请求数据那样耗时,并且取不同的字符串耗时差距比较大,也会发生覆盖问题.但是我觉得从本地取数据是很快的,并且,即是耗时,从plist中取一个长度为10,和长度为100的字符串也是差不多的.这种误差通常应该不会发生

  SDWebImage的解决方式,即是斩断这种多个数据填一单元格的问题,当单元格已经出去了,却没有下载好图片,那么就取消单元格1的下载操作,于是滚到单元格5的时候,这个单元格就只有一个网络请求,不会再有比它更慢的网络请求的图片回来覆盖单元格5对应的正确的数据.

  这种实现比较麻烦,先考虑简单的解决方式.

  方式1:并不直接将网络请求的数据设置给单元格,而是通过给plist文件对应的模型增加一个属性UIImage,因为模型数据总是一一对应的,因为角标不同,当单元格1的图片数据请求回来,设置给模型中的image属性,因为---------会不会发生这种情况,将请求回来的图片设置给模型的image属性的时候发生错乱?比如,单元格1请求数据比较慢,单元格3请求下来的数据设置给单元格1对应的image属性?就目前这种写法,应该不会,因为实际上,每次请求图片都是新开了了线程,如果CPU分配,那么每一个单元格对应着一个线程,也就是单元格3请求数据和单元格1请求图片和设置图片是在不同线程,不会发生设想的问题.假设CPU并不让每一个单元格重新开线程,而是上一个执行完了再将新的任务添加到同一个线程中,也不会发生设想的这种错乱.

  因为设置数据的时候,是在子线程请求图片的,因而可以直接忽略该段代码,因而会导致一个bug,图片请求回来了,却并没有图片,因为控件是懒加载,当第一次布局单元格的时候,并没有图片请求回来,因而layoutSubViews不会布局图片框的位置,当滑动或者点击单元格,又会调用layoutSubViews方法,此时系统意识到有图片,因而会布局图片,该问题可以通过占位图解决,

 方式2:

  思路和前面仍然是一样,不直接将下载的数据和单元格关联,通过一个中介将下载的图片和plist文件一一对应即可.字典是一一对应的,将模型中的url作为键,(每个单元格都不同,因而是唯一的),将图片作为值存储起来.此时还需要在下载图片前先从图片缓存字典中取,如果有,就设置图片,没有就下载,也可以解决错行问题.SDWebImage只不过是通过NSCache解决的,实现思路也是一样.并且NSCache其实和字典也差不多,只需要setObjectForKey存,ObjectForKey取即可.

>3.有网状态下下载下来的图片需要缓存到本地,这样,当没网,就可以到缓存中取.数据本地化可以使用归档或者plist,我用的是plist,注意,当没有网的时候就可以从沙盒中找.又由于,从沙盒中取效率不如从内存中取高,当第一次从沙盒中加载,再次上下滑动时候,考虑从内存中去,于是可以从沙盒中取出图片后,同时将图片设置给图片缓存.又如果是第一次从网上加载,再次上下滑动,从沙盒中加载,不如直接从内存中加载效率高,考虑从网上下载后同样设置给图片缓存(内存).

 

>4.bug3,即使图片下载下来了,但是再次滚动单元格还是会再去下载,这显示是没必要的,我最初的想法是,给模型增加一个BOOL值,当任务被添加到队列中,将BOOL值设置为YES,表示正在下载,不必重复下载,实际上也是可以解决问题的.但是又有一个问题.如果用户清除缓存了,并且程序没有挂掉,也就是说沙盒中,没有图片,那么可以去图片缓存中取,如果重新运行程序,BOOL值又恢复为nil,又从网上加载,貌似没有问题.????为啥还要移除操作缓存????有没有这样一种可能,BOOL值显示为正在下载,但是图片缓存中没有数据,沙盒中也没有数据,但是又因为BOOL值显示正在下载导致无法再次下载???有可能,当收到内存警告,内存中被清空了,沙盒中又被清除缓存,模型中的BOOL值仍然显示着正在下载,导致无法再次下载.-----确实有问题,当收到内存警告,内存缓存中的图片缓存会被清空,并且沙盒中也被清除缓存,此时操作缓存中显示正在下载,不会再次下载,如果设置了占位图,就会发生只有占位图,如果没有设置占位图,就会发生图片复用.因而,当图片下载完成,需要将操作缓存移除.

>5.通过BOOL值确实可以解决重复下载的问题,但是呢,这样会不会造成代码的耦合性过高呢?假设需要将下载以外的事务抽取出来,不放在控制器中,专门封装到一个管理者类中,操作缓存这一步该如何封装进去?

>6.在增加新的东西前,先优化一下当前代码,你不觉得设置单元格的方法太过于冗长了么?怎么抽取一下?,

 

以上是关于异步下载图片的综合应用的主要内容,如果未能解决你的问题,请参考以下文章

多线程异步加载图片

python异步图片下载(多个url)

iOS Swift CoreData 异步下载图片数据

Android实战简易教程-第七十一枪(异步网络下载网络图片及图片廊制作)

iOS-UIImageView载入网络下载的图片(异步+多线程)

android:异步任务asyncTask介绍及异步任务下载图片(带进度条)