图片缓存

使用内存缓存

使用内存缓存可以快速的拿到bitmap,但是是以占用应用可用内存为代价的。LruCache类就是为了实现bitmap的缓存的,它把最近被引用的对象存储在了一个强引用类型的LinkedHashMap中,在缓存大小超过它设定的值的时候就会赶出尽量少的最近使用的对象。

注意:在以前,一个比较受欢迎的内存缓存实现是SoftReference或者WeakReference bitmap缓存,然而并不建议这么做。从Android2.3(API 9)开始,android虚拟机的垃圾收集器变得更加的具有侵略性,极有可能会回收掉soft/weak的引用,这可能就会使使用soft/weak的缓存策略失效。而且,在android3.0(API
11)之前,bitmap的数据时存储在本地内存中的,这种内存的释放是不可预期的不可控的,这样就有可能造成内存达到阀值从而崩溃。

为了选择LruCache的合适大小,下面是几个需要考虑的因素:

-有多少图片会一次性的展示在屏幕上?有多少需要在稍后被还原到屏幕上?

-设备的屏幕大小的密度。xhdpi的密度比hdpi的设备需要更多的缓存空间大小。

-图片的尺寸和配置信息,每张图片会占用多少内存

-图片被使用的频率。是否有其中一些图片比其他的图片使用频率更高?如果这样的话,你可能要把这些图片始终放在内存中,甚至为不同的图片组使用多个LruCache对象。

-你能平衡好数量和质量么?有的时候可能存储大量的低质量的图片更加有用,而在后台默默的加载对应图片的高质量版本。

没有适合于所有应用的指定大小或者计算公式。全都依靠你自己的应用的内存使用情况,并自己给出合适的方案。太小不会有明显的效果,太大可能会导致OutOfMemory异常。

下面是一个例子:

private LruCache<String, Bitmap> mMemoryCache;

@Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    // Get max available VM memory, exceeding this amount will throw an    // OutOfMemory exception. Stored in kilobytes as LruCache takes an    // int in its constructor.这里除以1024是因为maxMemory()方法返回的是字节数,而LruCache的构造函数是以KB作为最大值的单位
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // Use 1/8th of the available memory for this memory cache.    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {        @Override        protected int sizeOf(String key, Bitmap bitmap) {            // The cache size will be measured in kilobytes rather than            // number of items.            return bitmap.getByteCount() / 1024;        }    };    ...}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {    if (getBitmapFromMemCache(key) == null) {        mMemoryCache.put(key, bitmap);    }}

public Bitmap getBitmapFromMemCache(String key) {    return mMemoryCache.get(key);}

注意:在这个例子中,八分之一的应用内存分配给了我们的缓存。在hdpi的设备上,这大概是4MB左右的大小。一个480*800设备上的一个全屏的铺满图片的GridView大概会使用1.5(480*800*4 bytes)MB,所以这个能够缓存大概2.5页图片在内存中。

当向ImageView中加载图片的时候,LruCache会首先进行检查。如果发现已经有缓存了,就会立刻使用缓存的图片来更新ImageView的显示,否则就会开启后台线程来处理图片。

public void loadBitmap(int resId, ImageView imageView) {    final String imageKey = String.valueOf(resId);

    final Bitmap bitmap = getBitmapFromMemCache(imageKey);    if (bitmap != null) {        mImageView.setImageBitmap(bitmap);    } else {        mImageView.setImageResource(R.drawable.image_placeholder);        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);        task.execute(resId);    }}

BitmapWorkerTask也需要更新来把下载好的图片加入到缓存中:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {    ...    // Decode image in background.    @Override    protected Bitmap doInBackground(Integer... params) {        final Bitmap bitmap = decodeSampledBitmapFromResource(                getResources(), params[0], 100, 100));        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);        return bitmap;    }    ...}

使用磁盘缓存

像GridView这样的拥有大量数据的组件可能很容易就会填满内存缓存。你的应用也有可能被来电打断,或者在后台运行的时候被kill掉,这个时候如果用户重新进入到应用中,你的应用还要重新处理每一个图片。

磁盘缓存这时候就有用了,它可以帮助减少图片加载的次数(先到内存中找有没有有缓存,没有的话再到磁盘中找,再没有的话则请求网络数据)。当然,从磁盘中加载数据比从内存中加载要慢,并且要在后台线程中进行,因为磁盘的读取时间是不可预期的。

注意:如果缓存的图片频繁的被访问,ContentProvider可能是一个储存缓存图片的更好的选择,例如图片浏览器(Image Gallery)应用。

下面的实例代码是从android源码中抽取的一段DiskLruCache的代码。这里除了已经存在的内存缓存之外,还添加了一个磁盘缓存:

private DiskLruCache mDiskLruCache;private final Object mDiskCacheLock = new Object();private boolean mDiskCacheStarting = true;private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MBprivate static final String DISK_CACHE_SUBDIR = "thumbnails";

@Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    // Initialize memory cache    ...    // Initialize disk cache on background thread    File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);    new InitDiskCacheTask().execute(cacheDir);    ...}

class InitDiskCacheTask extends AsyncTask<File, Void, Void> {    @Override    protected Void doInBackground(File... params) {        synchronized (mDiskCacheLock) {            File cacheDir = params[0];            mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);            mDiskCacheStarting = false; // Finished initialization            mDiskCacheLock.notifyAll(); // Wake any waiting threads        }        return null;    }}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {    ...    // Decode image in background.    @Override    protected Bitmap doInBackground(Integer... params) {        final String imageKey = String.valueOf(params[0]);

        // Check disk cache in background thread        Bitmap bitmap = getBitmapFromDiskCache(imageKey);

        if (bitmap == null) { // Not found in disk cache            // Process as normal            final Bitmap bitmap = decodeSampledBitmapFromResource(                    getResources(), params[0], 100, 100));        }

        // Add final bitmap to caches        addBitmapToCache(imageKey, bitmap);

        return bitmap;    }    ...}

public void addBitmapToCache(String key, Bitmap bitmap) {    // Add to memory cache as before    if (getBitmapFromMemCache(key) == null) {        mMemoryCache.put(key, bitmap);    }

    // Also add to disk cache    synchronized (mDiskCacheLock) {        if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {            mDiskLruCache.put(key, bitmap);        }    }}

public Bitmap getBitmapFromDiskCache(String key) {    synchronized (mDiskCacheLock) {        // Wait while disk cache is started from background thread        while (mDiskCacheStarting) {            try {                mDiskCacheLock.wait();            } catch (InterruptedException e) {}        }        if (mDiskLruCache != null) {            return mDiskLruCache.get(key);        }    }    return null;}

// Creates a unique subdirectory of the designated app cache directory. Tries to use external// but if not mounted, falls back on internal storage.public static File getDiskCacheDir(Context context, String uniqueName) {    // Check if media is mounted or storage is built-in, if so, try and use external cache dir    // otherwise use internal cache dir    final String cachePath =            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||                    !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :                            context.getCacheDir().getPath();

    return new File(cachePath + File.separator + uniqueName);}
时间: 2024-10-08 10:04:35

图片缓存的相关文章

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============

麦子学院ios笔记:IOS把图片缓存到本地的几种方法

把ios的图片缓存到本地的方法有几种?现在来看看学生在麦子学院学习ios开发的笔记中有讲到哪几种方法呢? <code>把图片缓存到本地,在很多场景都会用到,如果是只储存文字信息,那建一个plist文件,或者数据库就能很方便的解决问题,但是如果存图片到沙盒就没那么方便了.这里介绍两种保存图片到沙盒的方法. </code> 一.把图片转为base64的字符串存到数据库中或者plist文件中,然后用到的时候再取出来 <code class="hljs" obje

AFN清除图片缓存 以及菊花转圈

AFNetworking网络库已经提供了很好的图片缓存机制,效率是比较高的,但是我发现没有直接提供清除缓存的功能,可项目通常都需要添加 清除功能的功能,因此,在这里我以UIImageView+AFNetworking类中添加了下面一个清除功能方法: [objc] view plaincopyprint? /** Clear image cache author: huangyibiao */ + (void)clearCache; [objc] view plaincopyprint? /**

Android图片缓存分析(一)

Android中写应用时,经常会遇到加载图片的事,由于很多图片是网络上下载获取的,当我们进页面时,便会去网络下载图片,一两次可能没啥问题,但如果同一张图片每次都去网络拉取,不仅速度慢,更影响用户体验,同时会浪费用户的流量. 基于此,很多人便想到了图片缓存的方法. 现在比较普遍的图片缓存主要有以下几个步骤: 一.从缓存中获取图片 二.如果缓存中未获取图片,则从存储卡中获取 三.如果存储卡中未获取图片,则从网络中获取 一.从缓存中获取图片 我们知道,Android中分配给每个应用的内存空间是有限的,

图片缓存策略

图片缓存策略 1.图片缓存策略分析 从网络上加载一张图,然后把它显示到UI上是个很简单的事情.当图片变多时,处理起来就有些麻烦了,很典型的应用场景,如ListView,GridView或者ViePager等.我们既需要保证用户看到更多的图片,以免屏幕出现大面积的空白,又要保证内存能Hold住. GC会自动释放一个没有强引用的图片或者View,这本来是个好事情,但为了让用户来回滚动时还能快速加载老图片,通常会使用图片缓存. 下面分别讨论下,通过使用Memory Cache和Disk Cache来增

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近

Android公共库——图片缓存 网络缓存 下拉及底部更多ListView 公共类

Android公共库--图片缓存 网络缓存 下拉及底部更多ListView 公共类 转载自http://www.trinea.cn/android/android-common-lib/ 介绍总结的一些android公共库,包含缓存(图片缓存.预取缓存.网络缓存).公共View(下拉及底部加载更多ListView.底部加载更多ScrollView.滑动一页Gallery).及Android常用工具类(网络.下载.shell.文件.json等等). TrineaAndroidCommon已开源,地

Android 三大图片缓存原理、特性对比

这是我在 MDCC 上分享的内容(略微改动),也是源码解析第一期发布时介绍的源码解析后续会慢慢做的事. 从总体设计和原理上对几个图片缓存进行对比,没用到他们的朋友也可以了解他们在某些特性上的实现. 上篇关于选择开源项目的好处及如何选择开源项目可见:开源项目使用及选型. 一. 四大图片缓存基本信息 Universal ImageLoader 是很早开源的图片缓存,在早期被很多应用使用. Picasso 是 Square 开源的项目,且他的主导者是 JakeWharton,所以广为人知. Glide

android使用ImageLoader实现图片缓存(安卓开发必备)

相信大家在学习以及实际开发中基本都会与网络数据打交道,而这其中一个非常影响用户体验的就是图片的缓存了,若是没有弄好图片缓存,用户体验会大大下降,总会出现卡顿情况,而这个问题尤其容易出现在ListView中的Item有图片的情况中. 前面与大家分享了一个网络连接框架Retrofit,里面也有类似的图片加载的picasso,大家都可以去体验,直通车:http://www.cnblogs.com/liushilin/p/5680135.html 当然还有当前我认为最好用的图片缓存加载框架Fresco,

【WP8】图片缓存控件

在做图片相关的应用的时候,经常需要用大图片的缓存,默认的Image控件不支持缓存的支持,本文自定义一个支持图片缓存的控件 当图片的地址是网络图片时候 根据Url判断该图片是否存在本地,如果存在,则直接从本地读取,如果不存在,则通过Http请求下载该图片,保存到本地,然后读取到Image控件中 当图片为本地地址的时候,直接从本地读取,设置到Image控件中 1.在定义可缓存图片控件之前,先封装一下文件存储的帮助类 using System; using System.IO; using Syste