由于项目对图片加载需求的特殊性,现有图片加载框架无法满足,就自己写了一个简单的图片加载功能,在写的过程中遇到了一些坑,下面就分享下我在图片下载这条线上遇到的坑 和 怎么解决这些坑的。
我们使用如下代码先把图片数据下载到内存里面:
Oh, beautiful code~~ 但是,你中枪了!
试想,你的APP最大可用内存 64M,你现在下载的是一个 80M 的图片会怎么样?Bomb,你的APP炸了,你看,我还没开始(decode)就结束。
在Android开发中提起 OOM 大家立马会想到 "Decode Bitmap",但是OOM并不是"Decode Bitmap"的专属,这里就说明不decode也会出现OOM。我们得想办法解决这个问题,谁也不想做秒男。既然内存不够用,那我们先把数据保存成文件,然后 decodeFile:
诶~,这下没有OOM,但是下载耗时多了好多,原因是每次下载10kb数据就需要写一次文件,IO操作相比内存操作是很耗时的。动作太慢没感觉(其实还是秒男,只是变成了0.5倍速了),不行,我要做真男人。
内存只是不够用并不是不能用了,我们能不能分出5M的内存,先把数据下载到这快内存里面,当这块内存装满时,把这5M的数据拿出来保存到文件里,当然可以,这就是 BufferedOutputStream :
这样,下载5M数据,之前要写512(5*1024/10)次文件,现在只需要写一次,速度自然就提上来了。现在才是真男人,速度可以接受,还不会OOM。
那这个5M怎么来的呢?这个大小取决与你要加载的图片大小分布,目的就是为了在内存允许的情况下让大部分的图片只需要一次写操作就能搞定,尽可能的减小由于写操作带来的速度影响。例如,你的APP加载的图片大部分是1M以内,只有极少数大于1M,那这个大小就是1M。
回过头来看这三种方式,其实就是 空间换时间、时间换空间、权衡方案。
老司机都知道为了防止解析图片时OOM,我们一般先只解析出图片尺寸信息,然后根据尺寸信息和我们实际显示大小来计算出 inSampleSize 的值,然后去 deocde 一个缩放过的图片,代码可能是这样的:
如果你是这样写的,那么你又中枪了(WTF)!
假如图片原始大小480x800,你要显示的大小是250x420,你得到的 inSampleSize 将会是 1,因为 int/int 是取整的,不会对图片做缩放,这可能就是OOM的隐患。
我们在计算 inSampleSize 的时候应该是向上取整:
好啦,剩下的就只需要 decodeBitmap 啦,强无敌!!!