首页 > 代码库 > 图片缓存策略

图片缓存策略

图片缓存策略

1、图片缓存策略分析

从网络上加载一张图,然后把它显示到UI上是个很简单的事情。当图片变多时,处理起来就有些麻烦了,很典型的应用场景,如ListView,GridView或者ViePager等。我们既需要保证用户看到更多的图片,以免屏幕出现大面积的空白,又要保证内存能Hold住。

GC会自动释放一个没有强引用的图片或者View,这本来是个好事情,但为了让用户来回滚动时还能快速加载老图片,通常会使用图片缓存。

下面分别讨论下,通过使用Memory Cache和Disk Cache来增加UI的响应速度和流畅度。

 

一、使用Memory Cache

Memory Cache花费定量的内存来换取对图片的快速访问。可以使用LruCache来快速实现。过去,我们通常使用SoftReference或WeakReference来实现Memory Cache,但是在Android 2.3 (API Level 9)之后,GC对SoftReference和WeakReference回收的更快,所以,SoftReference和WeakReference的效果就很差了。而且,在Android 3.0 (API Level 11)之前,Bitmap的像素数据存放在Native Memory中,如何释放是不确定的,存在着内存泄漏,甚至Crash的风险。

为了确定LruCache的大小,我们需要参考如下几个因素:

1、当前进程的剩余内存有多少

2、为了保证良好的用户体验,应用场景显示多少张图片可以满足要求?

3、屏幕的density是多少?不同像素的屏幕对图片质量的要求是不一样的,可以据此在取图时做一些优化。

4、图片被访问的频率是否相差很多?如果是这样的话可以存放在不同的LruCache中。

5、有时需要在数量和质量之间做一些平衡。比如为图片列表在Memory Cache中缓存

较多的缩略图,在UI中先显示缩略图,然后在后台加载一张高清的。

 

这里没有一个精确的数字来做为指导,需要我们在实际中就以上几个因素进行权衡,设置合适的值。至于如何使用LruCache可以参考Support Library中的Demo。

二、使用Disk Cache

Memory Cache可以加快图片的访问速度,但内存有限,不可能所有图片都放在Memory Cache中。当程序先被挤出内存,后又被重新启动时,图片仍需要重新获取。这个时候,可以应用Disk Cache来缓存图片,图片就不需要从网络上获取了,节省了流量,减少了获取时间。但需要注意,Disk Cache的读取涉及到本地IO操作,需要开启后台线程操作。

我们可以像系统Gallery那样使用ContentProvider。另外,Disk Cache也可以很简单地通过Android源码中提供的DiskLruCache来实现。

 

2、图片缓存实现

一、内存缓存

//需要导入外部jar文件 android-support-v4.jar

 importandroid.support.v4.util.LruCache;

 //开辟8M硬缓存空间

private final int hardCachedSize =8*1024*1024;  

 

//hardcache 强引用

private final LruCache<String,Bitmap> sHardBitmapCache = new LruCache<String, Bitmap>(hardCachedSize)

{

  @Override

  public int sizeOf(Stringkey, Bitmap value)

  {

         return value.getRowBytes() *value.getHeight();
    }

 

  @Override

  protected voidentryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue)

  {

    Log.v("tag","hard cache is full , push to soft cache");
      

    //硬引用缓存区满,将一个最不经常使用的oldvalue推入到软引用缓存区

    sSoftBitmapCahe.put(key,new SoftReference<Bitmap>(oldValue));

   }

}

 

//软引用

 private static final intSOFT_CACHE_CAPACITY = 40;

private final static LinkedHashMap<String,SoftReference<Bitmap>> sSoftBitmapCache = new  LinkedHashMao<String,SoftReference<Bitmap>>(SOFT_CACHE_CAPACITY, 0.75f, true)

{

  @Override

  publicSoftReference<Bitmap> put(String key, SoftReference<Bitmap> value)

  {

    returnsuper.input(key, value);

   }

  @Override

  protectedboolean removeEldestEntry(LinkedHashMap.Entry<Stirng,SoftReference<Bitmap>> eldest)

  {

    if(size() >SOFT_CACHE_CAPACITY)

    {

      Log.v("tag","Soft Reference limit , purge one");

      return true;

    }

  return false;

  }

 }

 

 

 //缓存bitmap

public boolean putBitmap(String key,Bitmap bitmap)

{

  if(bitmap !=null)

  {

    synchronized(sHardBitmapCache)

    {

      sHardBitmapCache.put(key,bitmap);

    }

    return true;

  }  

  return false;
 }

 

//从缓存中获取bitmap

public Bitmap getBitmap(String key)

{

  synchronized(sHardBitmapCache)

  {

    final Bitmapbitmap = sHardBitmapCache.get(key);

    if(bitmap !=null)

    {

      return bitmap;

    }

  }

   //硬引用缓存区间中读取失败,从软引用缓存区间读取

  synchronized(sSoftBitmapCache)

  {

    SoftReference<Bitmap>bitmapReference = sSoftBtimapCache.get(key);
    if(bitmapReference != null)

    {

      final Bitmapbitmap2 = bitmapReference.get();

      if(bitmap2 !=null)

      {

        return bitmap2;

      }

      else

      {
                  Log.v("tag","soft reference 已经被回收");

        sSoftBitmapCache.remove(key);

      }

    }

   }

  return null;

 }

 

 

二、磁盘文件缓存

private File mCacheDir = context.getCacheDir();

private static final int MAX_CACHE_SIZE = 20 * 1024 * 1024; //20M

private final LruCache<String, Long> sFileCache = new LruCache<String, Long>(MAX_CACHE_SIZE)

{

  @Override
      public int sizeOf(Stringkey, Long value)

  {

    return value.intValue();

  }
  

  @Override

  protected void entryRemoved(booleanevicted, String key, Long oldValue, Long newValue)

  {

    File file = getFile(key);

    if(file != null)

    {

      file.delete();

    }

   }

 

  private File getFile(String fileName)throws FileNotFoundException

  {

    File file = new File(mCacheDir, fileName);

    if(!file.exists() || !file.isFile())

    {

             thrownew FileNotFoundException("文件不存在或有同名文件夹");

    }

    return file;

   }

 

  //缓存bitmap到外部存储

  public boolean putBitmap(String key, Bitmapbitmap)

  {

    File file = getFile(key);

    if(file != null)

    {

       Log.v("tag", "文件已经存在");

      return true;

     }

    FileOutputStream fos =getOutputStream(key);

    boolean saved = bitmap.compress(CompressFormat.JPEG,100, fos);

    fos.flush();

    fos.close();

    if(saved)

    {

      synchronized(sFileCache)

      {

         sFileCache.put(key,getFile(key).length());

       }

      return true; 

    }

    return false;

   }

 

   //根据key获取OutputStream

  private FileOutputStreamgetOutputStream(String key)

  {

    if(mCacheDir == null)

    return null;

    FileOutputStream fos = newFileOutputStream(mCacheDir.getAbsolutePath() + File.separator + key);

    return fos;

  }

 

  //获取bitmap

  private static BitmapFactory.OptionssBitmapOptions;

  static

  {

    sBitmapOptions = newBitmapFactory.Options();

    sBitmapOptions.inPurgeable=true; //bitmapcan be purged to disk
   }

 

  public Bitmap getBitmap(String key)

  {

    File bitmapFile = getFile(key);

    if(bitmapFile != null)

    {

      Bitmap bitmap =BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null,sBitmapOptions);

            if(bitmap!= null)

     {

        //重新将其缓存至硬引用中

        ...

      }

         }

  }

}

 
摘自: 

android读取大图片并缓存 http://www.cnblogs.com/leehongee/p/3323837.html

 图片缓存实现策略分析  http://blog.csdn.net/a345017062/article/details/8753649