图片优化
优化图片Bitmap资源的使用 & 内存管理
图片的内存占据了App的大部分
1.使用完毕后释放图片资源 Bitmap.recycle/软引用
使用完毕后 释放图片资源
优化原因
使用完毕后若不释放图片资源,容易造成内存泄露,从而导致内存溢出
优化方案
a. 在 Android2.3.3(API 10)前,调用 Bitmap.recycle()方法
b. 在 Android2.3.3(API 10)后,采用软引用(SoftReference)
2.根据分辨率适配缩放图片
优化原因
若 Bitmap 与 当前设备的分辨率不匹配,则会拉伸Bitmap,而Bitmap分辨率增加后,所占用的内存也会相应增加
因为Bitmap 的内存占用 根据 x、y的大小来增加的
优化方案
(1)设置多套图片资源
(2)BitmapFactory.decodeResource() 对Bitmap根据当前的设备的像素密度进行缩放适配
(3)BitmapFactory.inSampleSize 能够等比的缩放显示图片,同时还避免了需要先把原图加载进内存的缺点
3.按需选择合适的解码方式
BitmapFactory.inPreferredConfig,默认使用解码方式:ARGB_8888
inJustDecodeBounds 使用这个属性去尝试解码图片,可以事先获取到图片的大小而不至于占用什么内存
4.设置图片缓存
三级缓存+软引用
优先内存,其次本地缓存,然后网络加载
https://www.jianshu.com/p/49e4f44d7a83
1.Bitmap 使用注意点
当我们需要获取图片的宽高等属性时且不对数据进行操作,那么我们不应该把图片的数据加载到内存中,这时我们可以设置inJustDecodeBounds属性为true.
BitmapFactory.Options opt=new BitmapFactory.Options();
opt.inJustDecodeBounds=true;(inJustDecodeBounds就是我们不用讲图片加载到内存中。我们只想得到关于图片的信息)
BitmapFactory.decodeFile(filePath, opt);
final int height = options.outHeight;
final int width = options.outWidth;
2.减小Bitmap对象的内存占用
Bitmap是一个极容易消耗内存的大胖子,减小创建出来的Bitmap的内存占用是很重要的,通常来说有下面2个措施:
1.inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
2.decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
3.Bitmap对象的复用
我们知道大多数对象的复用,最终实施的方案都是利用对象池技术,要么是在编写代码的时候显式的在程序里面去创建对象池,然后处理好复用的实现逻辑,
要么就是利用系统框架既有的某些复用特性达到减少对象的重复创建,从而减少内存的分配与回收。
在Bitmap中,利用inBitmap的高级特性提高Android系统在Bitmap分配与释放执行效率上的提升(3.0以及4.4以后存在一些使用限制上的差异)。
使用inBitmap属性可以告知Bitmap解码器去尝试使用已经存在的内存区域,新解码的bitmap会尝试去使用之前那张bitmap在heap中所占据的pixel data内存区域,
而不是去问内存重新申请一块区域来存放bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小。
4.Bitmap对象及时回收
虽然在大多数情况下,我们会对Bitmap增加缓存机制,但是在某些时候,部分Bitmap是需要及时回收的。
例如临时创建的某个相对比较大的bitmap对象,在经过变换得到新的bitmap对象之后,应该尽快回收原始的bitmap,这样能够更快释放原始bitmap所占用的空间。
bitmap.recycle();
5.LRU管理Bitmap
LRU是Least Recently Used 近期最少使用算法。其实LruCache的作用就是对缓存的元素进行排序,当超过设定的内存值时就会将使用最少,使用最早元素先回收。
使用Lru来管理Bitmap,设置最大内存,可以防止出现内存溢出。
相关源码可以看LruCache使用以及源码详细解析
6.直接使用更小的图片
(1).我们可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费.
以七牛为例, 可以在请求图片的url中添加诸如质量, 格式, width, height等path来获取合适的图片资源.七牛
7.如何加载大图,可压缩
图片在磁盘上的实际大小约为3.5 MB,但在内存中的大小为12262248字节,相当于12.3 MB。
存储在磁盘上的图片是被压缩过的(以JPG,PNG或类似的格式存储)。 一旦将图片加载到内存中,它就不再被压缩,并占用尽可能多的图片的所有像素所需的内存空间。
具体方法:得到图片的分辨率,得到手机的分辨率,缩放
先设置inJustDecodeBounds = true ,获取图片的真实宽高,计算压缩比例,
再根据压缩比例显示图片,设置到mImageView。
//创建bitmap工厂的配置参数
BitmapFactory.Options options=new BitmapFactory.Options();
//返回null,不去真正解析位图,只是得到宽高等信息
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile("/storage/sdcard/test.JPG",options);
int imgWidth=options.outWidth;
int imgHeight=options.outHeight;
System.out.print("图片宽"+imgWidth+"图片高"+imgHeight);
// 计算缩放比
int scale=1;
int scalex=imgWidth/screenWidth;
int scaley=imgHeight/screenHeight;
scale=scalex>scaley?scalex:scaley;
//按照缩放比显示图片
options.inSampleSize=scale;
//开始真正解析位图
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile("/storage/sdcard/test.JPG",options);
mImageView.setImageBitmap(bitmap);
8.加载大图,不允许压缩。
首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中,所以肯定是局部加载,就需要用BitmapRegionDecoder
其次,既然屏幕显示不完,那么最起码要添加一个上下左右拖动的手势,让用户可以拖动查看
InputStream inputStream = getAssets().open("tangyan.jpg");
//获得图片的宽、高
BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
tmpOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, tmpOptions);
int width = tmpOptions.outWidth;
int height = tmpOptions.outHeight;
//设置显示图片的中心区域
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);
9.图片数量非常多,则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内。
https://juejin.cn/user/3298190612500696/posts 安卓文章集合
https://juejin.cn/post/6970683481127043085
Glide做了哪些优化?
1.Glide图片加载的总体流程介绍
2.Glide缓存机制做了哪些优化?
3.Glide做了哪些内存优化?
4.Glide如何管理生命周期?
5.Glide怎么做大图加载?
(1)Glide 跟Fresco对比:
Glide:
多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
Fresco:
最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
适用于需要高性能加载大量图片的场景
对于一般App来说,Glide完全够用,而对于图片需求比较大的App,为了防止加载大量图片导致OOM,Fresco 会更合适一些。
并不是说用Glide会导致OOM,Glide默认用的内存缓存是LruCache,内存不会一直往上涨。
(2)图片加载框架的需求:
异步加载:线程池
切换线程:Handler,没有争议吧
缓存:LruCache、DiskLruCache
防止OOM:软引用、LruCache、图片压缩、Bitmap像素存储位置
内存泄露:注意ImageView的正确引用,生命周期管理
列表滑动加载的问题:加载错乱、队满任务过多问题