背景:Android系统分配给每个应用程序的内存有限,而图片资源非常消耗内存(即Bitmap)
原因:若对Bitmap的使用与内存管理不当,则可能引发内存溢出,从而导致程序崩溃
Bitmap占多大的内存?
bitmap.getAllocationByteCount(); 或者
bitmap.getByteCount();
加载Bitmap的时候,通常要做这几件事
一、根据分辨率适配与缩放图片
-
设置多套图片资源
如果项目大小允许,至少在xh/xxh/xxxh中都放置图片,然后通过BitmapFactory.decodeResource获取 -
缩放图片比例进行压缩,按图片分辨率需求加载,用inSampleSize
示例代码:BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 4; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
二、按需选择合适的解码方式:options.inPreferredConfig
优化原因:不同的图片解码方式对应的内存占用大小相差很大
知识点:
-
ARGB_4444: 基本不用,因为图片比较失真
-
示例ARGB_8888: 32位的ARGB位图,由4个8位组成(即32位),每个像素占8位,即A=8,R=8,G=8,B=8。 8+8+8+8 = 32位= 4个字节。即1个像素占4个字节
示例:若1张480*800的图片,格式是ARGB_8888,将占1536000字节,即1500KB的内存
3.默认使用解码方式:ARGB_8888
特别注意:PNG图片设置无效
示例代码:BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher,options1);
四、使用完毕后释放图片资源
优化原因:使用完毕后若不释放图片资源,容易造成内存泄漏,从而导致内存溢出
优化方案:
在 Android2.3.3(API 10)前,调用 Bitmap.recycle()方法
在 Android2.3.3(API 10)后,采用软引用(SoftReference)
示例代码:
View view = findViewById(R.id.button);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
Drawable drawable = new BitmapDrawable(bitmap);
SoftReference drawableSoftReference =
new SoftReference(drawable);
Drawable bgdrawable = drawableSoftReference.get();
if(bgdrawable != null) {
view.setBackground(bgdrawable);
}
需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃
三、设置图片缓存
优化原因:重复加载图片资源消耗太多资源(cpu,内存与流量)
优化方案:使用三级缓存机制或者软引用
三级缓存机制:内存缓存 – 本地缓存(硬盘、数据库或文件) – 网络缓存
为什么要使用三级缓存:
假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。
特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
什么是三级缓存:
网络缓存, 不优先加载, 速度慢,浪费流量
本地缓存, 次优先加载, 速度快
内存缓存, 优先加载, 速度最快
三级缓存原理:
首次加载App时,通过网络交互来获取图片,之后将图片保存到本地SD卡和内存中
之后运行App时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的,如果SD卡中没有,则加载网络的
内存缓存示例代码
关键代码LruCache
/**
* 三级缓存之内存缓存
*/
public class MemoryCacheUtils {
// private HashMap mMemoryCache=new HashMap();//1.因为强引用,容易造成内存溢出,所以考虑使用下面弱引用的方法
// private HashMap> mMemoryCache = new HashMap();//2.因为在Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache
private LruCache mMemoryCache;
public MemoryCacheUtils(){
long maxMemory = Runtime.getRuntime().maxMemory()/8;//得到手机最大允许内存的1/8,即超过指定内存,则开始回收
//需要传入允许的内存最大值,虚拟机默认内存16M,真机不一定相同
mMemoryCache=new LruCache((int) maxMemory){
//用于计算每个条目的大小
@Override
protected int sizeOf(String key, Bitmap value) {
int byteCount = value.getByteCount();
return byteCount;
}
};
}
/**
* 从内存中读图片
* @param url
*/
public Bitmap getBitmapFromMemory(String url) {
//Bitmap bitmap = mMemoryCache.get(url);//1.强引用方法
/*2.弱引用方法
SoftReference bitmapSoftReference = mMemoryCache.get(url);
if (bitmapSoftReference != null) {
Bitmap bitmap = bitmapSoftReference.get();
return bitmap;
}
*/
Bitmap bitmap = mMemoryCache.get(url);
return bitmap;
}
/**
* 往内存中写图片
* @param url
* @param bitmap
*/
public void setBitmapToMemory(String url, Bitmap bitmap) {
//mMemoryCache.put(url, bitmap);//1.强引用方法
/*2.弱引用方法
mMemoryCache.put(url, new SoftReference(bitmap));
*/
mMemoryCache.put(url,bitmap);
}
}
图片三级缓存这个,现在基本都是用成熟的图片框架Glide或者picasso
针对Glide的skipMemoryCache(false)//启动内存缓存 diskCacheStrategy() //硬盘缓存策略
参考:https://www.jianshu.com/nb/21401973