Java tutorial
package me.aerovulpe.crawler.data; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.annotation.NonNull; import android.util.Log; import com.jakewharton.disklrucache.DiskLruCache; import org.apache.commons.io.IOUtils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Adapted from https://github.com/fhucho/simple-disk-cache * License Apache 2.0 */ public class SimpleDiskCache { private static final int VALUE_IDX = 0; private static final int METADATA_IDX = 1; private static final List<File> usedDirs = new ArrayList<>(); private static final String TAG = "SimpleDiskCache"; private DiskLruCache diskLruCache; private int mAppVersion; private SimpleDiskCache(File dir, int appVersion, long maxSize) throws IOException { mAppVersion = appVersion; diskLruCache = DiskLruCache.open(dir, appVersion, 2, maxSize); } public static synchronized SimpleDiskCache open(File dir, int appVersion, long maxSize) throws IOException { if (usedDirs.contains(dir)) { throw new IllegalStateException("Cache dir " + dir.getAbsolutePath() + " was used before."); } usedDirs.add(dir); return new SimpleDiskCache(dir, appVersion, maxSize); } /** * User should be sure there are no outstanding operations. * * @throws IOException */ public void clear() throws IOException { File dir = diskLruCache.getDirectory(); long maxSize = diskLruCache.getMaxSize(); diskLruCache.delete(); diskLruCache = DiskLruCache.open(dir, mAppVersion, 2, maxSize); } public void setMaxSize(long maxSize) { diskLruCache.setMaxSize(maxSize); } public DiskLruCache getCache() { return diskLruCache; } public InputStreamEntry getInputStream(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return null; return new InputStreamEntry(snapshot, readMetadata(snapshot)); } public BitmapEntry getBitmap(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return null; try { Bitmap bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(VALUE_IDX)); return new BitmapEntry(bitmap, readMetadata(snapshot)); } finally { snapshot.close(); } } public StringEntry getString(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return null; try { return new StringEntry(snapshot.getString(VALUE_IDX), readMetadata(snapshot)); } finally { snapshot.close(); } } public boolean contains(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return false; snapshot.close(); return true; } public OutputStream openStream(String key) throws IOException { return openStream(key, new HashMap<String, Serializable>()); } public synchronized OutputStream openStream(String key, Map<String, ? extends Serializable> metadata) throws IOException { DiskLruCache.Editor editor = diskLruCache.edit(toInternalKey(key)); if (editor == null) { Log.e(TAG, "Unable to open stream."); return new OutputStream() { @Override public void write(int oneByte) throws IOException { // DO NOTHING } }; } try { writeMetadata(metadata, editor); BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX)); return new CacheOutputStream(bos, editor); } catch (IOException e) { editor.abort(); throw e; } } public void put(String key, InputStream is) throws IOException { put(key, is, new HashMap<String, Serializable>()); } public void put(String key, InputStream is, Map<String, Serializable> annotations) throws IOException { OutputStream os = null; try { os = openStream(key, annotations); IOUtils.copy(is, os); } finally { if (os != null) os.close(); } } public void put(String key, String value) throws IOException { put(key, value, new HashMap<String, Serializable>()); } public void put(String key, String value, Map<String, ? extends Serializable> annotations) throws IOException { OutputStream cos = null; try { cos = openStream(key, annotations); cos.write(value.getBytes()); } finally { if (cos != null) cos.close(); } } private void writeMetadata(Map<String, ? extends Serializable> metadata, DiskLruCache.Editor editor) throws IOException { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new BufferedOutputStream(editor.newOutputStream(METADATA_IDX))); oos.writeObject(metadata); } finally { IOUtils.closeQuietly(oos); } } private Map<String, Serializable> readMetadata(DiskLruCache.Snapshot snapshot) throws IOException { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new BufferedInputStream(snapshot.getInputStream(METADATA_IDX))); @SuppressWarnings("unchecked") Map<String, Serializable> annotations = (Map<String, Serializable>) ois.readObject(); return annotations; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { IOUtils.closeQuietly(ois); } } private String toInternalKey(String key) { return md5(key); } private String md5(String s) { try { MessageDigest m = MessageDigest.getInstance("MD5"); m.update(s.getBytes("UTF-8")); byte[] digest = m.digest(); BigInteger bigInt = new BigInteger(1, digest); return bigInt.toString(16); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new AssertionError(); } } private class CacheOutputStream extends FilterOutputStream { private final DiskLruCache.Editor editor; private boolean failed = false; private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) { super(os); this.editor = editor; } @Override public void close() throws IOException { IOException closeException = null; try { super.close(); } catch (IOException e) { closeException = e; } if (failed) { editor.abort(); } else { editor.commit(); } if (closeException != null) throw closeException; } @Override public void flush() throws IOException { try { super.flush(); } catch (IOException e) { failed = true; throw e; } } @Override public void write(int oneByte) throws IOException { try { super.write(oneByte); } catch (IOException e) { failed = true; throw e; } } @Override public void write(@NonNull byte[] buffer) throws IOException { try { super.write(buffer); } catch (IOException e) { failed = true; throw e; } } @Override public void write(@NonNull byte[] buffer, int offset, int length) throws IOException { try { super.write(buffer, offset, length); } catch (IOException e) { failed = true; throw e; } } } public static class InputStreamEntry { private final DiskLruCache.Snapshot snapshot; private final Map<String, Serializable> metadata; public InputStreamEntry(DiskLruCache.Snapshot snapshot, Map<String, Serializable> metadata) { this.metadata = metadata; this.snapshot = snapshot; } public InputStream getInputStream() { return snapshot.getInputStream(VALUE_IDX); } public Map<String, Serializable> getMetadata() { return metadata; } public void close() { snapshot.close(); } } public static class BitmapEntry { private final Bitmap bitmap; private final Map<String, Serializable> metadata; public BitmapEntry(Bitmap bitmap, Map<String, Serializable> metadata) { this.bitmap = bitmap; this.metadata = metadata; } public Bitmap getBitmap() { return bitmap; } public Map<String, Serializable> getMetadata() { return metadata; } } public static class StringEntry { private final String string; private final Map<String, Serializable> metadata; public StringEntry(String string, Map<String, Serializable> metadata) { this.string = string; this.metadata = metadata; } public String getString() { return string; } public Map<String, Serializable> getMetadata() { return metadata; } } }