public abstract class LruFileCache<K, T> {
private final LinkedHashMap<String, FileWrapper> lruEntries = new LinkedHashMap<>(0, 0.75f, true);
private long maxSize = 100;
private File rootFile;
private final Runnable cleanupRunnable = new Runnable() {
public void run() {
synchronized (lruEntries) {
try {
long curSize = 0;
Iterator iterator = lruEntries.values().iterator();
while (curSize < maxSize) {
FileWrapper file = iterator.next();
curSize += file.length();
}
while (iterator.hasNext()) {
FileWrapper file = iterator.next();
file.delete();
lruEntries.remove(file.name);
}
} catch (IOException ignored) {
}
}
}
};
private final Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory() {
@Override public Thread newThread(Runnable runnable) {
Thread result = new Thread(runnable, name);
result.setDaemon(true);
return result;
}});
public put(K key, T value) {
String fileName = getFileName(key);
Editor editor = getEditor(fileName);
BufferedSink sink = Okio.buffer(editor.newSink());
sink.writeUtf8(key.toString())
.writeUtf8(": ")
.writeUtf8(value.toString())
.writeByte('\n');
sink.close();
executor.execute(cleanupRunnable);
}
public T get(K key) {
String fileName = getFileName(key);
Editor editor = getEditor(fileName);
BufferedSource source;
try {
Source in = editor.newSource();
source = Okio.buffer(in);
} finally {
in.close();
}
if (source == null) return null;
String keyAndValueStr = source.readUtf8LineStrict();
String valueStr = keyAndValue.subString(keyAndValue.indexOf(": "), keyAndValue.length());
return T.fromString(valueStr);
}
private synchronized Editor getEditor(String fileName) throws IOException {
initialize();
FileWrapper wrapper = lruEntries.get(fileName);
if (wrapper != null && wrapper.currentEditor != null)
return null;
if (wrapper == null || !wrapper.exists()) {
wrapper.createNewFile();
lruEntries.put(fileName, wrapper);
}
Editor editor = new Editor(wrapper);
return editor;
}
abstract String getFileName(K key);
}
public class Editor {
private FileWrapper wrapper;
Editor(FileWrapper wrapper) {
this.wrapper = wrapper;
}
public Source newSource() {
synchronized (wrapper) {
if (wrapper.currentEditor != this)
return null;
try {
return Okio.source(file);
} catch (FileNotFoundException e) {
return null;
}
}
}
public Sink newSink() {
synchronized (wrapper) {
if (entry.currentEditor != this)
return Okio.blackhole();
Sink sink;
try {
sink = Okio.sink(file);
} catch (FileNotFoundException e) {
return Okio.blackhole();
}
return sink;
}
}
}