Bitmap内存管理

内存复用

复用之前

复用之后

代码

BitmapFactory.Options options=new BitmapFactory.Options();
//如果要复用,需要设计成异变
\t\toptions.inMutable=true;
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.wyz_p,options);
for(int i=0;i<100;i++){
options.inBitmap=bitmap;
bitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.wyz_p,options);
}

设计缓存

申请图片--》内存缓存--》(内存缓存放不下了)复用池--》磁盘缓存--》网络找

其中缓存都采用的是LRU算法

内存缓存使用系统提供的LRUCache,磁盘缓存需要下载第三方库DiskLruCache

先定义一个图片缓存管理类,ImageCache.java

/**
* 管理内存中的图片
*/
public class ImageCache {
private static ImageCache instance;
private Context context;
private LruCache<string> memoryCache;
private DiskLruCache diskLruCache;
BitmapFactory.Options options=new BitmapFactory.Options();
/**
* 定义一个复用沲


*/
public static Set<weakreference>> reuseablePool;
public static ImageCache getInstance(){
if(null==instance){
synchronized (ImageCache.class){
if(null==instance){
instance=new ImageCache();
}
}
}
return instance;
}
//引用队列
ReferenceQueue referenceQueue;
Thread clearReferenceQueue;
boolean shutDown;
private ReferenceQueue<bitmap> getReferenceQueue(){
if(null==referenceQueue){
//当弱用引需要被回收的时候,会进到这个队列中
referenceQueue=new ReferenceQueue<bitmap>();
//单开一个线程,去扫描引用队列中GC扫到的内容,交到native层去释放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while(!shutDown){
try {
//remove是阻塞式的
Reference<bitmap> reference=referenceQueue.remove();
Bitmap bitmap=reference.get();
if(null!=bitmap && !bitmap.isRecycled()){
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return referenceQueue;
}
//dir是用来存放图片文件的路径
public void init(Context context,String dir){

this.context=context.getApplicationContext();
//复用池
reuseablePool=Collections.synchronizedSet(new HashSet<weakreference>>());
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//获取程序最大可用内存 单位是M
int memoryClass=am.getMemoryClass();
//参数表示能够缓存的内存最大值 单位是byte
memoryCache=new LruCache<string>(memoryClass/8*1024*1024){
/**
* @return value占用的内存大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前 必需同等大小,才能复用 inSampleSize=1
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount();
}
return value.getByteCount();
}
/**
* 当lru满了,bitmap从lru中移除对象时,会回调
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是设置成能复用的内存块,拉到java层来管理
//3.0以下 Bitmap native
//3.0以后---8.0之前 java
//8。0开始 native
//把这些图片放到一个复用池中
reuseablePool.add(new WeakReference<bitmap>(oldValue,referenceQueue));
}else{
//oldValue就是移出来的对象
oldValue.recycle();
}
}
};
//valueCount:表示一个key对应valueCount个文件
try {

diskLruCache = DiskLruCache.open(new File(dir), BuildConfig.VERSION_CODE, 1, 10 * 1024 * 1024);
}catch(Exception e){
e.printStackTrace();
}
getReferenceQueue();
}
public void putBitmapToMemeory(String key,Bitmap bitmap){
memoryCache.put(key,bitmap);
}
public Bitmap getBitmapFromMemory(String key){
return memoryCache.get(key);
}
public void clearMemoryCache(){
memoryCache.evictAll();
}
//获取复用池中的内容
public Bitmap getReuseable(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
return null;
}
Bitmap reuseable=null;
Iterator<weakreference>> iterator = reuseablePool.iterator();
while(iterator.hasNext()){
Bitmap bitmap=iterator.next().get();
if(null!=bitmap){
//可以复用
if(checkInBitmap(bitmap,w,h,inSampleSize)){
reuseable=bitmap;
iterator.remove();
break;
}else{
iterator.remove();
}
}
}
return reuseable;
}
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
return bitmap.getWidth()==w && bitmap.getHeight()==h && inSampleSize==1;
}
if(inSampleSize>=1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount<=bitmap.getAllocationByteCount();
}
private int getPixelsCount(Bitmap.Config config) {

if(config==Bitmap.Config.ARGB_8888){
return 4;
}
return 2;
}
//磁盘缓存的处理
/**
* 加入磁盘缓存
*/
public void putBitMapToDisk(String key,Bitmap bitmap){
DiskLruCache.Snapshot snapshot=null;
OutputStream os=null;
try {
snapshot=diskLruCache.get(key);
//如果缓存中已经有这个文件 不理他
if(null==snapshot){
//如果没有这个文件,就生成这个文件
DiskLruCache.Editor editor=diskLruCache.edit(key);
if(null!=editor){
os=editor.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG,50,os);
editor.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=snapshot){
snapshot.close();
}
if(null!=os){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 从磁盘缓存中取
*/
public Bitmap getBitmapFromDisk(String key,Bitmap reuseable){
DiskLruCache.Snapshot snapshot=null;
Bitmap bitmap=null;

try {
snapshot=diskLruCache.get(key);
if(null==snapshot){
return null;
}
//获取文件输入流,读取bitmap
InputStream is=snapshot.getInputStream(0);
//解码个图片,写入
options.inMutable=true;
options.inBitmap=reuseable;
bitmap=BitmapFactory.decodeStream(is,null,options);
if(null!=bitmap){
memoryCache.put(key,bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(null!=snapshot){
snapshot.close();
}
}
return bitmap;
}
}
/<weakreference>/<bitmap>/<string>/<weakreference>/<bitmap>/<bitmap>/<bitmap>/<weakreference>/<string>

缩放图片类ImageResize.java

public class ImageResize {
/**
* 缩放bitmap
* @param context
* @param id
* @param maxW
* @param maxH
* @return
*/
public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){
Resources resources = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
// 只解码出 outxxx参数 比如 宽、高
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources,id,options);
//根据宽、高进行缩放
int w = options.outWidth;
int h = options.outHeight;
//设置缩放系数


options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
if (!hasAlpha){
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds = false;
//设置成能复用
options.inMutable=true;
options.inBitmap=reusable;
return BitmapFactory.decodeResource(resources,id,options);
}
/**
* 计算缩放系数
* @param w
* @param h
* @param maxW
* @param maxH
* @return 缩放的系数
*/
private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH){
inSampleSize = 2;
//循环 使宽、高小于 最大的宽、高
while (w /inSampleSize > maxW && h / inSampleSize > maxH){
inSampleSize *= 2;
}
}
return inSampleSize;
}
}

最后使用以上两个类

Bitmap bitmap=ImageCache.getInstance().getBitmapFromMemory(String.valueOf(position));
if(null==bitmap){
//如果内存没数据,就去复用池找
Bitmap reuseable=ImageCache.getInstance().getReuseable(60,60,1);
//reuseable能复用的内存
//从磁盘找
bitmap = ImageCache.getInstance().getBitmapFromDisk(String.valueOf(position),reuseable);
//如果磁盘中也没缓存,就从网络下载
if(null==bitmap){
bitmap=ImageResize.resizeBitmap(context,R.mipmap.wyz_p,80,80,false,reuseable);


ImageCache.getInstance().putBitmapToMemeory(String.valueOf(position),bitmap);
ImageCache.getInstance().putBitMapToDisk(String.valueOf(position),bitmap);
}
}