12

这是我正在关注的磁盘缓存教程。我已将源代码下载到 DiskLruCache,但源代码中不存在此示例中使用的方法。

http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#disk-cache

我是否需要自己实现这些方法,或者是否有我在某处缺少的 DiskLruCache 版本?

4

2 回答 2

2

这里是完整的实现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;
    }
}

要初始化缓存,只需new DiskCache().getInstance().requestInit();从 MainActivity 的onCreate(). 另请注意,磁盘操作应在单独的线程中完成,Handler例如AsyncTask. 因此,当您想向/从磁盘缓存添加/获取位图时,请从工作线程执行此操作。

于 2015-09-11T06:06:09.253 回答
1

您将需要使用您自己的提供更高级别抽象的缓存来包装 DiskLruCache。有人已经为位图做到了这一点,请参阅Using DiskLruCache in android 4.0 does not provide for openCache method

或者,您可以使用开源库,例如 Picasso。

于 2014-02-16T20:09:01.363 回答