android代码解析之图片缓存(ImageDownloader)
在android代码里development/samples有一个工具类:ImageDownloader,其作用是从网上下载图片显示到给定的ImageView.如下时原文说明:
This helper class download images from the Internet and binds those with the provided ImageView.
这里来解析一下这个工具类是如何实现的,下面一个一个来解释.
(1)首先,他采用了强引用(StrongReference)和软引用(SoftReference)来保存下载的图片(bitmap),具体做法是:StrongReference来保存一定容量的图片(bitmap),当超过这个容量的时候就将其移入SoftReference来保存.
其中这里的强StrongReference来保存图片(bitmap)实际上是采用LinkedHashMap来实现的! 如下代码所示:
private final static HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {// private static final long serialVersionUID = -7190622541619388252L; @Override protected boolean removeEldestEntry(Map.Entry<String, Bitmap> eldest) { if (size() > HARD_CACHE_CAPACITY) { // Entries push-out of hard reference cache are transferred to soft reference cache sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); return true; } else { return false; } } };上面的代码定义来一个容量为HARD_CACHE_CAPACITY / 2的LinkedHashMap(这里的HARD_CACHE_CAPACITY可以根据你的实际情况来定义,这里定义的是40), 并且该LinkedHashMap时按照取用来排序的,增长率时0.75. 最重要的是从写removeEldestEntry这个方法,这样一来,一旦超过容量就会把最后一个元素移动到sSoftBitmapCache里面.这里的sSoftBitmapCache其实也是一个HashMap. 那sSoftBitmapCache到底是一个什么HashMap呢,这里先卖一个关子.
上面说到的保存图片资源的时使用map的键值对:Map.Entry<String, Bitmap> 这里的String是我们需要下载的图片的路径(也就是网络地址).
(2)每次需要获取一个图片的时候首先检测sHardBitmapCache和sSoftBitmapCache是否已经存在了,否则需要开一个任务来后台下载并处理.这里的后台处理实际上是采用来常用的AsyncTask来实现.下面看看给一个ImageView下载图片的入口方法:
private void forceDownload(String url, ImageView imageView, String cookie) { // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys. if (url == null) { imageView.setImageDrawable(null); return; } if (cancelPotentialDownload(url, imageView)) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); imageView.setImageDrawable(downloadedDrawable); task.execute(url, cookie); } }上面代码中需要解释DownloadedDrawable和BitmapDownloaderTask,其中DownloadedDrawable是一个ColorDrawable, 实际上这里可以自己定义一个预存的图片.
static class DownloadedDrawable extends ColorDrawable { private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { super(Color.BLACK); bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); } public BitmapDownloaderTask getBitmapDownloaderTask() { return bitmapDownloaderTaskReference.get(); } }实际上DownloadedDrawable还缓存来他自己的BitmapDownloaderTask,后面就知道什么用了.
BitmapDownloaderTask其实就是一个AsyncTask,在方法doInBackground里面采用AndroidHttpClient进行从网上下载图片数据并保存为Bitmap.然后在onPostExecute里面处理获取的bitmap
protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } // Add bitmap to cache if (bitmap != null) { synchronized (sHardBitmapCache) { sHardBitmapCache.put(url, bitmap); } } if (imageViewReference != null) { ImageView imageView = imageViewReference.get(); BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); // Change bitmap only if this process is still associated with it if (this == bitmapDownloaderTask) { imageView.setImageBitmap(bitmap); } } }上面的代码首先将获取的bitmap缓存到sHardBitmapCache里面.然后找到对应的ImageView,并设置该bitmap显示.
(3)我们现在思考一个问题: 一般来说手机的运行内存本来就紧张,这里sHardBitmapCache和sSoftBitmapCache由于加载了大量的图片资源,难免没有上面问题,最重要的是sHardBitmapCache和sSoftBitmapCache都是static的变量.所以实际上他们保存的数据时没有必要一直存在的,所以只要不需要的时候就需要clear掉.
(4)最后来看看sSoftBitmapCache到底用什么HashMap可以. 上面说了bitmap的加载实际上采用了AsyncTask的多线程方式, 所以是很可能出现多个修改并发操作的.所以采用ConcurrentHashMap最合适.ConcurrentHashMap采用了锁分离技术可以很好的解决多操作并发进行,并且也是线程安全的.
// Soft cache for bitmap kicked out of hard cache private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。