High-performance mobile key-value storage framework with memory mapping, encryption, and multi-process support for Android applications.
—
Advanced data management operations including key inspection, file operations, backup/restore capabilities, and performance optimization methods.
Methods for inspecting and analyzing stored keys and their properties.
/**
* Check whether MMKV contains the key.
* @param key The key to check
* @return True if key exists, false otherwise
*/
public boolean containsKey(String key);
/**
* Get all keys in the MMKV instance.
* @return Array of all keys, or null if no keys exist
*/
public String[] allKeys();
/**
* Get all non-expired keys in the MMKV instance.
* Note: This call has performance costs.
* @return Array of non-expired keys, or null if no keys exist
*/
public String[] allNonExpireKeys();
/**
* Get the total count of all keys.
* @return The total number of keys
*/
public long count();
/**
* Get the total count of all non-expired keys.
* Note: This call has performance costs.
* @return The total number of non-expired keys
*/
public long countNonExpiredKeys();Usage Example:
MMKV kv = MMKV.defaultMMKV();
// Add some test data
kv.encode("user_id", 123);
kv.encode("username", "john_doe");
kv.encode("is_premium", true);
kv.encode("temp_data", "temporary", MMKV.ExpireInMinute);
// Check if specific keys exist
boolean hasUserId = kv.containsKey("user_id"); // true
boolean hasEmail = kv.containsKey("email"); // false
// Get all keys
String[] allKeys = kv.allKeys();
if (allKeys != null) {
Log.d("MMKV", "Total keys: " + allKeys.length);
for (String key : allKeys) {
Log.d("MMKV", "Key: " + key);
}
}
// Get count of keys
long totalCount = kv.count(); // 4
long nonExpiredCount = kv.countNonExpiredKeys(); // 3 (after temp_data expires)
Log.d("MMKV", String.format("Total: %d, Non-expired: %d", totalCount, nonExpiredCount));
// Get only non-expired keys (more expensive operation)
String[] activeKeys = kv.allNonExpireKeys();Analyze the size and memory usage of stored values.
/**
* Get the actual size consumption of the key's value.
* Note: Might be slightly larger than value's length due to metadata.
* @param key The key of the value
* @return The size in bytes
*/
public int getValueSize(String key);
/**
* Get the actual size of the key's value.
* Returns string length or byte array length, etc.
* @param key The key of the value
* @return The actual value size in bytes
*/
public int getValueActualSize(String key);
/**
* Get the size of the underlying file.
* Aligned to disk block size, typically 4K for Android devices.
* @return The total file size in bytes
*/
public long totalSize();
/**
* Get the actual used size of the MMKV instance.
* This size might increase and decrease as MMKV does insertion and full write back.
* @return The actual used size in bytes
*/
public long actualSize();Usage Example:
MMKV kv = MMKV.defaultMMKV();
// Store different types of data
kv.encode("short_string", "Hello");
kv.encode("long_string", "This is a much longer string that will take more space");
kv.encode("large_number", 1234567890L);
byte[] imageData = new byte[1024 * 100]; // 100KB of data
kv.encode("image_data", imageData);
// Analyze individual value sizes
int shortStringSize = kv.getValueSize("short_string"); // ~5 + metadata
int shortStringActual = kv.getValueActualSize("short_string"); // 5
int longStringSize = kv.getValueSize("long_string");
int imageDataSize = kv.getValueSize("image_data"); // ~100KB + metadata
Log.d("MMKV", String.format("Short string: %d bytes (actual: %d)",
shortStringSize, shortStringActual));
Log.d("MMKV", String.format("Long string: %d bytes", longStringSize));
Log.d("MMKV", String.format("Image data: %d bytes", imageDataSize));
// Analyze overall file sizes
long totalFileSize = kv.totalSize(); // File size on disk (aligned to 4K blocks)
long actualUsedSize = kv.actualSize(); // Actual data size used
Log.d("MMKV", String.format("Total file size: %d bytes (%.2f KB)",
totalFileSize, totalFileSize / 1024.0));
Log.d("MMKV", String.format("Actual used size: %d bytes (%.2f KB)",
actualUsedSize, actualUsedSize / 1024.0));
Log.d("MMKV", String.format("Efficiency: %.1f%%",
(actualUsedSize * 100.0) / totalFileSize));Remove individual keys, multiple keys, or clear all data.
/**
* Remove the value for a specific key.
* @param key The key to remove
*/
public void removeValueForKey(String key);
/**
* Batch remove multiple keys from the MMKV instance.
* @param arrKeys Array of keys to be removed
*/
public void removeValuesForKeys(String[] arrKeys);
/**
* Clear all key-values inside the MMKV instance.
* The data file will be trimmed down to pageSize and sync operations called.
* For better performance without file trimming, use clearAllWithKeepingSpace().
*/
public void clearAll();
/**
* Faster clearAll() implementation that keeps the file size.
* The file size is kept as previous for later use.
*/
public void clearAllWithKeepingSpace();Usage Example:
MMKV kv = MMKV.defaultMMKV();
// Add test data
kv.encode("keep_this", "important data");
kv.encode("temp1", "temporary data 1");
kv.encode("temp2", "temporary data 2");
kv.encode("temp3", "temporary data 3");
kv.encode("delete_this", "data to delete");
// Remove single key
kv.removeValueForKey("delete_this");
// Remove multiple keys at once (more efficient than individual removes)
String[] keysToRemove = {"temp1", "temp2", "temp3"};
kv.removeValuesForKeys(keysToRemove);
// Check what's left
String[] remainingKeys = kv.allKeys();
Log.d("MMKV", "Remaining keys: " + Arrays.toString(remainingKeys)); // Should show "keep_this"
// Clear all data (with file trimming for space reclamation)
long sizeBefore = kv.totalSize();
kv.clearAll();
long sizeAfter = kv.totalSize();
Log.d("MMKV", String.format("Size before clear: %d, after: %d", sizeBefore, sizeAfter));
// Alternative: clear all but keep file size for performance
kv.encode("test", "data");
kv.clearAllWithKeepingSpace(); // Faster, but doesn't reclaim disk spaceOperations for maintaining and optimizing MMKV file storage.
/**
* Reduce file size after lots of deletions.
* The totalSize() of an MMKV instance won't reduce after deleting key-values.
* Call this method after lots of deleting if you care about disk usage.
* Note: clearAll() has a similar effect.
*/
public void trim();
/**
* Save all mmap memory to file synchronously.
* You don't need to call this normally - MMKV handles persistence automatically.
* Use only if you worry about device running out of battery.
*/
public void sync();
/**
* Save all mmap memory to file asynchronously.
* No need to call this unless you worry about losing battery power.
*/
public void async();
/**
* Clear memory cache of the MMKV instance.
* Call this on memory warning to free up RAM.
* Any subsequent call will trigger key-values loading from file again.
*/
public void clearMemoryCache();
/**
* Call this method if the MMKV instance is no longer needed.
* Any subsequent call to MMKV instances with the same ID is undefined behavior.
*/
public void close();Usage Example:
public class MMKVMaintenanceExample {
private MMKV kv;
public void demonstrateFileMaintenance() {
kv = MMKV.mmkvWithID("maintenance_example");
// Add lots of data
for (int i = 0; i < 10000; i++) {
kv.encode("key_" + i, "value_" + i);
}
long sizeBefore = kv.totalSize();
Log.d("MMKV", "Size before deletions: " + sizeBefore);
// Delete most of the data
for (int i = 0; i < 9000; i++) {
kv.removeValueForKey("key_" + i);
}
long sizeAfterDeletion = kv.totalSize();
Log.d("MMKV", "Size after deletions: " + sizeAfterDeletion); // Still large
// Trim file to reclaim space
kv.trim();
long sizeAfterTrim = kv.totalSize();
Log.d("MMKV", "Size after trim: " + sizeAfterTrim); // Much smaller
// Force synchronous save to disk
kv.sync();
// Or use asynchronous save for better performance
kv.async();
}
public void handleMemoryPressure() {
// Clear memory cache to free RAM
kv.clearMemoryCache();
Log.d("MMKV", "Memory cache cleared due to memory pressure");
}
@Override
protected void onDestroy() {
super.onDestroy();
// Close MMKV instance when no longer needed
if (kv != null) {
kv.close();
kv = null;
}
}
}Import data from other sources and export for backup purposes.
/**
* Import all key-value items from another MMKV instance.
* @param src The source MMKV instance to import from
* @return Count of items imported
*/
public long importFrom(MMKV src);
/**
* Atomically migrate all key-values from an existing SharedPreferences.
* @param preferences The SharedPreferences to import from
* @return The total count of key-values imported
*/
public int importFromSharedPreferences(SharedPreferences preferences);Usage Example:
public class MMKVImportExportExample {
/**
* Migrate from SharedPreferences to MMKV.
*/
public void migrateFromSharedPreferences(Context context) {
// Get existing SharedPreferences
SharedPreferences oldPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
// Create new MMKV instance
MMKV newKv = MMKV.mmkvWithID("user_prefs");
// Import all data from SharedPreferences
int importedCount = newKv.importFromSharedPreferences(oldPrefs);
Log.d("MMKV", "Imported " + importedCount + " items from SharedPreferences");
// Verify the migration
if (importedCount > 0) {
// Clear old SharedPreferences after successful migration
oldPrefs.edit().clear().apply();
Log.d("MMKV", "Successfully migrated to MMKV");
}
}
/**
* Copy data between MMKV instances.
*/
public void copyDataBetweenInstances() {
// Source instance with data
MMKV sourceKv = MMKV.mmkvWithID("source_data");
sourceKv.encode("user_id", 123);
sourceKv.encode("username", "john");
sourceKv.encode("settings", "dark_mode=true");
// Destination instance
MMKV destKv = MMKV.mmkvWithID("backup_data");
// Import all data from source to destination
long importedCount = destKv.importFrom(sourceKv);
Log.d("MMKV", "Imported " + importedCount + " items to backup");
// Verify the copy
String username = destKv.decodeString("username"); // "john"
int userId = destKv.decodeInt("user_id"); // 123
Log.d("MMKV", String.format("Verified backup: user=%s, id=%d", username, userId));
}
/**
* Create incremental backup.
*/
public void createIncrementalBackup() {
MMKV mainKv = MMKV.defaultMMKV();
MMKV backupKv = MMKV.mmkvWithID("incremental_backup");
// Get timestamp of last backup
long lastBackupTime = backupKv.decodeLong("last_backup_time", 0);
// Import all current data (MMKV handles duplicates efficiently)
long importedCount = backupKv.importFrom(mainKv);
// Update backup timestamp
backupKv.encode("last_backup_time", System.currentTimeMillis());
Log.d("MMKV", String.format("Incremental backup: %d items, last backup: %d",
importedCount, lastBackupTime));
}
}File-level backup and restore operations for data protection.
/**
* Backup one MMKV instance to destination directory.
* @param mmapID The MMKV ID to backup
* @param dstDir The backup destination directory
* @param rootPath The customize root path of the MMKV, null for default root
* @return True if backup successful
*/
public static boolean backupOneToDirectory(String mmapID, String dstDir, String rootPath);
/**
* Restore one MMKV instance from source directory.
* @param mmapID The MMKV ID to restore
* @param srcDir The restore source directory
* @param rootPath The customize root path of the MMKV, null for default root
* @return True if restore successful
*/
public static boolean restoreOneMMKVFromDirectory(String mmapID, String srcDir, String rootPath);
/**
* Backup all MMKV instances from default root dir to destination directory.
* @param dstDir The backup destination directory
* @return Count of MMKV instances successfully backed up
*/
public static long backupAllToDirectory(String dstDir);
/**
* Restore all MMKV instances from source directory to default root dir.
* @param srcDir The restore source directory
* @return Count of MMKV instances successfully restored
*/
public static long restoreAllFromDirectory(String srcDir);Usage Example:
public class MMKVBackupRestoreExample {
/**
* Create backup of specific MMKV instances.
*/
public void backupSpecificInstances(Context context) {
// Create backup directory
File backupDir = new File(context.getExternalFilesDir(null), "mmkv_backup");
if (!backupDir.exists()) {
backupDir.mkdirs();
}
String backupPath = backupDir.getAbsolutePath();
// Backup specific instances
boolean userBackup = MMKV.backupOneToDirectory("user_data", backupPath, null);
boolean settingsBackup = MMKV.backupOneToDirectory("app_settings", backupPath, null);
Log.d("MMKV", String.format("Backup results - User: %b, Settings: %b",
userBackup, settingsBackup));
if (userBackup && settingsBackup) {
// Store backup metadata
MMKV backupMeta = MMKV.mmkvWithID("backup_metadata");
backupMeta.encode("last_backup_time", System.currentTimeMillis());
backupMeta.encode("backup_path", backupPath);
backupMeta.encode("backup_count", 2);
}
}
/**
* Backup all MMKV instances.
*/
public void backupAllInstances(Context context) {
File backupDir = new File(context.getExternalFilesDir(null), "mmkv_full_backup");
backupDir.mkdirs();
// Backup all instances
long backedUpCount = MMKV.backupAllToDirectory(backupDir.getAbsolutePath());
Log.d("MMKV", "Backed up " + backedUpCount + " MMKV instances");
// Create backup manifest
MMKV manifest = MMKV.mmkvWithID("backup_manifest");
manifest.encode("backup_timestamp", System.currentTimeMillis());
manifest.encode("backup_count", backedUpCount);
manifest.encode("backup_type", "full");
}
/**
* Restore from backup.
*/
public void restoreFromBackup(String backupPath) {
try {
// Restore specific instances
boolean userRestored = MMKV.restoreOneMMKVFromDirectory("user_data", backupPath, null);
boolean settingsRestored = MMKV.restoreOneMMKVFromDirectory("app_settings", backupPath, null);
if (userRestored && settingsRestored) {
Log.d("MMKV", "Successfully restored specific instances");
}
// Or restore all instances
long restoredCount = MMKV.restoreAllFromDirectory(backupPath);
Log.d("MMKV", "Restored " + restoredCount + " instances from backup");
} catch (Exception e) {
Log.e("MMKV", "Failed to restore from backup", e);
}
}
/**
* Automated backup with rotation.
*/
public void automatedBackupWithRotation(Context context) {
File backupRoot = new File(context.getExternalFilesDir(null), "mmkv_backups");
backupRoot.mkdirs();
// Create timestamped backup directory
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
.format(new Date());
File currentBackup = new File(backupRoot, "backup_" + timestamp);
currentBackup.mkdirs();
// Perform backup
long backedUpCount = MMKV.backupAllToDirectory(currentBackup.getAbsolutePath());
if (backedUpCount > 0) {
// Keep only last 5 backups
File[] backups = backupRoot.listFiles((dir, name) -> name.startsWith("backup_"));
if (backups != null && backups.length > 5) {
Arrays.sort(backups, (a, b) -> a.getName().compareTo(b.getName()));
// Delete oldest backups
for (int i = 0; i < backups.length - 5; i++) {
deleteRecursive(backups[i]);
Log.d("MMKV", "Deleted old backup: " + backups[i].getName());
}
}
Log.d("MMKV", String.format("Automated backup completed: %d instances, %s",
backedUpCount, timestamp));
}
}
private void deleteRecursive(File file) {
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursive(child);
}
}
}
file.delete();
}
}Methods for managing MMKV storage files and checking storage status.
/**
* Remove the storage of the MMKV, including data file and meta file (.crc).
* Note: The existing instance (if any) will be closed and destroyed.
* @param mmapID The unique ID of the MMKV instance
* @return True if removal successful
*/
public static boolean removeStorage(String mmapID);
/**
* Remove MMKV storage with custom root path.
* @param mmapID The unique ID of the MMKV instance
* @param rootPath The folder of the MMKV instance, null for default
* @return True if removal successful
*/
public static boolean removeStorage(String mmapID, String rootPath);
/**
* Check existence of the MMKV file.
* @param mmapID The unique ID of the MMKV instance
* @return True if file exists
*/
public static boolean checkExist(String mmapID);
/**
* Check existence of MMKV file with custom root path.
* @param mmapID The unique ID of the MMKV instance
* @param rootPath The folder of the MMKV instance, null for default
* @return True if file exists
*/
public static boolean checkExist(String mmapID, String rootPath);
/**
* Check whether the MMKV file is valid.
* Note: Don't use this to check existence - result is undefined on nonexistent files.
* @param mmapID The unique ID of the MMKV instance
* @return True if file is valid
*/
public static boolean isFileValid(String mmapID);
/**
* Check MMKV file validity with custom root path.
* @param mmapID The unique ID of the MMKV instance
* @param rootPath The folder of the MMKV instance, null for default
* @return True if file is valid
*/
public static boolean isFileValid(String mmapID, String rootPath);Usage Example:
public class MMKVStorageManagementExample {
/**
* Clean up unused MMKV instances.
*/
public void cleanupUnusedInstances() {
String[] instancesToCheck = {"temp_cache", "old_user_data", "deprecated_settings"};
for (String instanceId : instancesToCheck) {
if (MMKV.checkExist(instanceId)) {
if (MMKV.isFileValid(instanceId)) {
// File exists and is valid - check if still needed
if (isInstanceStillNeeded(instanceId)) {
Log.d("MMKV", "Keeping instance: " + instanceId);
} else {
// Remove unused instance
boolean removed = MMKV.removeStorage(instanceId);
Log.d("MMKV", "Removed unused instance " + instanceId + ": " + removed);
}
} else {
Log.w("MMKV", "Invalid file detected: " + instanceId);
// Remove corrupted file
MMKV.removeStorage(instanceId);
}
} else {
Log.d("MMKV", "Instance doesn't exist: " + instanceId);
}
}
}
/**
* Health check for all MMKV instances.
*/
public void performHealthCheck() {
// Get all MMKV files in default directory
File mmkvRoot = new File(MMKV.getRootDir());
File[] mmkvFiles = mmkvRoot.listFiles((dir, name) -> !name.endsWith(".crc"));
if (mmkvFiles != null) {
for (File file : mmkvFiles) {
String instanceId = file.getName();
boolean exists = MMKV.checkExist(instanceId);
boolean valid = exists && MMKV.isFileValid(instanceId);
Log.d("MMKV", String.format("Health check - %s: exists=%b, valid=%b",
instanceId, exists, valid));
if (exists && !valid) {
Log.e("MMKV", "Corrupted MMKV file detected: " + instanceId);
// Handle corruption - backup and recreate, or remove
handleCorruptedFile(instanceId);
}
}
}
}
/**
* Get storage statistics.
*/
public void getStorageStats() {
File mmkvRoot = new File(MMKV.getRootDir());
long totalSize = 0;
int fileCount = 0;
File[] files = mmkvRoot.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
totalSize += file.length();
fileCount++;
}
}
}
Log.d("MMKV", String.format("Storage stats - Files: %d, Total size: %d bytes (%.2f KB)",
fileCount, totalSize, totalSize / 1024.0));
// Also check individual instance sizes
String[] activeInstances = {"user_data", "app_settings", "cache"};
for (String instanceId : activeInstances) {
if (MMKV.checkExist(instanceId)) {
MMKV kv = MMKV.mmkvWithID(instanceId);
long instanceSize = kv.totalSize();
long actualSize = kv.actualSize();
Log.d("MMKV", String.format("%s - Total: %d bytes, Used: %d bytes (%.1f%%)",
instanceId, instanceSize, actualSize,
(actualSize * 100.0) / instanceSize));
}
}
}
private boolean isInstanceStillNeeded(String instanceId) {
// Implement your logic to determine if instance is still needed
// For example, check app settings, user preferences, etc.
return !instanceId.startsWith("temp_");
}
private void handleCorruptedFile(String instanceId) {
Log.w("MMKV", "Handling corrupted file: " + instanceId);
// Try to backup what we can
try {
File backupDir = new File(MMKV.getRootDir(), "corrupted_backup");
backupDir.mkdirs();
MMKV.backupOneToDirectory(instanceId, backupDir.getAbsolutePath(), null);
} catch (Exception e) {
Log.e("MMKV", "Failed to backup corrupted file", e);
}
// Remove corrupted file
MMKV.removeStorage(instanceId);
// Optionally recreate with default values
// recreateWithDefaults(instanceId);
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-tencent--mmkv