High-performance mobile key-value storage framework with memory mapping, encryption, and multi-process support for Android applications.
—
Built-in encryption capabilities with key management, supporting AES encryption for secure data storage and key transformation operations.
Manage encryption keys for secure data storage with up to 16-byte keys.
/**
* Get the current encryption key.
* @return The encryption key string, or null if not encrypted
*/
public String cryptKey();
/**
* Transform plain text into encrypted text, or vice versa.
* Can also change existing encryption key with a different key.
* @param cryptKey The new encryption key (no more than 16 bytes), null to remove encryption
* @return True if successful, false otherwise
*/
public boolean reKey(String cryptKey);
/**
* Reset the encryption key without encrypting or decrypting anything.
* Usually called after another process has reKey() the multi-process MMKV instance.
* @param cryptKey The new encryption key (no more than 16 bytes)
*/
public void checkReSetCryptKey(String cryptKey);Usage Example:
import com.tencent.mmkv.MMKV;
// Create encrypted MMKV instance
MMKV secureKv = MMKV.mmkvWithID("secure_data", MMKV.SINGLE_PROCESS_MODE, "my-secret-key");
// Check current encryption key
String currentKey = secureKv.cryptKey(); // "my-secret-key"
// Store sensitive data
secureKv.encode("password", "user-password");
secureKv.encode("api_key", "secret-api-key");
secureKv.encode("token", "access-token-12345");
// Change encryption key (re-encrypts all data)
boolean success = secureKv.reKey("new-secret-key");
if (success) {
Log.d("MMKV", "Successfully changed encryption key");
} else {
Log.e("MMKV", "Failed to change encryption key");
}
// Remove encryption (converts to plain text)
secureKv.reKey(null);
// Add encryption to existing plain text data
secureKv.reKey("another-secret-key");Handle encryption key changes across multiple processes safely.
/**
* Reset the encryption key without encrypting/decrypting.
* Use this when another process has changed the encryption key.
* @param cryptKey The new encryption key that was set by another process
*/
public void checkReSetCryptKey(String cryptKey);Usage Example:
// In Process A - change encryption key
MMKV multiProcessKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");
boolean changed = multiProcessKv.reKey("new-key");
// In Process B - synchronize the key change
MMKV sharedKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE, "old-key");
if (changed) {
// Reset to the new key without re-encrypting
sharedKv.checkReSetCryptKey("new-key");
// Now Process B can access the data with the new key
}Check if an MMKV instance has encryption enabled.
/**
* Check if encryption is enabled for this instance.
* @return True if encryption is enabled, false otherwise
*/
private boolean isEncryptionEnabled();Usage Example:
MMKV plainKv = MMKV.mmkvWithID("plain_data");
MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "secret");
// Note: isEncryptionEnabled() is private, but you can check via cryptKey()
boolean plainHasEncryption = (plainKv.cryptKey() != null); // false
boolean encryptedHasEncryption = (encryptedKv.cryptKey() != null); // true
Log.d("MMKV", "Plain KV encrypted: " + plainHasEncryption);
Log.d("MMKV", "Encrypted KV encrypted: " + encryptedHasEncryption);Best practices for using MMKV encryption securely.
Key Generation Example:
import java.security.SecureRandom;
import java.util.Base64;
public class MMKVSecurity {
/**
* Generate a secure random encryption key.
* @return A base64-encoded encryption key suitable for MMKV
*/
public static String generateSecureKey() {
SecureRandom random = new SecureRandom();
byte[] keyBytes = new byte[16]; // 16 bytes = 128 bits
random.nextBytes(keyBytes);
return Base64.getEncoder().encodeToString(keyBytes);
}
/**
* Create an encrypted MMKV instance with a secure key.
* @param instanceId The unique ID for the MMKV instance
* @param encryptionKey The encryption key (store securely!)
* @return The encrypted MMKV instance
*/
public static MMKV createSecureMMKV(String instanceId, String encryptionKey) {
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
}
}
// Usage
String secureKey = MMKVSecurity.generateSecureKey();
// Store secureKey in Android Keystore or other secure location
MMKV secureStorage = MMKVSecurity.createSecureMMKV("user_credentials", secureKey);Example of integrating MMKV encryption with Android Keystore for enhanced security.
Usage Example:
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import java.security.KeyStore;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import android.util.Base64;
public class MMKVKeystoreHelper {
private static final String KEYSTORE_ALIAS = "MMKVMasterKey";
private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
/**
* Generate or retrieve a master key from Android Keystore.
* @return The master key for encrypting MMKV keys
*/
public static SecretKey getMasterKey() throws Exception {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);
if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
// Generate new key
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
KEYSTORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
return keyGenerator.generateKey();
} else {
// Retrieve existing key
return (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null);
}
}
/**
* Create an MMKV instance with Keystore-protected encryption.
* @param instanceId The unique ID for the MMKV instance
* @return The encrypted MMKV instance
*/
public static MMKV createKeystoreProtectedMMKV(String instanceId) throws Exception {
// Generate MMKV encryption key
SecureRandom random = new SecureRandom();
byte[] mmkvKey = new byte[16];
random.nextBytes(mmkvKey);
// Encrypt the MMKV key with Keystore key
SecretKey masterKey = getMasterKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, masterKey);
byte[] encryptedKey = cipher.doFinal(mmkvKey);
// Store encrypted key in SharedPreferences or other location
// (In real app, you'd save this encrypted key for later retrieval)
// Create MMKV with the raw key
String mmkvKeyString = Base64.encodeToString(mmkvKey, Base64.NO_WRAP);
return MMKV.mmkvWithID(instanceId, MMKV.SINGLE_PROCESS_MODE, mmkvKeyString);
}
}
// Usage
try {
MMKV keystoreProtectedKv = MMKVKeystoreHelper.createKeystoreProtectedMMKV("secure_user_data");
keystoreProtectedKv.encode("sensitive_info", "highly confidential data");
} catch (Exception e) {
Log.e("MMKV", "Failed to create keystore-protected MMKV", e);
}Encryption impact on performance and optimization strategies.
Usage Example:
public class MMKVPerformanceExample {
public void demonstrateEncryptionPerformance() {
// Plain MMKV - fastest
MMKV plainKv = MMKV.mmkvWithID("plain_data");
// Encrypted MMKV - slower due to encryption overhead
MMKV encryptedKv = MMKV.mmkvWithID("encrypted_data", MMKV.SINGLE_PROCESS_MODE, "key");
// Disable compare-before-set for encrypted instances (it's inefficient)
// encryptedKv.enableCompareBeforeSet(); // Don't do this with encryption
long startTime, endTime;
String testData = "This is test data for performance measurement";
// Measure plain storage performance
startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
plainKv.encode("key_" + i, testData);
}
endTime = System.nanoTime();
long plainTime = endTime - startTime;
// Measure encrypted storage performance
startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
encryptedKv.encode("key_" + i, testData);
}
endTime = System.nanoTime();
long encryptedTime = endTime - startTime;
Log.d("MMKV", String.format("Plain: %d ns, Encrypted: %d ns, Overhead: %.2fx",
plainTime, encryptedTime, (double)encryptedTime / plainTime));
}
}Migrating existing plain text data to encrypted storage.
Usage Example:
public class MMKVEncryptionMigration {
/**
* Migrate existing plain MMKV data to encrypted storage.
* @param instanceId The MMKV instance ID to migrate
* @param encryptionKey The encryption key to use
* @return True if migration successful
*/
public static boolean migrateToEncrypted(String instanceId, String encryptionKey) {
try {
// Get existing plain instance
MMKV plainKv = MMKV.mmkvWithID(instanceId);
// Get all existing keys and values
String[] allKeys = plainKv.allKeys();
if (allKeys == null || allKeys.length == 0) {
// No data to migrate, just enable encryption
return plainKv.reKey(encryptionKey);
}
// Create temporary encrypted instance
String tempId = instanceId + "_temp_encrypted";
MMKV tempEncryptedKv = MMKV.mmkvWithID(tempId, MMKV.SINGLE_PROCESS_MODE, encryptionKey);
// Copy all data to encrypted instance
for (String key : allKeys) {
// Try different data types (you might want to track types separately)
try {
String stringValue = plainKv.decodeString(key);
if (stringValue != null) {
tempEncryptedKv.encode(key, stringValue);
continue;
}
} catch (Exception ignored) {}
try {
int intValue = plainKv.decodeInt(key, Integer.MIN_VALUE);
if (intValue != Integer.MIN_VALUE) {
tempEncryptedKv.encode(key, intValue);
continue;
}
} catch (Exception ignored) {}
// Add other type checks as needed...
// Fallback to bytes
byte[] bytesValue = plainKv.decodeBytes(key);
if (bytesValue != null) {
tempEncryptedKv.encode(key, bytesValue);
}
}
// Replace original with encrypted version
plainKv.clearAll();
long importCount = plainKv.importFrom(tempEncryptedKv);
boolean reKeySuccess = plainKv.reKey(encryptionKey);
// Clean up temporary instance
MMKV.removeStorage(tempId);
return reKeySuccess && importCount > 0;
} catch (Exception e) {
Log.e("MMKV", "Failed to migrate to encrypted storage", e);
return false;
}
}
}
// Usage
boolean migrated = MMKVEncryptionMigration.migrateToEncrypted("user_data", "secret-key");
if (migrated) {
Log.d("MMKV", "Successfully migrated to encrypted storage");
} else {
Log.e("MMKV", "Failed to migrate to encrypted storage");
}checkReSetCryptKey() to synchronize key changes across processesenableCompareBeforeSet() for encrypted instances as it's inefficientreKey() to change encryption keys and re-encrypt existing dataInstall with Tessl CLI
npx tessl i tessl/maven-com-tencent--mmkv