or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# DiskLruCache

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: com.jakewharton:disklrucache

7

- **Package Type**: maven

8

- **Language**: Java

9

- **Target**: Java 1.5+, Android compatible

10

- **Installation**: Add to `pom.xml`:

11

```xml

12

<dependency>

13

<groupId>com.jakewharton</groupId>

14

<artifactId>disklrucache</artifactId>

15

<version>2.0.2</version>

16

</dependency>

17

```

18

19

## Core Imports

20

21

```java

22

import com.jakewharton.disklrucache.DiskLruCache;

23

import java.io.File;

24

import java.io.IOException;

25

```

26

27

## Basic Usage

28

29

```java

30

import com.jakewharton.disklrucache.DiskLruCache;

31

import java.io.File;

32

import java.io.IOException;

33

import java.io.InputStream;

34

import java.io.OutputStream;

35

36

// Create or open cache

37

File cacheDir = new File(context.getCacheDir(), "image_cache");

38

int appVersion = 1;

39

int valueCount = 1; // number of values per entry

40

long maxSize = 10 * 1024 * 1024; // 10MB

41

DiskLruCache cache = DiskLruCache.open(cacheDir, appVersion, valueCount, maxSize);

42

43

// Write to cache

44

DiskLruCache.Editor editor = cache.edit("key1");

45

if (editor != null) {

46

OutputStream out = editor.newOutputStream(0);

47

// write data to out

48

out.close();

49

editor.commit();

50

}

51

52

// Read from cache

53

DiskLruCache.Snapshot snapshot = cache.get("key1");

54

if (snapshot != null) {

55

InputStream in = snapshot.getInputStream(0);

56

// read data from in

57

String value = snapshot.getString(0); // or read as string

58

snapshot.close();

59

}

60

61

// Clean up

62

cache.close();

63

```

64

65

## Architecture

66

67

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

68

69

- **Journal File**: Records all cache operations (CLEAN, DIRTY, REMOVE, READ) for crash recovery

70

- **Entry Management**: Uses LinkedHashMap for LRU ordering with automatic eviction

71

- **Atomic Operations**: Edit operations are atomic - entries are either fully committed or aborted

72

- **Background Cleanup**: Automatic size management and journal compaction via background thread

73

- **File Organization**: Each entry stored as separate files in the cache directory

74

75

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

76

77

## Capabilities

78

79

### Cache Creation and Management

80

81

Create, configure and manage the cache instance lifecycle.

82

83

```java { .api }

84

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException;

85

public synchronized long getMaxSize();

86

public synchronized void setMaxSize(long maxSize);

87

public synchronized long size();

88

public File getDirectory();

89

public synchronized boolean isClosed();

90

public synchronized void flush() throws IOException;

91

public synchronized void close() throws IOException;

92

public void delete() throws IOException;

93

```

94

95

### Reading from Cache

96

97

Retrieve cached entries as snapshots providing access to stored values.

98

99

```java { .api }

100

public synchronized Snapshot get(String key) throws IOException;

101

```

102

103

### Writing to Cache

104

105

Create and edit cache entries through atomic editor operations.

106

107

```java { .api }

108

public Editor edit(String key) throws IOException;

109

public synchronized boolean remove(String key) throws IOException;

110

```

111

112

### Snapshot Operations

113

114

Access cached entry values through immutable snapshots.

115

116

```java { .api }

117

public final class Snapshot implements Closeable {

118

public Editor edit() throws IOException;

119

public InputStream getInputStream(int index);

120

public String getString(int index) throws IOException;

121

public long getLength(int index);

122

public void close();

123

}

124

```

125

126

### Editor Operations

127

128

Create and modify cache entries through atomic edit sessions.

129

130

```java { .api }

131

public final class Editor {

132

public InputStream newInputStream(int index) throws IOException;

133

public OutputStream newOutputStream(int index) throws IOException;

134

public String getString(int index) throws IOException;

135

public void set(int index, String value) throws IOException;

136

public void commit() throws IOException;

137

public void abort() throws IOException;

138

public void abortUnlessCommitted();

139

}

140

```

141

142

## Key and Value Constraints

143

144

### Key Requirements

145

146

Cache keys must conform to strict validation rules:

147

148

- **Pattern**: Must match regex `[a-z0-9_-]{1,64}`

149

- **Length**: 1 to 64 characters

150

- **Characters**: Only lowercase letters, digits, underscore, and hyphen

151

- **Case sensitive**: Keys are case-sensitive

152

153

```java

154

// Valid keys

155

cache.edit("user_123");

156

cache.edit("profile-data");

157

cache.edit("temp_file_001");

158

159

// Invalid keys (throw IllegalArgumentException)

160

cache.edit("User_123"); // uppercase letters

161

cache.edit("user/profile"); // forward slash

162

cache.edit("user@domain.com"); // @ symbol

163

cache.edit(""); // empty string

164

```

165

166

### Value Structure

167

168

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

169

170

- **Value Count**: Fixed per cache (set in `open()` call)

171

- **Size Limits**: Each value 0 to `Integer.MAX_VALUE` bytes

172

- **Storage**: Values stored as byte sequences on disk

173

- **Access**: Values accessible as streams or strings

174

- **Indexing**: Values accessed by zero-based index (0 to valueCount-1)

175

176

## Error Handling and Thread Safety

177

178

### Exception Handling

179

180

```java { .api }

181

// Methods that throw IOException

182

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException;

183

public synchronized Snapshot get(String key) throws IOException;

184

public Editor edit(String key) throws IOException;

185

public synchronized boolean remove(String key) throws IOException;

186

public synchronized void flush() throws IOException;

187

public synchronized void close() throws IOException;

188

public void delete() throws IOException;

189

190

// Methods that throw IllegalArgumentException

191

// Thrown for invalid keys or invalid constructor parameters

192

```

193

194

Common error scenarios:

195

- **Invalid Keys**: `IllegalArgumentException` for keys not matching `[a-z0-9_-]{1,64}`

196

- **Closed Cache**: `IllegalStateException` when operating on closed cache

197

- **Concurrent Edits**: `edit()` returns `null` when entry already being edited

198

- **I/O Errors**: `IOException` for filesystem operations

199

- **Missing Entries**: `get()` returns `null` for non-existent entries

200

201

### Thread Safety

202

203

- **Synchronized Operations**: All cache operations are thread-safe

204

- **Concurrent Reads**: Multiple threads can read simultaneously

205

- **Exclusive Editing**: Only one editor per key at a time

206

- **Background Operations**: Automatic cleanup runs on background thread

207

- **Process Exclusivity**: Cache directory must be exclusive to single process

208

209

## Usage Examples

210

211

### Image Caching

212

213

```java

214

// Setup image cache

215

File imageDir = new File(context.getCacheDir(), "images");

216

DiskLruCache imageCache = DiskLruCache.open(imageDir, 1, 1, 50 * 1024 * 1024);

217

218

// Cache image

219

public void cacheImage(String url, byte[] imageData) throws IOException {

220

String key = urlToKey(url);

221

DiskLruCache.Editor editor = imageCache.edit(key);

222

if (editor != null) {

223

OutputStream out = editor.newOutputStream(0);

224

out.write(imageData);

225

out.close();

226

editor.commit();

227

}

228

}

229

230

// Retrieve cached image

231

public byte[] getCachedImage(String url) throws IOException {

232

String key = urlToKey(url);

233

DiskLruCache.Snapshot snapshot = imageCache.get(key);

234

if (snapshot != null) {

235

InputStream in = snapshot.getInputStream(0);

236

byte[] data = readFully(in);

237

snapshot.close();

238

return data;

239

}

240

return null;

241

}

242

243

private String urlToKey(String url) {

244

return url.replaceAll("[^a-z0-9_-]", "_").toLowerCase();

245

}

246

```

247

248

### Multi-Value Entries

249

250

```java

251

// Cache with multiple values per entry (metadata + content)

252

DiskLruCache dataCache = DiskLruCache.open(cacheDir, 1, 2, maxSize);

253

254

// Store data with metadata

255

public void cacheWithMetadata(String key, String metadata, byte[] content) throws IOException {

256

DiskLruCache.Editor editor = dataCache.edit(key);

257

if (editor != null) {

258

// Store metadata as string in index 0

259

editor.set(0, metadata);

260

261

// Store content as bytes in index 1

262

OutputStream out = editor.newOutputStream(1);

263

out.write(content);

264

out.close();

265

266

editor.commit();

267

}

268

}

269

270

// Retrieve data with metadata

271

public class CachedData {

272

public final String metadata;

273

public final byte[] content;

274

275

public CachedData(String metadata, byte[] content) {

276

this.metadata = metadata;

277

this.content = content;

278

}

279

}

280

281

public CachedData getCachedData(String key) throws IOException {

282

DiskLruCache.Snapshot snapshot = dataCache.get(key);

283

if (snapshot != null) {

284

String metadata = snapshot.getString(0);

285

InputStream contentStream = snapshot.getInputStream(1);

286

byte[] content = readFully(contentStream);

287

snapshot.close();

288

return new CachedData(metadata, content);

289

}

290

return null;

291

}

292

```

293

294

### Error Recovery and Cleanup

295

296

```java

297

// Robust cache operations with proper error handling

298

public boolean safeCacheOperation(String key, byte[] data) {

299

DiskLruCache.Editor editor = null;

300

try {

301

editor = cache.edit(key);

302

if (editor == null) {

303

return false; // Entry being edited by another thread

304

}

305

306

OutputStream out = editor.newOutputStream(0);

307

out.write(data);

308

out.close();

309

editor.commit();

310

return true;

311

312

} catch (IOException e) {

313

if (editor != null) {

314

try {

315

editor.abort();

316

} catch (IOException ignored) {

317

}

318

}

319

return false;

320

}

321

}

322

323

// Proper cache shutdown

324

public void shutdownCache() {

325

try {

326

if (cache != null && !cache.isClosed()) {

327

cache.flush(); // Ensure pending writes complete

328

cache.close();

329

}

330

} catch (IOException e) {

331

// Log error but continue shutdown

332

}

333

}

334

```

335

336

## Constants and Validation

337

338

### Public Constants

339

340

```java { .api }

341

public static final String JOURNAL_FILE = "journal";

342

public static final String JOURNAL_FILE_TEMP = "journal.tmp";

343

public static final String JOURNAL_FILE_BACKUP = "journal.bkp";

344

public static final String MAGIC = "libcore.io.DiskLruCache";

345

public static final String VERSION_1 = "1";

346

public static final long ANY_SEQUENCE_NUMBER = -1L;

347

public static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");

348

```

349

350

### Validation Rules

351

352

- **Directory**: Must be writable and exclusive to the cache

353

- **App Version**: Used for cache invalidation when app updates

354

- **Value Count**: Must be positive, fixed for cache lifetime

355

- **Max Size**: Must be positive, can be changed after creation

356

- **Keys**: Must match `LEGAL_KEY_PATTERN` regex exactly