BitMap高效显示策略(四):使用DiskLruCache作为硬盘缓存

接上篇 BitMap高效显示策略(三):使用内存缓存技术和BitmapFactory.Options.inBitmap参数,在实现内存 缓存的基础上,再添加文件缓存。

由于Android上单个应用最大可用内存的限制,内存缓存的大小是有限的,这是使用内存缓存的第一个局限,第二个局限是,如果关闭应用,下次打开应用又需要重复网络下载-》加入内存缓存-》读取内存缓存的步骤,这样对离线应用来说,仍然不能满足需求,并且在加载效率、节省流量上依然有提升的空间。

针对以上的问题,可以使用文件缓存来弥补内存缓存的不足。

硬盘缓存的原理是,将加载好的图片存入SD卡,下载图片的url作为文件索引,统一存放在一个文件,下次取图片时,从索引文件中获取文件路径,再从该路径加载图片。文件缓存的实现有现成的方案,是DiskLruCache,这个类的使用方法在这个博主的文章中讲的很清楚了,参照:http://blog.csdn.net/guolin_blog/article/details/28863651

下面改写上一篇的代码,实现双缓存:

因为涉及多线程读取文件的问题,所以要对文件进行线程同步操作,首先在ImageCache中加上对象锁:

private final Object mDiskCacheLock = new Object();

为ImageCacheParams添加构造方法:

	public static class ImageCacheParams {
		// mem
		public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
		public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;

		// disk
		public File diskCacheDir;
		public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
		public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;

		public ImageCacheParams(Context context, String diskCacheSubDir) {
			// 初始化缓存目录
			diskCacheDir = getDiskCacheDir(context, diskCacheSubDir);
		}

getDiskCacheDir:

public static File getDiskCacheDir(Context context,
				String diskCacheSubDir) {
			String cachePath = "";
			// 先从sd卡上找空间
			if (Environment.MEDIA_MOUNTED.equals(Environment
					.getExternalStorageState())
					|| Environment.isExternalStorageRemovable()) {
				cachePath = context.getExternalCacheDir().getPath();
			}
			// 否则内部存储中找
			else {
				cachePath = context.getCacheDir().getPath();
			}

			return new File(cachePath + File.separator + diskCacheSubDir);
		}

getDiskCacheDir方法根据手机是否挂载SD卡,设定缓存目录。

接下来添加初始化硬盘缓存的方法:

    public void initDiskCache() {
    	synchronized (mDiskCacheLock) {
    		if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
    			//
    			File diskCacheDir = mCacheParams.diskCacheDir;
    			if (mCacheParams.diskCacheEnabled && diskCacheDir != null) {
    				if (!diskCacheDir.exists()) {
                        diskCacheDir.mkdirs();
                    }
    				if (getUsableSpace(diskCacheDir) > mCacheParams.diskCacheSize) {
    					//init
                        try {
							mDiskLruCache = DiskLruCache.open(
							        diskCacheDir, 1, 1, mCacheParams.diskCacheSize);
						} catch (IOException e) {
							e.printStackTrace();
						}
    				}
    				
    			}
    		}
    		mDiskCacheStarting = false;
    		mDiskCacheLock.notifyAll();
    	}
    }
    @TargetApi(VERSION_CODES.GINGERBREAD)
	public static long getUsableSpace(File path) {
        if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
            return path.getUsableSpace();
        }
        final StatFs stats = new StatFs(path.getPath());
        return  (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
    }

给 addBitmapToCache加上硬盘缓存的部分:

//先判断是否允许文件缓存
    	if (mCacheParams.diskCacheEnabled) {
    		
    		synchronized (mDiskCacheLock) {
    			if (mDiskLruCache != null) {
    				final String key = hashKeyForDisk(url);
    				OutputStream out = null;
    				try {
	    				DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
	    				//如果不存在则存入文件,否则关闭输入流
	    				if (snapshot == null) {
	    					final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
	    					if (editor != null) {
	    						//获取输出流
	    						out = editor.newOutputStream(DISK_CACHE_INDEX);
	    						//压缩后存入
	    						bitmapDrawable.getBitmap().compress(
	    							mCacheParams.compressFormat, mCacheParams.compressQuality, out);
	    						editor.commit();
	    						out.close();
	    					}
	    				} else {
	    					snapshot.getInputStream(DISK_CACHE_INDEX).close();
	    				}
    				} catch (Exception e) {
    					e.printStackTrace();
    				} finally {
    					try {
    						if (out != null) {
								out.close();
    						}
    					} catch (IOException e) {
    						e.printStackTrace();
    					}
    				}
    			}
    		}
    	}
由于url中可能有特殊字符,所以文件名key要经过处理,hashKeyForDisk方法负责这个工作:

    public static String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private static String bytesToHexString(byte[] bytes) {
        // http://stackoverflow.com/questions/332079
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }
从硬盘缓存中获取图片:

    /**
     * 从文件缓存中获取图片
     * @param data
     * @return
     */
    public Bitmap getBitmapFromDiskCache(String url) {
    	String key = hashKeyForDisk(url);
    	Bitmap bitmap = null;
    	synchronized (mDiskCacheLock) {
    		while (mDiskCacheStarting) {
                try {
                    mDiskCacheLock.wait();
                } catch (InterruptedException e) {}
            }
    		
    		if (mDiskLruCache != null) {
    			InputStream inputStream = null;
    			try {
    				DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
    				if (snapshot != null) {
    					if (BuildConfig.DEBUG) {
                            Log.d(TAG, "Disk cache hit");
                        }
    					inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
    					if (inputStream != null) {
    						FileDescriptor fd = ((FileInputStream) inputStream).getFD();
    						bitmap = ImageResizer.decodeSampledBitmapFromDescriptor(
    								fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
    					}
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			} finally {
                    try {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                    } catch (IOException e) {}
                }
    		}
    		
    		return bitmap; 
    	}
    }
缓存操作的一些其他方法:

/**
     * 清空缓存
     */
    public void clearCache() {
    	if (mMemoryCache != null) {
            mMemoryCache.evictAll();
        }
    	synchronized (mDiskCacheLock) {
    		mDiskCacheStarting = true;
    		if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
    			try {
                    mDiskLruCache.delete();
                } catch (IOException e) {
                    Log.e(TAG, "clearCache - " + e);
                }
    			mDiskLruCache = null;
                initDiskCache();
    		}
    	}
    }
    
    //stop时候调用
    public void flush() {
    	synchronized (mDiskCacheLock) {
    		if (mDiskLruCache != null) {
    			try {
    				mDiskLruCache.flush();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    //destroy时候调用
    public void close() {
    	synchronized (mDiskCacheLock) {
    		 try {
	    		if (!mDiskLruCache.isClosed()) {
	    			mDiskLruCache.close();
	    			mDiskLruCache = null;
	    		}
    		 } catch (IOException e) {
    			 e.printStackTrace();
    		 }
    	}
    }

关于代码中初始化硬盘缓存:因为是文件操作,为了避免ANR,所以需要使用异步任务进行文件操作:

ImageWorker.initImageCache:

public void initImageCache(FragmentManager fragmentManager,
            ImageCache.ImageCacheParams cacheParams) {
    	mImageCacheParams = cacheParams;
    	mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams);
    	
    	//硬盘缓存初始化任务
    	new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
    }
CacheAsyncTask:

    protected class CacheAsyncTask extends AsyncTask<Integer, Void, Void> {

		@Override
		protected Void doInBackground(Integer... params) {
			switch (params[0]) {
			case MESSAGE_INIT_DISK_CACHE:
				initDiskCacheInternal();
                break;
			case MESSAGE_FLUSH:
				flushCacheInternal();
			case MESSAGE_CLEAR:
                clearCacheInternal();
                break;
            case MESSAGE_CLOSE:
                closeCacheInternal();
                break;
			}
			
			return null;
		}
    }
    
    protected void clearCacheInternal() {
    	if (mImageCache != null) {
            mImageCache.clearCache();
        }
    }
    
    protected void closeCacheInternal() {
    	if (mImageCache != null) {
    		mImageCache.close();
    		mImageCache = null;
    	}
    }
    
    protected void flushCacheInternal() {
        if (mImageCache != null) {
            mImageCache.flush();
        }
    }
    
    protected void initDiskCacheInternal() {
        if (mImageCache != null) {
            mImageCache.initDiskCache();
        }
    }
    
    public void clearCache() {
        new CacheAsyncTask().execute(MESSAGE_CLEAR);
    }

    public void flushCache() {
        new CacheAsyncTask().execute(MESSAGE_FLUSH);
    }

    public void closeCache() {
        new CacheAsyncTask().execute(MESSAGE_CLOSE);
    }
最后,修改ImageWork的BitmapWorkerTask的 doInBackground方法,加上读取硬盘缓存代码:

加载的最终逻辑为:根据下载url先从内存缓存中读取对应的图片,如果没有,则从硬盘缓存中寻找,如果再没有,则从网络上下载。

final String dataString = String.valueOf(mUrl);
			//先从文件缓存中读取
            if (mImageCache != null && !isCancelled() && getAttachedImageView() != null
                    && !mExitTasksEarly) {
            	bitmap = mImageCache.getBitmapFromDiskCache(dataString);
            }

			if (bitmap == null && !isCancelled()
					&& getAttachedImageView() != null && !mExitTasksEarly) {
				bitmap = processBitmap(mUrl);
			}


Demo下载地址:

http://download.csdn.net/detail/ohehehou/8151069

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值