来自 android 教程的 DiskLruCache 缺少很多方法。
Posted
技术标签:
【中文标题】来自 android 教程的 DiskLruCache 缺少很多方法。【英文标题】:DiskLruCache from android tutorial is missing lots of methods. 【发布时间】:2013-08-29 11:53:27 【问题描述】:这是我正在关注的磁盘缓存教程。我已将源代码下载到 DiskLruCache,但源代码中不存在此示例中使用的方法。
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#disk-cache
我需要自己实现这些方法,还是我在某处缺少某个版本的 DiskLruCache?
【问题讨论】:
code.google.com/p/android/issues/… 您找到正确的 DiskLruCache.java 了吗?也许这可以工作:developer.android.com/samples/DisplayingBitmaps/src/… issuetracker.google.com/issues/37002468 【参考方案1】:这里是DiskLruCache
的完整实现。
首先从 AOSP 下载 DiskLruCache.java。
这是我的DiskCache.java
一个用于基本缓存操作的辅助类。
/**
* Created by Babar on 12-Aug-15.
*/
public class DiskCache
private Context context;
private static DiskCache diskCache;
public DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private BitmapProcessor bitmapProcessor;
private static final int DISK_CACHE_INDEX = 0;
public static boolean mDiskCacheStarting = true;
private static final String DISK_CACHE_SUBDIR = "ooredoo_thumbnails";
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 100; // 100MB
public static DiskCache getInstance()
if(diskCache == null)
diskCache = new DiskCache();
return diskCache;
private DiskCache()
public void requestInit(Context context)
this.context = context;
bitmapProcessor = new BitmapProcessor();
new DiskCacheTask(this).execute(DiskCacheTask.INIT);
public void init() throws IOException
synchronized (mDiskCacheLock)
if(mDiskLruCache == null || mDiskLruCache.isClosed())
File cacheDir = FileUtils.getDiskCacheDir(context, DISK_CACHE_SUBDIR);
if(!cacheDir.exists())
cacheDir.mkdir();
if(FileUtils.getUsableSpace(cacheDir) > DISK_CACHE_SIZE)
mDiskLruCache = DiskLruCache.open(cacheDir, 1, 1, DISK_CACHE_SIZE);
else
Logger.print("InitDiskCache failed: NOT enough space on disk");
mDiskCacheStarting = false; // Finished initialization
mDiskCacheLock.notifyAll(); // Wake any waiting threads
public void addBitmapToDiskCache(final String key, final Bitmap value)
if (key == null || value == null)
return;
synchronized (mDiskCacheLock)
if (mDiskLruCache != null)
OutputStream out = null;
String encryptedKey = CryptoUtils.encryptToMD5(key);
Logger.print("addBitmapToDiskCache encryptToMD5: " + encryptedKey);
try
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(encryptedKey);
if (snapshot == null)
final DiskLruCache.Editor editor = mDiskLruCache.edit(encryptedKey);
if (editor != null)
out = editor.newOutputStream(DISK_CACHE_INDEX);
value.compress(Bitmap.CompressFormat.JPEG, 100, out);
editor.commit();
out.close();
else
snapshot.getInputStream(DISK_CACHE_INDEX).close();
catch (IOException e)
e.printStackTrace();
finally
if (out != null)
try
out.close();
catch (IOException e)
e.printStackTrace();
/**
* Get from disk cache.
*
* @param key Unique identifier for which item to get
* @return The bitmap if found in cache, null otherwise
*/
public Bitmap getBitmapFromDiskCache(final String key)
Bitmap bitmap = null;
String encryptedKey = CryptoUtils.encryptToMD5(key);
Logger.print("getBitmapFromDiskCache encryptToMD5: " + encryptedKey);
synchronized (mDiskCacheLock)
Logger.print("mDiskcachestarting: "+mDiskCacheStarting);
// Wait while disk cache is started from background thread
while (mDiskCacheStarting)
try
mDiskCacheLock.wait();
catch (InterruptedException e)
e.printStackTrace();
if(mDiskLruCache != null)
InputStream inputStream = null;
try
final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(encryptedKey);
if(snapshot != null)
Logger.print("Disk cache hit");
inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
if(inputStream != null)
FileDescriptor fd = ((FileInputStream) inputStream).getFD();
// Decode bitmap, but we don't want to sample so give
// MAX_VALUE as the target dimensions
bitmap = bitmapProcessor.decodeSampledBitmapFromDescriptor(fd);
catch (IOException e)
e.printStackTrace();
finally
if(inputStream != null)
try
inputStream.close();
catch (IOException e)
e.printStackTrace();
Logger.print("dCache getBitmapFromDiskCache synchronized completed");
Logger.print("dCache getBitmapFromDiskCache returning Bitmap");
return bitmap;
public void requestFlush()
new DiskCacheTask(this).execute(DiskCacheTask.FLUSH);
/**
* Flushes the disk cache associated with this ImageCache object. Note that this includes
* disk access so this should not be executed on the main/UI thread.
*/
public void flush()
synchronized (mDiskCacheLock)
if(mDiskLruCache != null)
try
mDiskLruCache.flush();
Logger.print("flush: disk cache flushed");
catch (IOException e)
e.printStackTrace();
public void requestClose()
new DiskCacheTask(this).execute(DiskCacheTask.CLOSE);
/**
* Closes the disk cache associated with this ImageCache object. Note that this includes
* disk access so this should not be executed on the main/UI thread.
*/
public void close()
synchronized (mDiskCacheLock)
if(mDiskLruCache != null)
if(!mDiskLruCache.isClosed())
try
mDiskLruCache.close();
mDiskLruCache = null;
Logger.print("disk cache closed");
catch (IOException e)
e.printStackTrace();
/**
* Do not call this method unless you need to explicitly clear disk cache
*/
public void requestTearDown()
new DiskCacheTask(this).execute(DiskCacheTask.TEAR_DOWN);
public final void tearDown()
synchronized (mDiskCacheLock)
mDiskCacheStarting = true;
if(mDiskLruCache != null && !mDiskLruCache.isClosed())
try
mDiskLruCache.delete();
Logger.print("disk cache cleared");
catch (IOException e)
e.printStackTrace();
mDiskLruCache = null;
这里是BitmapProcesser.java
/**
* @author Babar
* @since 15-Jun-15.
*/
public class BitmapProcessor
public Bitmap decodeSampledBitmapFromStream(InputStream inputStream, URL url,
int reqWidth, int reqHeight) throws IOException
Bitmap bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
int width = options.outWidth;
int height = options.outHeight;
Logger.print("@Req Width: "+reqWidth);
Logger.print("@Req Height: " + reqHeight);
Logger.print("@Actual Width: "+width);
Logger.print("@Actual Height: " + height);
int sampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
inputStream = url.openStream();
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
if(bitmap != null)
width = bitmap.getWidth();
height = bitmap.getHeight();
Logger.print("@inSample:"+sampleSize);
Logger.print("@Modified Width: "+width);
Logger.print("@Modified Height: " + height);
return bitmap;
public Bitmap decodeSampledBitmapFromDescriptor(FileDescriptor fd)
/*final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd, null, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;*/
return BitmapFactory.decodeFileDescriptor(fd);
public Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) throws IOException
Bitmap bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
int width = options.outWidth;
int height = options.outHeight;
Logger.print("@Req Width: "+reqWidth);
Logger.print("@Req Height: " + reqHeight);
Logger.print("@Actual Width: "+width);
Logger.print("@Actual Height: " + height);
int sampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
bitmap = BitmapFactory.decodeFile(pathName, options);
if(bitmap != null)
width = bitmap.getWidth();
height = bitmap.getHeight();
Logger.print("@inSample:"+sampleSize);
Logger.print("@Modified Width: "+width);
Logger.print("@Modified Height: " + height);
return bitmap != null ? rotateBitmapIfNeeded(pathName, bitmap) : null;
public int calculateInSampleSize(BitmapFactory.Options options,
int requiredWidth, int requiredHeight)
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if(width > requiredWidth || height > requiredHeight)
final int halfWidth = width / 2;
final int halfHeight = height / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfWidth / inSampleSize) > requiredWidth
&&
(halfHeight / inSampleSize) > requiredHeight)
inSampleSize *= 2;
return inSampleSize;
public static Bitmap rotateBitmapIfNeeded(String pathName, Bitmap bitmap) throws IOException
ExifInterface exifInterface = new ExifInterface(pathName);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
Logger.logI("BITMAP_ORIENTATION: " + orientation, pathName);
switch (orientation)
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateBitmap(bitmap, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateBitmap(bitmap, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateBitmap(bitmap, 270);
return bitmap;
public Bitmap makeBitmapRound(Bitmap src)
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Canvas canvas = new Canvas(bitmap);
BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rectF = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
Canvas canvas = new Canvas(src);
canvas.drawRoundRect(rectF, 30, 30, paint);
return src;
public static Bitmap rotateBitmap(Bitmap bitmap, int degree)
Matrix matrix = new Matrix();
matrix.postRotate(degree);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
这里是CryptoUtils.java
/**
* @author Babar
* @since 29-Jun-15.
*/
public class CryptoUtils
private static final String MD5_ALGO = "MD5";
public static String encryptToMD5(String text)
try
java.security.MessageDigest md = java.security.MessageDigest.getInstance(MD5_ALGO);
md.update(text.getBytes());
byte[] bytes = md.digest();
String hex = bytesToHexString(bytes);
return hex;
catch (NoSuchAlgorithmException e)
e.printStackTrace();
return String.valueOf(text.hashCode());
private static String bytesToHexString(byte[] bytes)
StringBuffer sb = new StringBuffer();
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();
public static String encodeToBase64(String str)
String tmp = "";
if(isNotNullOrEmpty(str))
try
tmp = new String(Base64.encode(str.getBytes(), Base64.DEFAULT)).trim();
catch(Throwable e)
e.printStackTrace();
return tmp;
这里是DiskCacheTask.java
/**
* Created by Babar on 12-Aug-15.
*/
public class DiskCacheTask extends BaseAsyncTask<Integer, Void, Void>
private DiskCache diskCache;
public static final int INIT = 1;
public static final int FLUSH = 2;
public static final int CLOSE = 3;
public static final int TEAR_DOWN = 4;
public static final int REMOVE = 5;
public DiskCacheTask(DiskCache diskCache)
this.diskCache = diskCache;
@Override
protected Void doInBackground(Integer... params)
switch (params[0])
case INIT:
try
diskCache.init();
catch (IOException e)
e.printStackTrace();
break;
case FLUSH:
diskCache.flush();
break;
case CLOSE:
diskCache.close();
break;
case TEAR_DOWN:
diskCache.tearDown();
break;
case REMOVE:
diskCache.remove();
break;
return null;
要初始化缓存,只需从 MainActivity 的 onCreate()
理想情况下调用 new DiskCache().getInstance().requestInit();
。另请注意磁盘操作应在单独的线程中完成,例如Handler
、AsyncTask
。因此,当您想在磁盘缓存中添加/获取位图时,请从工作线程执行此操作。
【讨论】:
【参考方案2】:您需要使用您自己的提供更高级别抽象的缓存来包装 DiskLruCache。有人已经为位图做了这个,见Using DiskLruCache in android 4.0 does not provide for openCache method
或者,您可以使用开源库,例如 Picasso。
【讨论】:
以上是关于来自 android 教程的 DiskLruCache 缺少很多方法。的主要内容,如果未能解决你的问题,请参考以下文章
来自 Android 中不同类的 setContentView(R.layout.xy)
开发互动直播应用很简单:声网 Android Demo保姆级运行教程