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
```