全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Android 图片缓存机制的深入理解

Android 图片缓存机制的深入理解

Android加载一张图片到用户界面是很简单的,但是当一次加载多张图片时,情况就变得复杂起来。很多情况下(像ListView、GridView或ViewPager等组件),屏幕上已显示的图片和即将滑动到当前屏幕上的图片数量基本上是没有限制的。

这些组件通过重用已经移除屏幕的子视图来将降低内存的使用,垃圾回收器也会及时释放那些已经不再使用的已下载的图片,这些都是很好的方法,但是为了保持一个流畅的、快速加载的用户界面,就应该避免当再次回到某个页面时而重新处理图片。内存缓存和磁盘缓存可以帮我们做到这些,它们允许组件快速地重新加载已处理好的图片。

使用内存缓存

内存缓存允许快速地访问图片,但它以占用App宝贵的内存为代价。LruCache类(API Level 4的Support Library也支持)特别适合来做图片缓存,它使用一个强引用的LinkedHashMap来保存最近使用的对象,并且会在缓存数量超出预设的大小之前移除最近最少使用的对象。

说明:以前流行的内存缓存方案是使用软引用或弱引用来缓存图片,然而现在不推荐这样做了,因为从android 2.3(API Level 9)起,垃圾收集器更倾向于先回收软引用或弱引用,这样就使它们变得低效。另外在Android 3.0(API Level 11)之前,图片的像素数据是存储在本地内存(native memory)中的,它以一种不可预测的方式释放,因此可能会导致App超过内存限制甚至崩溃。

为了给LruCache设置一个合适的大小,以下是应该考虑的一些因素:

1.你的Activity或App的可用内存是多少?

2.一次展示到屏幕上的图片是多少?有多少图片需要预先准备好以便随时加载到屏幕?

3.设备的屏幕尺寸和密度是多少?像Galaxy Nexus这样的高分辨率(xhdpi)设备比Nexus S这样分辨率(hdpi)的设备在缓存相同数量的图片时需要更大的缓存空间。

4.图片的尺寸和配置是怎样的?每张图片会占用多少内存?

5.图片的访问频率如何?是否有一些图片比另一些访问更加频繁?如果这样的话,或许可以将某些图片一直保存在内存里或者针对不同的图片分组设置不同的LruCache对象。

6.你能否平衡图片质量和数量之间的关系?有时候存储更多低质量的图片更加有用,当在需要的时候,再通过后台任务下载高质量的图片。

这里没有一个具体的大小和计算公式适用于所有的App,你需要分析你的使用情况并得到一个合适的方案。当一个缓存太小时会导致无益的额外的开销,而缓存太大时也可能会引起Java.lang.OutOfMemory异常,另外缓存越大,留给App其他部分的内存相应就越小。

这里是一个为图片设置LruCache的示例:

private LruCache<String, Bitmap> mMemoryCache; 
 
@Override 
protected 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. 
  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); 
} 

说明:在上述例子中,我们分配了应用内存的1/8作为缓存大小,在一个normal/hdpi的设备上最少也有4MB(32/8)的大小。一个800*480分辨率的屏幕上的一个填满图片的GridView大概占用1.5MB(800*480*4byte)的内存,因此该Cache至少可以缓存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); 
  } 
} 

上述线程中,在解码图片之后,也需要把它添加到内存缓存中:

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这样展示大量数据的组件可以很轻易地就占满内存缓存。你的App也可能被类似电话这样的任务打断,当App被切换到后台后也可能被杀死,内存缓存也可能被销毁,一旦用户回到之前的界面,你的App依然要重新处理每个图片。

磁盘缓存可以用来辅助存储处理过的图片,当内存缓存中图片不可用时,可以从磁盘缓存中查找,从而减少加载次数。当然,从磁盘读取图片要比从内存读取慢并且读取时间是不可预期的,因此需要使用后台线程来读取。

说明: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; // 10MB 
private static final String DISK_CACHE_SUBDIR = "thumbnails"; 
 
@Override 
protected 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); 
} 

说明:初始化磁盘缓存需要磁盘操作因此它不应在主线程进行,然而这意味着有可能在磁盘缓存尚未初始化之前就有访问操作发生,为了解决这个问题,在上面的实现中,使用一个锁对象来确保只有在磁盘缓存初始化之后才会从磁盘缓存读取数据。

内存缓存可以直接在UI线程读取,然而磁盘缓存必须在后台线程检查,磁盘操作不应该在UI线程发生。当图片处理完毕后,务必将最终的图片添加到内存缓存和磁盘缓存以备后续使用。

以上就是对Android 图片缓存机制的详解,如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Android  # 图片缓存机制  # 图片缓存机制的详解  # 详解如何从原生Android 跳转到hbuilder项目  # 深踩Android Studio 缓存的坑及解决方法  # 浅谈Android 中图片的三级缓存策略  # 浅谈Android轻量级的数据缓存框架RxCache  # 实现Android 获取cache缓存的目录路径的方法  # Android使用HBuilder的缓存方法  # 加载  # 是一个  # 屏幕上  # 它以  # 移除  # 也有  # 很好  # 就在  # 也会  # 有可能  # 就有  # 如有  # 基础上  # 才会  # 更大  # 适用于  # 会在  # 是从  # 把它  # 有多少 


相关文章: 免费制作海报的网站,哪位做平面的朋友告诉我用什么软件做海报比较好?ps还是cd还是ai这几个软件我都会些我是做网页的?  太原网站制作公司有哪些,网约车营运证查询官网?  网站插件制作软件免费下载,网页视频怎么下到本地插件?  建站ABC备案流程中有哪些关键注意事项?  如何在Golang中使用encoding/gob序列化对象_存储和传输数据  网站制作大概多少钱一个,做一个平台网站大概多少钱?  如何获取开源自助建站系统免费下载链接?  网站制作难吗安全吗,做一个网站需要多久时间?  高端建站三要素:定制模板、企业官网与响应式设计优化  如何在企业微信快速生成手机电脑官网?  正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好?  已有域名如何免费搭建网站?  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  香港服务器网站卡顿?如何解决网络延迟与负载问题?  如何快速使用云服务器搭建个人网站?  清单制作人网站有哪些,近日“兴风作浪的姑奶奶”引起很多人的关注这是什么事情?  黑客入侵网站服务器的常见手法有哪些?  建站之星如何快速生成多端适配网站?  建站之星下载版如何获取与安装?  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  宁波自助建站系统如何快速打造专业企业网站?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  广州营销型建站服务商推荐:技术优势与SEO优化解析  C++时间戳转换成日期时间的步骤和示例代码  沈阳制作网站公司排名,沈阳装饰协会官方网站?  定制建站流程解析:需求评估与SEO优化功能开发指南  武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?  高端企业智能建站程序:SEO优化与响应式模板定制开发  c# 在高并发下使用反射发射(Reflection.Emit)的性能  css网站制作参考文献有哪些,易聊怎么注册?  北京网站制作公司哪家好一点,北京租房网站有哪些?  教育培训网站制作流程,请问edu教育网站的域名怎么申请?  如何高效利用200m空间完成建站?  建站VPS推荐:2025年高性能服务器配置指南  建站之星安装模板失败:服务器环境不兼容?  大连网站设计制作招聘信息,大连投诉网站有哪些?  如何用花生壳三步快速搭建专属网站?  如何在阿里云高效完成企业建站全流程?  大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?  ,想在网上投简历,哪几个网站比较好?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  建站之星伪静态规则如何设置?  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  建站之星安装后界面空白如何解决?  如何通过虚拟主机快速搭建个人网站?  如何用已有域名快速搭建网站?  智能起名网站制作软件有哪些,制作logo的软件?  免费公司网站制作软件,如何申请免费主页空间做自己的网站?  网站微信制作软件,如何制作微信链接?  如何通过西部数码建站助手快速创建专业网站? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。