or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mddata-management.mdencryption.mdindex.mdinitialization.mdinstance-management.mdmulti-process.mdstorage-operations.md

advanced-features.mddocs/

0

# Advanced Features

1

2

Advanced functionality including key expiration, native buffer operations, Anonymous Shared Memory support, performance optimizations, and specialized use cases for high-performance applications.

3

4

## Capabilities

5

6

### Key Expiration System

7

8

Automatic key expiration functionality for time-based data management.

9

10

```java { .api }

11

/**

12

* Enable auto key expiration. This is an upgrade operation, the file format will change.

13

* And the file won't be accessed correctly by older version (v1.2.16) of MMKV.

14

* NOTICE: enableCompareBeforeSet will be invalid when Expiration is on.

15

* @param expireDurationInSecond The expire duration for all keys, ExpireNever (0) means no default duration

16

* @return true if successfully enabled, false otherwise

17

*/

18

public boolean enableAutoKeyExpire(int expireDurationInSecond);

19

20

/**

21

* Disable auto key expiration. This is a downgrade operation.

22

* @return true if successfully disabled, false otherwise

23

*/

24

public boolean disableAutoKeyExpire();

25

```

26

27

**Usage Example:**

28

29

```java

30

MMKV cacheKv = MMKV.mmkvWithID("cache_data");

31

32

// Enable expiration with default 1-hour expiration for all keys

33

boolean enabled = cacheKv.enableAutoKeyExpire(MMKV.ExpireInHour);

34

if (enabled) {

35

Log.d("MMKV", "Auto-expiration enabled with 1-hour default");

36

37

// Store data with default expiration

38

cacheKv.encode("api_response", responseData);

39

40

// Store with custom expiration (overrides default)

41

cacheKv.encode("temp_token", token, MMKV.ExpireInMinute);

42

43

// Store without expiration (overrides default)

44

cacheKv.encode("permanent_config", config, MMKV.ExpireNever);

45

} else {

46

Log.e("MMKV", "Failed to enable auto-expiration");

47

}

48

49

// Disable expiration when no longer needed

50

boolean disabled = cacheKv.disableAutoKeyExpire();

51

if (disabled) {

52

Log.d("MMKV", "Auto-expiration disabled");

53

}

54

55

// Check expiration status through key counts

56

long totalKeys = cacheKv.count();

57

long activeKeys = cacheKv.countNonExpiredKeys();

58

Log.d("MMKV", String.format("Keys: %d total, %d active, %d expired",

59

totalKeys, activeKeys, totalKeys - activeKeys));

60

```

61

62

### Performance Optimizations

63

64

Advanced performance features for high-throughput applications.

65

66

```java { .api }

67

/**

68

* Enable data compare before set, for better performance.

69

* If data for key seldom changes, use it.

70

* When encryption or expiration is on, compare-before-set will be invalid.

71

*/

72

public void enableCompareBeforeSet();

73

74

/**

75

* Disable data compare before set.

76

* Disabled by default.

77

*/

78

public void disableCompareBeforeSet();

79

```

80

81

**Usage Example:**

82

83

```java

84

MMKV configKv = MMKV.mmkvWithID("app_config");

85

86

// Enable compare-before-set for configuration data that rarely changes

87

try {

88

configKv.enableCompareBeforeSet();

89

Log.d("MMKV", "Compare-before-set optimization enabled");

90

91

// These operations will be optimized if data hasn't changed

92

configKv.encode("theme", "dark");

93

configKv.encode("language", "en");

94

configKv.encode("notifications", true);

95

96

// Subsequent identical writes will be faster

97

configKv.encode("theme", "dark"); // Optimized - no actual write

98

configKv.encode("theme", "light"); // Full write - data changed

99

100

} catch (RuntimeException e) {

101

Log.w("MMKV", "Compare-before-set not available: " + e.getMessage());

102

// Happens when encryption or expiration is enabled

103

}

104

105

// Performance benchmark example

106

private void benchmarkCompareBeforeSet() {

107

MMKV testKv = MMKV.mmkvWithID("benchmark");

108

109

// Test without optimization

110

long startTime = System.currentTimeMillis();

111

for (int i = 0; i < 10000; i++) {

112

testKv.encode("test_key", "same_value");

113

}

114

long timeWithoutOptimization = System.currentTimeMillis() - startTime;

115

116

// Test with optimization

117

testKv.enableCompareBeforeSet();

118

startTime = System.currentTimeMillis();

119

for (int i = 0; i < 10000; i++) {

120

testKv.encode("test_key", "same_value");

121

}

122

long timeWithOptimization = System.currentTimeMillis() - startTime;

123

124

Log.d("MMKV", String.format("Without optimization: %dms, With optimization: %dms",

125

timeWithoutOptimization, timeWithOptimization));

126

}

127

```

128

129

### Native Buffer Operations

130

131

Direct native memory operations for high-performance scenarios and JNI integration.

132

133

```java { .api }

134

/**

135

* Create a native buffer, whose underlying memory can be directly transferred to another JNI method.

136

* Avoiding unnecessary JNI boxing and unboxing. Must be manually destroyed to avoid memory leak.

137

* @param size The size of the underlying memory

138

* @return NativeBuffer instance, or null if allocation failed

139

*/

140

public static NativeBuffer createNativeBuffer(int size);

141

142

/**

143

* Destroy the native buffer. Must be manually called to avoid memory leak.

144

* @param buffer The buffer to destroy

145

*/

146

public static void destroyNativeBuffer(NativeBuffer buffer);

147

148

/**

149

* Write the value of the key to the native buffer.

150

* @param key The key to read

151

* @param buffer The native buffer to write to

152

* @return The size written, or -1 on error

153

*/

154

public int writeValueToNativeBuffer(String key, NativeBuffer buffer);

155

```

156

157

**Usage Example:**

158

159

```java

160

MMKV kv = MMKV.defaultMMKV();

161

kv.encode("large_data", new byte[8192]); // Store 8KB data

162

163

// Create native buffer for direct memory access

164

NativeBuffer buffer = MMKV.createNativeBuffer(10240); // 10KB buffer

165

if (buffer != null) {

166

try {

167

// Write MMKV value directly to native buffer

168

int bytesWritten = kv.writeValueToNativeBuffer("large_data", buffer);

169

170

if (bytesWritten > 0) {

171

Log.d("MMKV", "Wrote " + bytesWritten + " bytes to native buffer");

172

173

// Buffer can now be passed to native JNI methods without copying

174

// processNativeData(buffer.pointer, buffer.size);

175

176

} else {

177

Log.e("MMKV", "Failed to write to native buffer");

178

}

179

180

} finally {

181

// Always destroy buffer to prevent memory leaks

182

MMKV.destroyNativeBuffer(buffer);

183

}

184

} else {

185

Log.e("MMKV", "Failed to create native buffer");

186

}

187

188

// Example JNI integration

189

public class NativeDataProcessor {

190

static {

191

System.loadLibrary("native_processor");

192

}

193

194

// Native method that works directly with MMKV native buffer

195

private native int processData(long pointer, int size);

196

197

public void processMMKVData(MMKV kv, String key) {

198

// Estimate buffer size

199

int valueSize = kv.getValueSize(key);

200

if (valueSize <= 0) return;

201

202

NativeBuffer buffer = MMKV.createNativeBuffer(valueSize + 1024); // Extra space

203

if (buffer != null) {

204

try {

205

int bytesWritten = kv.writeValueToNativeBuffer(key, buffer);

206

if (bytesWritten > 0) {

207

// Process data directly in native code

208

int result = processData(buffer.pointer, bytesWritten);

209

Log.d("MMKV", "Native processing result: " + result);

210

}

211

} finally {

212

MMKV.destroyNativeBuffer(buffer);

213

}

214

}

215

}

216

}

217

```

218

219

### Anonymous Shared Memory (Ashmem) Support

220

221

Advanced inter-process communication using Anonymous Shared Memory for temporary data sharing.

222

223

```java { .api }

224

/**

225

* Create an MMKV instance based on Anonymous Shared Memory, not synced to any disk files.

226

* Anonymous Shared Memory on Android can't grow dynamically, must set appropriate size on creation.

227

* @param context The context of Android App

228

* @param mmapID The unique ID of the MMKV instance

229

* @param size The maximum size of the underlying Anonymous Shared Memory

230

* @param mode The process mode of the MMKV instance

231

* @param cryptKey The encryption key (no more than 16 bytes, nullable)

232

* @return MMKV instance

233

* @throws RuntimeException if there's a runtime error

234

*/

235

public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, String cryptKey);

236

237

/**

238

* Get an ashmem MMKV instance that has been initiated by another process.

239

* @param mmapID The unique ID of the MMKV instance

240

* @param fd The file descriptor of the ashmem of the MMKV file

241

* @param metaFD The file descriptor of the ashmem of the MMKV crc file

242

* @param cryptKey The encryption key (no more than 16 bytes, nullable)

243

* @return MMKV instance

244

* @throws RuntimeException if there's a runtime error

245

*/

246

public static MMKV mmkvWithAshmemFD(String mmapID, int fd, int metaFD, String cryptKey);

247

248

/**

249

* Get the file descriptor of the ashmem of the MMKV file.

250

* @return The file descriptor

251

*/

252

public int ashmemFD();

253

254

/**

255

* Get the file descriptor of the ashmem of the MMKV crc file.

256

* @return The file descriptor

257

*/

258

public int ashmemMetaFD();

259

```

260

261

**Usage Example:**

262

263

```java

264

// Create ashmem instance for high-performance IPC

265

MMKV ashmemKv = MMKV.mmkvWithAshmemID(

266

this,

267

"real_time_data",

268

2 * 1024 * 1024, // 2MB fixed size

269

MMKV.MULTI_PROCESS_MODE,

270

null

271

);

272

273

// Use for real-time data sharing between processes

274

ashmemKv.encode("sensor_data", sensorReadings);

275

ashmemKv.encode("location", currentLocation);

276

ashmemKv.encode("timestamp", System.currentTimeMillis());

277

278

// Get file descriptors for IPC

279

int dataFD = ashmemKv.ashmemFD();

280

int metaFD = ashmemKv.ashmemMetaFD();

281

282

Log.d("MMKV", String.format("Ashmem FDs - data: %d, meta: %d", dataFD, metaFD));

283

284

// Create ParcelableMMKV for Binder IPC

285

ParcelableMMKV parcelable = new ParcelableMMKV(ashmemKv);

286

287

// Example: High-frequency data sharing

288

public class RealTimeDataManager {

289

private MMKV realTimeKv;

290

private Handler updateHandler;

291

292

public void startRealTimeUpdates() {

293

realTimeKv = MMKV.mmkvWithAshmemID(

294

getApplicationContext(),

295

"real_time_stream",

296

1024 * 1024, // 1MB for high-frequency updates

297

MMKV.MULTI_PROCESS_MODE,

298

null

299

);

300

301

updateHandler = new Handler();

302

updateHandler.post(updateRunnable);

303

}

304

305

private Runnable updateRunnable = new Runnable() {

306

@Override

307

public void run() {

308

// High-frequency updates (every 100ms)

309

realTimeKv.encode("frame_data", getCurrentFrameData());

310

realTimeKv.encode("timestamp", System.currentTimeMillis());

311

312

updateHandler.postDelayed(this, 100);

313

}

314

};

315

}

316

```

317

318

### Namespace Management

319

320

Advanced namespace functionality for organizing MMKV instances with custom root directories.

321

322

```java { .api }

323

/**

324

* Create a NameSpace with custom root directory.

325

* @param dir The customize root directory of a NameSpace

326

* @return A NameSpace with custom root dir

327

* @throws RuntimeException if there's a runtime error

328

*/

329

public static NameSpace nameSpace(String dir);

330

331

/**

332

* Get the default NameSpace (identical with the original MMKV with the global root dir).

333

* @return Default NameSpace

334

* @throws RuntimeException if there's a runtime error

335

*/

336

public static NameSpace defaultNameSpace();

337

```

338

339

**Usage Example:**

340

341

```java

342

// Create custom namespaces for different data categories

343

File userDataDir = new File(getFilesDir(), "user_data");

344

File cacheDataDir = new File(getCacheDir(), "mmkv_cache");

345

File secureDataDir = new File(getFilesDir(), "secure");

346

347

NameSpace userNamespace = MMKV.nameSpace(userDataDir.getAbsolutePath());

348

NameSpace cacheNamespace = MMKV.nameSpace(cacheDataDir.getAbsolutePath());

349

NameSpace secureNamespace = MMKV.nameSpace(secureDataDir.getAbsolutePath());

350

351

// Create instances within namespaces

352

MMKV userSettings = userNamespace.mmkvWithID("settings");

353

MMKV userPrefs = userNamespace.mmkvWithID("preferences");

354

355

MMKV imageCache = cacheNamespace.mmkvWithID("images");

356

MMKV apiCache = cacheNamespace.mmkvWithID("api_responses");

357

358

MMKV credentials = secureNamespace.mmkvWithID("auth", MMKV.SINGLE_PROCESS_MODE, "secret_key");

359

360

// Namespace-specific operations

361

boolean backupSuccess = userNamespace.backupOneToDirectory("settings", getBackupDir());

362

boolean validFile = cacheNamespace.isFileValid("images");

363

364

// Different namespaces can have instances with same ID

365

MMKV userConfig = userNamespace.mmkvWithID("config"); // /user_data/config

366

MMKV appConfig = MMKV.defaultNameSpace().mmkvWithID("config"); // /mmkv/config

367

```

368

369

### Error Handling and Recovery

370

371

Advanced error handling with custom recovery strategies.

372

373

```java { .api }

374

/**

375

* Register a handler for MMKV log redirecting, and error handling.

376

* @param handler The callback handler

377

*/

378

public static void registerHandler(MMKVHandler handler);

379

380

/**

381

* Unregister the handler for MMKV.

382

*/

383

public static void unregisterHandler();

384

```

385

386

**Usage Example:**

387

388

```java

389

// Custom error handler with recovery strategies

390

MMKVHandler errorHandler = new MMKVHandler() {

391

@Override

392

public MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID) {

393

Log.w("MMKV", "CRC check failed for: " + mmapID);

394

395

// Different recovery strategies based on data type

396

if (mmapID.startsWith("cache_")) {

397

// Cache data can be discarded

398

return MMKVRecoverStrategic.OnErrorDiscard;

399

} else if (mmapID.equals("user_data")) {

400

// Critical user data should attempt recovery

401

return MMKVRecoverStrategic.OnErrorRecover;

402

} else {

403

// Default strategy

404

return MMKVRecoverStrategic.OnErrorRecover;

405

}

406

}

407

408

@Override

409

public MMKVRecoverStrategic onMMKVFileLengthError(String mmapID) {

410

Log.w("MMKV", "File length error for: " + mmapID);

411

412

// Log error for analytics

413

crashlytics.recordException(new Exception("MMKV file length error: " + mmapID));

414

415

// Attempt recovery for all file length errors

416

return MMKVRecoverStrategic.OnErrorRecover;

417

}

418

419

@Override

420

public boolean wantLogRedirecting() {

421

// Redirect logs in debug builds only

422

return BuildConfig.DEBUG;

423

}

424

425

@Override

426

public void mmkvLog(MMKVLogLevel level, String file, int line, String function, String message) {

427

String logTag = "MMKV-" + level.name();

428

429

switch (level) {

430

case LevelDebug:

431

Log.d(logTag, String.format("%s:%d %s() - %s", file, line, function, message));

432

break;

433

case LevelInfo:

434

Log.i(logTag, message);

435

break;

436

case LevelWarning:

437

Log.w(logTag, message);

438

break;

439

case LevelError:

440

Log.e(logTag, message);

441

// Report errors to crash reporting

442

crashlytics.log(message);

443

break;

444

case LevelNone:

445

break;

446

}

447

}

448

};

449

450

// Register error handler during app initialization

451

MMKV.registerHandler(errorHandler);

452

453

// Unregister when no longer needed

454

@Override

455

protected void onDestroy() {

456

super.onDestroy();

457

MMKV.unregisterHandler();

458

}

459

```

460

461

### System Information and Diagnostics

462

463

Get system information and perform diagnostics on MMKV instances.

464

465

```java { .api }

466

/**

467

* Get the device's memory page size.

468

* @return The device's memory page size in bytes

469

*/

470

public static int pageSize();

471

472

/**

473

* Get the version of MMKV.

474

* @return The version string of MMKV

475

*/

476

public static String version();

477

478

/**

479

* Notify MMKV that App is about to exit. It's totally fine not calling it at all.

480

*/

481

public static void onExit();

482

```

483

484

**Usage Example:**

485

486

```java

487

// System diagnostics and information

488

public class MMKVDiagnostics {

489

490

public void logSystemInfo() {

491

Log.d("MMKV", "MMKV Version: " + MMKV.version());

492

Log.d("MMKV", "System Page Size: " + MMKV.pageSize() + " bytes");

493

Log.d("MMKV", "Root Directory: " + MMKV.getRootDir());

494

}

495

496

public void performHealthCheck() {

497

String[] criticalInstances = {"user_data", "app_settings", "cache"};

498

499

for (String instanceId : criticalInstances) {

500

boolean exists = MMKV.checkExist(instanceId);

501

boolean valid = exists && MMKV.isFileValid(instanceId);

502

503

if (exists && !valid) {

504

Log.e("MMKV", "Corrupted instance detected: " + instanceId);

505

handleCorruption(instanceId);

506

}

507

508

if (exists && valid) {

509

MMKV kv = MMKV.mmkvWithID(instanceId);

510

long totalSize = kv.totalSize();

511

long actualSize = kv.actualSize();

512

long keyCount = kv.count();

513

514

Log.d("MMKV", String.format(

515

"Instance %s: %d keys, %d KB used, %d KB total (%.1f%% utilization)",

516

instanceId, keyCount, actualSize / 1024, totalSize / 1024,

517

100.0 * actualSize / totalSize

518

));

519

}

520

}

521

}

522

523

// App lifecycle integration

524

@Override

525

protected void onDestroy() {

526

super.onDestroy();

527

// Optional cleanup notification

528

MMKV.onExit();

529

}

530

}

531

```

532

533

## Types

534

535

```java { .api }

536

public final class NativeBuffer {

537

public long pointer; // Native memory pointer

538

public int size; // Buffer size in bytes

539

540

public NativeBuffer(long ptr, int length);

541

}

542

543

public final class NameSpace {

544

public String getRootDir();

545

public MMKV mmkvWithID(String mmapID);

546

public MMKV mmkvWithID(String mmapID, int mode);

547

public MMKV mmkvWithID(String mmapID, int mode, long expectedCapacity);

548

public MMKV mmkvWithID(String mmapID, int mode, String cryptKey);

549

public MMKV mmkvWithID(String mmapID, int mode, String cryptKey, long expectedCapacity);

550

public boolean backupOneToDirectory(String mmapID, String dstDir);

551

public boolean restoreOneMMKVFromDirectory(String mmapID, String srcDir);

552

public boolean isFileValid(String mmapID);

553

public boolean removeStorage(String mmapID);

554

public boolean checkExist(String mmapID);

555

}

556

557

public interface MMKVHandler {

558

MMKVRecoverStrategic onMMKVCRCCheckFail(String mmapID);

559

MMKVRecoverStrategic onMMKVFileLengthError(String mmapID);

560

boolean wantLogRedirecting();

561

void mmkvLog(MMKVLogLevel level, String file, int line, String function, String message);

562

}

563

564

public enum MMKVRecoverStrategic {

565

OnErrorDiscard, // Discard corrupted data (default)

566

OnErrorRecover // Attempt to recover corrupted data

567

}

568

```

569

570

## Constants

571

572

```java { .api }

573

// Expiration constants (in seconds)

574

public static final int ExpireNever = 0; // Never expire

575

public static final int ExpireInMinute = 60; // 1 minute

576

public static final int ExpireInHour = 60 * 60; // 1 hour

577

public static final int ExpireInDay = 24 * 60 * 60; // 1 day

578

public static final int ExpireInMonth = 30 * 24 * 60 * 60; // 30 days

579

public static final int ExpireInYear = 365 * 30 * 24 * 60 * 60; // 365 days

580

```