Android-Bitmap图片优化

前言

A:透明度
R:红色
G:绿
B:蓝

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位;
Bitmap.Config ARGB_8888:每个像素占八位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位;
Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位;
Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

思考优化点

  • Android中一般都是默认使用到ARGB_8888,由此可见,显示出图片会相比其他几种多耗费多少内存;
  • ui给出来的切图,没有正确的存放到对应资源文件夹下会不会在加载的时候造成影响?
  • 怎么样取做优化?

案例

相同的一张图,放入到不同的资源文件夹下面,所被加载时所占用的内存大小和获取到的图片宽高不一致;

获取Bitmap占用的字节数:getByteCount();
注意:wyz_p这张图片的宽、高,和它现在存放的文件夹(mipmap-hdpi),
图片852X1048 内存大小:3571584 ==3MB

在这里插入图片描述

和上面是相同的一张图片,只是放入到了对应的文件夹下面之后;
注意:wyz_p这张图片的宽、高,和它现在存放的文件夹(mipmap-xxhdpi),
图片426X524 内存大小:892896 == 1MB都没到

在这里插入图片描述
根据上面的案例,能明白图片是需要放到对应的文件夹下面
图片所占内存空间计算:内存大小计算:宽 X 高 X一个像素点所占的内存空间;
因为图片是ARGB_8888格式,所以一个像素点占4个字节;

优化一

思考点:
我们是不是可以考虑换成其他格式?
比如说换成RGB_565?不需要透明度的图片,我们把它的透明度通道去掉,我们来实践一下;

public static Bitmap resizeBitmap(Context context, int id, boolean hasAlpha) {
        Resources resources = context.getResources();
        BitmapFactory.Options options = new BitmapFactory.Options();
        //需要拿得到系统处理的信息  比如解码出宽高,....
        options.inJustDecodeBounds = true;
        //我们把原来的解码参数改了再去生成bitmap
        BitmapFactory.decodeResource(resources, id, options);


        if(!hasAlpha){
            options.inPreferredConfig=Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds=false;
        return BitmapFactory.decodeResource(resources,id,options);


    }

//调用
Bitmap bitmap2=ImageResize.resizeBitmap(getApplicationContext(),R.mipmap.wyz_p,false);

我们再来打印下:

看打印kai: RGB_565图片426X524 内存大小:446448;
宽高是一样的,内存明显减小了一半;
因为RGB_565 是 一个像素占用2个字节,所以是426X524X2 = 446448;
通过上面的格式通道调整,我们知道不需要透明通道图片,是可以去掉Alpha通道的,是可以用RGB_565的

在这里插入图片描述

优化二

是否还可以继续优化?
思考点
图片的宽高,是不是我们理想的宽高?是不是可以进行缩放操作?
我们来实践下

这里我们来设置下缩放系数,根据传入进来所需的宽高进行缩放到最接近的宽高

 public static Bitmap resizeBitmap(Context context, int id, int maxW, int maxH, boolean hasAlpha) {
        Resources resources = context.getResources();
        BitmapFactory.Options options = new BitmapFactory.Options();
        //需要拿得到系统处理的信息  比如解码出宽高,....
        options.inJustDecodeBounds = true;
        //我们把原来的解码参数改了再去生成bitmap
        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;
        return BitmapFactory.decodeResource(resources,id,options);


    }

    //返回结果是原来解码的图片的大小  是我们需要的大小的   最接近2的几次方倍
    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;
            }
        }
        inSampleSize/=2;
        return inSampleSize;
    }

//调用
Bitmap bitmap2=ImageResize.resizeBitmap(getApplicationContext(),R.mipmap.wyz_p,100,100,false);

我们再来看看打印

kai: 图片107X131 内存大小:28034
我们传入了宽高都是100,通过计算,得出最接近的图片尺寸107X131;
所占内存大小又小了不少

在这里插入图片描述

优化三(内存复用)

我想大家都知道,Bitmap是很耗内存的,如果不使用第三方的图片加载框架的时候,我们在使用列表去加载图片的时候,图片资源特别多的时候,Bitmap不断的开辟内存空间,就会造成内存溢出的情况。
如果对Bitmap熟悉的人,我想也应该知道bitmap的内存块复用:options.inBitmap,只要当前需要用到内存块比这一块小,宽高小一点就能够进行复用;

如果要复用,就一定要设置异变 options.inMutable=true;
复用内存块:options.inBitmap=bitmap;

/**
     *  缩放bitmap 可复用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;
    }

优化四(内存缓存+磁盘缓存+内存复用)

还有如果看过Glide源码的同学,应该会知道它里面有个磁盘缓存,我们可以根据这个磁盘缓存来修改成我们的磁盘缓存。
可以减少网络加载图片的请求时间
在这里插入图片描述

内存缓存和磁盘缓存,我们可以用Lru策略(最近最少使用策略)来进行缓存;
Lru策略就是一个个的节点通过双向链表来保存,如果缓存的个数到了极限,这个时候在进来一个新节点,就会从队尾删除一个节点,将这个新增的节点放入到队伍的最前面;如果是队列中的某一个节点被提取存放进来的时候,会将这个节点取出来,放到队伍的最前面;
双向链表的头指针和尾指针是连接起来的;

github-DiskLruCache
将这个库下载下来,我们来使用,我们先来看看这个库里面的部分源码;

LinkedHashMap是维护了一个Entry的双向链表;
内部还有个线程池
创建对象的时候传入maxSize ,也就是给这个

public final class DiskLruCache implements Closeable {
......
......
//双向链表
private final LinkedHashMap<String, Entry> lruEntries =
      new LinkedHashMap<String, Entry>(0, 0.75f, true);
//线程池
final ThreadPoolExecutor executorService =
      new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
......
......
 private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
    this.directory = directory;
    this.appVersion = appVersion;
    this.journalFile = new File(directory, JOURNAL_FILE);
    this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
    this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
    this.valueCount = valueCount;
    this.maxSize = maxSize;
  }

  /**
   * 通过传入文件目录,
   * Opens the cache in {@code directory}, creating a cache if none exists
   * there.
   *
   * @param directory a writable directory
   * @param valueCount the number of values per cache entry. Must be positive.
   * @param maxSize the maximum number of bytes this cache should use to store
   * @throws IOException if reading or writing the cache directory fails
   */
  public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
      throws IOException {
    if (maxSize <= 0) {
      throw new IllegalArgumentException("maxSize <= 0");
    }
    if (valueCount <= 0) {
      throw new IllegalArgumentException("valueCount <= 0");
    }

    // If a bkp file exists, use it instead.
    File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
    if (backupFile.exists()) {
      File journalFile = new File(directory, JOURNAL_FILE);
      // 如果日志文件也存在,删除备份文件
      if (journalFile.exists()) {
        backupFile.delete();
      } else {
        renameTo(backupFile, journalFile, false);
      }
    }

    // Prefer to pick up where we left off.
    DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
    if (cache.journalFile.exists()) {
      try {
        cache.readJournal();
        cache.processJournal();
        cache.journalWriter = new BufferedWriter(
            new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));
        return cache;
      } catch (IOException journalIsCorrupt) {
        System.out
            .println("DiskLruCache "
                + directory
                + " is corrupt: "
                + journalIsCorrupt.getMessage()
                + ", removing");
        cache.delete();
      }
    }

    // Create a new empty cache.
    directory.mkdirs();
    cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
    cache.rebuildJournal();
    return cache;
  }
......
......

下面是我们自己的单例类

内存缓存用到的是LruCache;
磁盘缓存用到的是DiskLruCache;
定义了一个Set的复用池,里面存放 弱引用Bitmap,但是这个Bitmap是可复用的内存块,而不是直接拿着用的Bitmap图片哦;
定义了引用队列,用来获取被GC回收的弱引用Bitmap对象;开启线程死循环,来不断的扫描这个引用队列;

package com.lk.bitmap_demo;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.LruCache;

import com.lk.bitmap_demo.disk.DiskLruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * 管理内存中的图片
 */
public class ImageCache {
	
    private static ImageCache instance;
    private Context context;
    //内存缓存
    private LruCache<String,Bitmap> memoryCache;
    //磁盘缓存
    private DiskLruCache diskLruCache;
	//用来做内存复用的
    BitmapFactory.Options options=new BitmapFactory.Options();

    /**
     * 定义一个复用沲
     */
    public static Set<WeakReference<Bitmap>> 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是阻塞式的
                            //获取到GC回收了弱引用的Bitmpa对象获取到
                            Reference<Bitmap> reference=referenceQueue.remove();
                            Bitmap bitmap=reference.get();
                            //如果bitmap没有回收释放,那么我们进行回收释放处理
                            if(null!=bitmap && !bitmap.isRecycled()){
                                bitmap.recycle();
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            clearReferenceQueue.start();
        }
        return referenceQueue;
    }
......
......
}

init方法

初始化工作中:初始化了复用池、初始化了内存缓存对象,并设定了缓存的最大值、初始化了磁盘缓存对象、开启了线程去循环扫描GC回收的弱引用对象,从引用队列中获取;
内存缓存对象的初始化,需要设置好缓存的最大值、重写了sizeOf、entryRemoved这两个方法,第一个方法是计算每个缓存的Bitmap对象所占大小,第二个方法是当Lru缓存满了之后,回调出来给我们自己处理的方法;
磁盘缓存对象的初始化,也给定了磁盘缓存最大的缓存空间;

 //dir是用来存放图片文件的路径
    public void init(Context context,String dir){
        this.context=context.getApplicationContext();

        //复用池
        reuseablePool=Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());

        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        //获取程序最大可用内存 单位是M
        int memoryClass=am.getMemoryClass();
        // 内存缓存
        //参数表示能够缓存的内存最大值  单位是byte
        memoryCache=new LruCache<String,Bitmap>(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();
    }
    //将bitmap对象存入到内存缓存中,键值对方式存入
    public void putBitmapToMemeory(String key,Bitmap bitmap){
        memoryCache.put(key,bitmap);
    }
    //获取到内存缓存中存放的bitmap对象,通过key获取
    public Bitmap getBitmapFromMemory(String key){
        return memoryCache.get(key);
    }
    //清空内存缓存
    public void clearMemoryCache(){
        memoryCache.evictAll();
    }


获取复用池中的Bitmap复用内存块

复用池中获取可用的复用内存块;
根据传入的宽高计算出内存,然后比对存放的内存块,看看是否又可用的复用内存块;

//获取复用池中,可用的复用内存块,如果没有的话会返回null
    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<Bitmap>> 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;
                }
            }
        }
        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();
    }
	// 我们开头说到 ARGB_8888格式,一像素占用的是4个字节
	//RGB_565格式,一像素占用的是2个字节
    private int getPixelsCount(Bitmap.Config config) {
        if(config==Bitmap.Config.ARGB_8888){
            return 4;
        }
        return 2;
    }

磁盘缓存的处理

存入磁盘缓存的时候,我们需要查看磁盘缓存中是否已经存放了,如果没有存放,才会存入到磁盘缓存中;
从磁盘缓存中获取的时候,会用可用的复用内存块进行加载这一个Bitmap对象,然后将从磁盘中获取到的Bitmap对象存放到内存缓存中,这样的话下次就能在内存缓存中直接获取到对应的Bitmap对象了,就能省去从磁盘缓存中加载Bitmap对象的这一步了;

//磁盘缓存的处理
    /**
     * 加入磁盘缓存
     */
    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);
            //从磁盘中获取到的图片Bitmap对象,我们会存放到内存缓存中
            if(null!=bitmap){
                memoryCache.put(key,bitmap);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            if(null!=snapshot){
                snapshot.close();
            }
        }
        return bitmap;
    }

完整代码

package com.lk.bitmap_demo;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.LruCache;

import com.lk.bitmap_demo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * 管理内存中的图片
 */
public class ImageCache {

    private static ImageCache instance;
    private Context context;
    //内存缓存
    private LruCache<String,Bitmap> memoryCache;
    //磁盘缓存
    private DiskLruCache diskLruCache;
    //用来做内存复用的
    BitmapFactory.Options options=new BitmapFactory.Options();

    /**
     * 定义一个复用沲
     */
    public static Set<WeakReference<Bitmap>> 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是阻塞式的
                            //获取到GC回收了弱引用的Bitmpa对象获取到
                            Reference<Bitmap> reference=referenceQueue.remove();
                            Bitmap bitmap=reference.get();
                            //如果bitmap没有回收释放,那么我们进行回收释放处理
                            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<Bitmap>>());

        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        //获取程序最大可用内存 单位是M
        int memoryClass=am.getMemoryClass();
        // 内存缓存
        //参数表示能够缓存的内存最大值  单位是byte
        memoryCache=new LruCache<String,Bitmap>(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();
    }
    //将bitmap对象存入到内存缓存中,键值对方式存入
    public void putBitmapToMemeory(String key,Bitmap bitmap){
        memoryCache.put(key,bitmap);
    }
    //获取到内存缓存中存放的bitmap对象,通过key获取
    public Bitmap getBitmapFromMemory(String key){
        return memoryCache.get(key);
    }
    //清空内存缓存
    public void clearMemoryCache(){
        memoryCache.evictAll();
    }

    //获取复用池中,可用的复用内存块,如果没有的话会返回null
    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<Bitmap>> 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;
                }
            }
        }
        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();
    }
    // 我们开头说到 ARGB_8888格式,一像素占用的是4个字节
    //RGB_565格式,一像素占用的是2个字节
    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);
            //从磁盘中获取到的图片Bitmap对象,我们会存放到内存缓存中
            if(null!=bitmap){
                memoryCache.put(key,bitmap);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            if(null!=snapshot){
                snapshot.close();
            }
        }
        return bitmap;
    }

}

如果对Gilde有兴趣了解的,可以看看我这篇;Android-图片加载Gilde

辛苦各位童鞋观看到最后,如果博客中有不对的地方望指出,大神勿喷,谢谢~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值