or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/maven-com-jakewharton--disklrucache

A disk-based implementation of a least-recently used cache for Android compatibility.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.jakewharton/disklrucache@2.0.x

To install, run

npx @tessl/cli install tessl/maven-com-jakewharton--disklrucache@2.0.0

index.mddocs/

DiskLruCache

A disk-based implementation of a least-recently used (LRU) cache that uses a bounded amount of filesystem space. Each cache entry has a string key and a fixed number of values stored as byte sequences, with automatic eviction of least-recently-used entries when storage limits are exceeded.

Package Information

  • Package Name: com.jakewharton:disklrucache
  • Package Type: maven
  • Language: Java
  • Target: Java 1.5+, Android compatible
  • Installation: Add to pom.xml:
    <dependency>
        <groupId>com.jakewharton</groupId>
        <artifactId>disklrucache</artifactId>
        <version>2.0.2</version>
    </dependency>

Core Imports

import com.jakewharton.disklrucache.DiskLruCache;
import java.io.File;
import java.io.IOException;

Basic Usage

import com.jakewharton.disklrucache.DiskLruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

// Create or open cache
File cacheDir = new File(context.getCacheDir(), "image_cache");
int appVersion = 1;
int valueCount = 1; // number of values per entry
long maxSize = 10 * 1024 * 1024; // 10MB
DiskLruCache cache = DiskLruCache.open(cacheDir, appVersion, valueCount, maxSize);

// Write to cache
DiskLruCache.Editor editor = cache.edit("key1");
if (editor != null) {
    OutputStream out = editor.newOutputStream(0);
    // write data to out
    out.close();
    editor.commit();
}

// Read from cache
DiskLruCache.Snapshot snapshot = cache.get("key1");
if (snapshot != null) {
    InputStream in = snapshot.getInputStream(0);
    // read data from in
    String value = snapshot.getString(0); // or read as string
    snapshot.close();
}

// Clean up
cache.close();

Architecture

DiskLruCache uses a journal-based design for persistence and crash recovery:

  • Journal File: Records all cache operations (CLEAN, DIRTY, REMOVE, READ) for crash recovery
  • Entry Management: Uses LinkedHashMap for LRU ordering with automatic eviction
  • Atomic Operations: Edit operations are atomic - entries are either fully committed or aborted
  • Background Cleanup: Automatic size management and journal compaction via background thread
  • File Organization: Each entry stored as separate files in the cache directory

The cache is designed for single-process use with exclusive directory access and provides strong consistency guarantees through its journaling system.

Capabilities

Cache Creation and Management

Create, configure and manage the cache instance lifecycle.

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException;
public synchronized long getMaxSize();
public synchronized void setMaxSize(long maxSize);
public synchronized long size();
public File getDirectory();
public synchronized boolean isClosed();
public synchronized void flush() throws IOException;
public synchronized void close() throws IOException;
public void delete() throws IOException;

Reading from Cache

Retrieve cached entries as snapshots providing access to stored values.

public synchronized Snapshot get(String key) throws IOException;

Writing to Cache

Create and edit cache entries through atomic editor operations.

public Editor edit(String key) throws IOException;
public synchronized boolean remove(String key) throws IOException;

Snapshot Operations

Access cached entry values through immutable snapshots.

public final class Snapshot implements Closeable {
    public Editor edit() throws IOException;
    public InputStream getInputStream(int index);
    public String getString(int index) throws IOException;
    public long getLength(int index);
    public void close();
}

Editor Operations

Create and modify cache entries through atomic edit sessions.

public final class Editor {
    public InputStream newInputStream(int index) throws IOException;
    public OutputStream newOutputStream(int index) throws IOException;
    public String getString(int index) throws IOException;
    public void set(int index, String value) throws IOException;
    public void commit() throws IOException;
    public void abort() throws IOException;
    public void abortUnlessCommitted();
}

Key and Value Constraints

Key Requirements

Cache keys must conform to strict validation rules:

  • Pattern: Must match regex [a-z0-9_-]{1,64}
  • Length: 1 to 64 characters
  • Characters: Only lowercase letters, digits, underscore, and hyphen
  • Case sensitive: Keys are case-sensitive
// Valid keys
cache.edit("user_123");
cache.edit("profile-data");
cache.edit("temp_file_001");

// Invalid keys (throw IllegalArgumentException)
cache.edit("User_123");        // uppercase letters
cache.edit("user/profile");    // forward slash
cache.edit("user@domain.com"); // @ symbol
cache.edit("");                // empty string

Value Structure

Each cache entry has a fixed number of values determined at cache creation:

  • Value Count: Fixed per cache (set in open() call)
  • Size Limits: Each value 0 to Integer.MAX_VALUE bytes
  • Storage: Values stored as byte sequences on disk
  • Access: Values accessible as streams or strings
  • Indexing: Values accessed by zero-based index (0 to valueCount-1)

Error Handling and Thread Safety

Exception Handling

// Methods that throw IOException
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException;
public synchronized Snapshot get(String key) throws IOException;
public Editor edit(String key) throws IOException;
public synchronized boolean remove(String key) throws IOException;
public synchronized void flush() throws IOException;
public synchronized void close() throws IOException;
public void delete() throws IOException;

// Methods that throw IllegalArgumentException  
// Thrown for invalid keys or invalid constructor parameters

Common error scenarios:

  • Invalid Keys: IllegalArgumentException for keys not matching [a-z0-9_-]{1,64}
  • Closed Cache: IllegalStateException when operating on closed cache
  • Concurrent Edits: edit() returns null when entry already being edited
  • I/O Errors: IOException for filesystem operations
  • Missing Entries: get() returns null for non-existent entries

Thread Safety

  • Synchronized Operations: All cache operations are thread-safe
  • Concurrent Reads: Multiple threads can read simultaneously
  • Exclusive Editing: Only one editor per key at a time
  • Background Operations: Automatic cleanup runs on background thread
  • Process Exclusivity: Cache directory must be exclusive to single process

Usage Examples

Image Caching

// Setup image cache
File imageDir = new File(context.getCacheDir(), "images");  
DiskLruCache imageCache = DiskLruCache.open(imageDir, 1, 1, 50 * 1024 * 1024);

// Cache image
public void cacheImage(String url, byte[] imageData) throws IOException {
    String key = urlToKey(url);
    DiskLruCache.Editor editor = imageCache.edit(key);
    if (editor != null) {
        OutputStream out = editor.newOutputStream(0);
        out.write(imageData);
        out.close();
        editor.commit();
    }
}

// Retrieve cached image
public byte[] getCachedImage(String url) throws IOException {
    String key = urlToKey(url);
    DiskLruCache.Snapshot snapshot = imageCache.get(key);
    if (snapshot != null) {
        InputStream in = snapshot.getInputStream(0);
        byte[] data = readFully(in);
        snapshot.close();
        return data;
    }
    return null;
}

private String urlToKey(String url) {
    return url.replaceAll("[^a-z0-9_-]", "_").toLowerCase();
}

Multi-Value Entries

// Cache with multiple values per entry (metadata + content)
DiskLruCache dataCache = DiskLruCache.open(cacheDir, 1, 2, maxSize);

// Store data with metadata
public void cacheWithMetadata(String key, String metadata, byte[] content) throws IOException {
    DiskLruCache.Editor editor = dataCache.edit(key);
    if (editor != null) {
        // Store metadata as string in index 0
        editor.set(0, metadata);
        
        // Store content as bytes in index 1
        OutputStream out = editor.newOutputStream(1);
        out.write(content);
        out.close();
        
        editor.commit();
    }
}

// Retrieve data with metadata
public class CachedData {
    public final String metadata;
    public final byte[] content;
    
    public CachedData(String metadata, byte[] content) {
        this.metadata = metadata;
        this.content = content;
    }
}

public CachedData getCachedData(String key) throws IOException {
    DiskLruCache.Snapshot snapshot = dataCache.get(key);
    if (snapshot != null) {
        String metadata = snapshot.getString(0);
        InputStream contentStream = snapshot.getInputStream(1);
        byte[] content = readFully(contentStream);
        snapshot.close();
        return new CachedData(metadata, content);
    }
    return null;
}

Error Recovery and Cleanup

// Robust cache operations with proper error handling
public boolean safeCacheOperation(String key, byte[] data) {
    DiskLruCache.Editor editor = null;
    try {
        editor = cache.edit(key);
        if (editor == null) {
            return false; // Entry being edited by another thread
        }
        
        OutputStream out = editor.newOutputStream(0);
        out.write(data);
        out.close();
        editor.commit();
        return true;
        
    } catch (IOException e) {
        if (editor != null) {
            try {
                editor.abort();
            } catch (IOException ignored) {
            }
        }
        return false;
    }
}

// Proper cache shutdown
public void shutdownCache() {
    try {
        if (cache != null && !cache.isClosed()) {
            cache.flush(); // Ensure pending writes complete
            cache.close();
        }
    } catch (IOException e) {
        // Log error but continue shutdown
    }
}

Constants and Validation

Public Constants

public static final String JOURNAL_FILE = "journal";
public static final String JOURNAL_FILE_TEMP = "journal.tmp";
public static final String JOURNAL_FILE_BACKUP = "journal.bkp";
public static final String MAGIC = "libcore.io.DiskLruCache";
public static final String VERSION_1 = "1";
public static final long ANY_SEQUENCE_NUMBER = -1L;
public static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");

Validation Rules

  • Directory: Must be writable and exclusive to the cache
  • App Version: Used for cache invalidation when app updates
  • Value Count: Must be positive, fixed for cache lifetime
  • Max Size: Must be positive, can be changed after creation
  • Keys: Must match LEGAL_KEY_PATTERN regex exactly