Android知识总结:图片放错mipmap文件夹导致内存泄露的问题
2016-12-01 21:53
579 查看
背景
最近开始着手优化app的性能。其中有这样一个activity,只要手机一开启内存就会飙升二十多M, 之前认为可能是其中大的图片资源解压导致,但是通过查看记录内存的.hprof文件,这个activity布局中一个ImageView中的bitmap竟然引用了二十多M的内存空间, 但是这张图在布局中是一张很小的图,打开图片资源文件,发现原图也仅仅是一张800x800的图…. 奇怪的是,当把这个应用放到一个平板上时,内存又变化的不是那么明显了….分析
我们知道Android中图片正常情况下是以RGBA的格式进行显示,其中RGBA中每一位占一个byte,显示一个像素点需要4个byte。根据经验我们可以猜测Android中位图buffer的大小是 长x宽x4。也就是说一张800x800的图片,解压出来大小800 x 800 x 4等于2.56M, 而事实上我们拿到的buffer大小是这个数字的整整9倍。为了防止其他代码的干扰,我单独开了一个工程,得到的结果也是一样的。通过翻阅一些资料,我发现解压图片的大小和资源文件是有关系的。之前总是听前人说切三套不同尺寸的图,分别放倒hdpi,xhdpi,xxhdpi,然后就万事大吉了,实践中也基本没出过大问题,现在才发现其实原先一直都没有领会不同mipmap文件的真正用途。
下面我使用三台手机做了三个实验,一台是像素密度为640dpi的三星,一台密度为480dpi的华为,还有一台密度为160dpi的华为平板。将一张2000x2000的png图片放入mipmap的mdpi文件下,使用如下代码进行测试
BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.ARGB_8888; Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_mine, opt);
我们在程序中打断点,查看每一个bitmap2中引用的byte数组大小。我们先使用像素密度高达640dpi的三星手机开刀…. 我们可以先猜测一下这张图的解压大小 ,会是16000000吗…..
其实程序运行的结果是,数组大小256 000 000,也就是256M, 是的没错,是256M……起初看到这个结果我也懵逼了,一张小小的图片那里来了那么多额外信息……
下面是这张图在不同手机的不同资源文件夹下解压大小的统计
640dpi的手机 (xxxdpi)
存放图片资源文件夹 文件解压大小 xxxhdpi 16 000 000 xxhdpi 28 451 556 xhdpi 64 000 000 hdpi 112 763 556 mdpi 256 000 000
480dpi的手机(xxdpi)
存放图片资源文件夹 文件解压大小 xxxhdpi 9 000 000 xxhdpi 16 000 000 xhdpi 36 000 000 hdpi 64 000 000 mdpi 144 000 000
160dpi的平板 (mdpi)
存放图片资源文件夹 文件解压大小 xxxhdpi 1 000 000 xxhdpi 1 779 556 xhdpi 4 000 000 hdpi 7 710 556 mdpi 16 000 000
通过上面的结果我们可以看出,只有图片存储到手机本身像素密度对应的dpi文件夹中,文件解压的大小才是理论上的解压大小。
对于高密度的手机,如果资源文件存放于低密度的文件夹中,那么高密度的手机会认为这张图片的像素密度不够,所以解压的时候会自动填充更多的像素信息。
同理,对于低密度的手机,如果资源文件存放与高密度的文件夹中,那么低密度的手机会认为图片密度过高,而自己的密度达不到那么高,所以就会降采样,最终得到的位图也会变小。
所以,针对上面出现的问题我们应该注意以下几问题
1.避免将高分辨率的图放入低密度的资源文件中,否则高密度的手机加载图片时会认为这张图是一张低密度的图,进而填充更多像素点,浪费内存。
2.如果低分辨率手机加载高分辨率文件夹中的图片时,解压的图片会自动降采样,对于一些图片,清晰度会下降,有些线条多的图片会变得非常模糊,所以最好在低分辨率文件夹中放一套图,或者给那些不清晰的图片单独放。
补充
发现一个网站可以对png图片进行再次压缩。我们直接从sketch中导出的png图是非常大的,有些背景图甚至上兆,而apk打包时图片并不会被压缩,这就导致我们的apk包非常大。这个网站为我们提供了png图片压缩的接口
https://tinypng.com/
这里分享一下我使用Python调用其api的脚本
其中 compress 函数用于压缩传入的图片,compressResize函数使用Python的PIL包中的方法将图片长宽缩减为原图的一半,再进行压缩。
tinify.key的值需要自己在网站进行申请
import tinify import os import os.path from PIL import Image tinify.key = 'xxxxxxxxxx'; sourceDir = '/Users/lidechen/Desktop/compress/test/'; targetDir = '/Users/lidechen/Desktop/compress/output/'; targetDirResize = '/Users/lidechen/Desktop/compress/output_resize/'; def compress(filename): source = tinify.from_file(sourceDir+filename) source.to_file(targetDir+filename) print 'done' def compressResize(filename): source = tinify.from_file(sourceDir+filename) im = Image.open(sourceDir+filename); originalWidth = im.size[0] originalHeight = im.size[1] print 'original size '+str(originalWidth)+' x '+str(originalHeight) newWidth = originalWidth/2 newHeight = originalHeight/2 print 'new size '+str(newWidth)+' x '+str(newHeight) resized = source.resize( method = 'fit', width = newWidth, height = newHeight ) resized.to_file(targetDirResize+filename) #source.to_file(targetDirResize+filename) print 'resize done' if "__name == __main__": counter = 0 for root, dirs, files in os.walk(sourceDir): for file in files: if os.path.splitext(file)[1] == '.png': print '' counter = counter + 1; print '######### counter '+str(counter)+'#########' print 'compress .... '+file compress(file); #compressResize(file); print 'done '+file+' !'
相关文章推荐
- Android大图片导致内存问题小结
- Android加载大图片内存溢出的问题总结
- Android开发编码规范导致的内存泄露问题
- 【转】android开发中,可能会导致内存泄露的问题
- Android大图片导致内存问题小结
- Android大图片导致内存问题小结
- Android Handler当做内部类,导致内存泄露的问题解决方案
- android开发中,可能会导致内存泄露的问题
- android图片占用计算&导致内存溢出的问题
- Android 异步获取网络图片并处理导致内存溢出问题解决方法
- Android加载大图片内存溢出的问题总结
- android避免大量图片内存泄露问题
- android开发中,可能会导致内存泄露的问题
- Android Bitmap操作内存问题总结(图片处理、截屏等)
- Android内存泄露问题总结
- Android中Handler使用不当导致内存泄露的问题
- Android加载大图片内存溢出的问题总结
- [置顶] android内存泄漏总结(总结所有导致内存泄露的可能性及解决方案)
- Android---加载图片 解决图片过大导致的内存溢出问题
- Android开发编码规范导致的内存泄露问题