High-performance mobile key-value storage framework with memory mapping, encryption, and multi-process support for Android applications.
—
Safe multi-process access with file locking, content change notifications, and process-aware instance management for applications with multiple processes.
Configure MMKV instances for multi-process access with proper synchronization.
/**
* Check if this instance is in multi-process mode.
* @return True if multi-process, false if single-process
*/
public boolean isMultiProcess();
/**
* Check if this instance is in read-only mode.
* @return True if read-only, false if read-write
*/
public boolean isReadOnly();Usage Example:
import com.tencent.mmkv.MMKV;
// Create multi-process instance
MMKV multiProcessKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE);
// Create single-process instance
MMKV singleProcessKv = MMKV.mmkvWithID("local_data", MMKV.SINGLE_PROCESS_MODE);
// Create read-only instance
MMKV readOnlyKv = MMKV.mmkvWithID("config_data", MMKV.READ_ONLY_MODE);
// Check process modes
boolean isMulti = multiProcessKv.isMultiProcess(); // true
boolean isSingle = singleProcessKv.isMultiProcess(); // false
boolean isReadOnly = readOnlyKv.isReadOnly(); // true
Log.d("MMKV", String.format("Multi: %b, Single: %b, ReadOnly: %b",
isMulti, isSingle, isReadOnly));Exclusive inter-process locking for critical sections that require atomic operations across processes.
/**
* Exclusively inter-process lock the MMKV instance.
* Blocks and waits until it successfully locks the file.
* No effect if the instance is in SINGLE_PROCESS_MODE.
*/
public void lock();
/**
* Exclusively inter-process unlock the MMKV instance.
* No effect if the instance is in SINGLE_PROCESS_MODE.
*/
public void unlock();
/**
* Try exclusively inter-process lock the MMKV instance.
* Does not block if the file is already locked by another process.
* No effect if the instance is in SINGLE_PROCESS_MODE.
* @return True if successfully locked, false if already locked
*/
public boolean tryLock();Usage Example:
MMKV sharedKv = MMKV.mmkvWithID("shared_counter", MMKV.MULTI_PROCESS_MODE);
// Atomic increment operation across processes
public void incrementCounter() {
sharedKv.lock();
try {
int currentValue = sharedKv.decodeInt("counter", 0);
int newValue = currentValue + 1;
sharedKv.encode("counter", newValue);
Log.d("MMKV", "Counter incremented to: " + newValue);
} finally {
sharedKv.unlock();
}
}
// Non-blocking lock attempt
public boolean tryIncrementCounter() {
if (sharedKv.tryLock()) {
try {
int currentValue = sharedKv.decodeInt("counter", 0);
int newValue = currentValue + 1;
sharedKv.encode("counter", newValue);
Log.d("MMKV", "Counter incremented to: " + newValue);
return true;
} finally {
sharedKv.unlock();
}
} else {
Log.d("MMKV", "Could not acquire lock, counter not incremented");
return false;
}
}Monitor changes made by other processes to shared MMKV instances.
/**
* Check inter-process content change manually.
* Call this to detect if another process has modified the MMKV instance.
*/
public void checkContentChangedByOuterProcess();
/**
* Register for MMKV inter-process content change notification.
* The notification triggers only when methods are manually called on the MMKV instance.
* @param notify The notification handler
*/
public static void registerContentChangeNotify(MMKVContentChangeNotification notify);
/**
* Unregister for MMKV inter-process content change notification.
*/
public static void unregisterContentChangeNotify();Usage Example:
import com.tencent.mmkv.MMKVContentChangeNotification;
public class MultiProcessExample {
private MMKV sharedKv;
public void setupMultiProcessNotifications() {
// Register global content change notification
MMKV.registerContentChangeNotify(new MMKVContentChangeNotification() {
@Override
public void onContentChangedByOuterProcess(String mmapID) {
Log.d("MMKV", "Content changed in instance: " + mmapID);
// Handle the change - maybe refresh UI or reload data
handleContentChange(mmapID);
}
});
sharedKv = MMKV.mmkvWithID("shared_settings", MMKV.MULTI_PROCESS_MODE);
// Start periodic checking for changes
startPeriodicChangeCheck();
}
private void startPeriodicChangeCheck() {
Handler handler = new Handler(Looper.getMainLooper());
Runnable checkRunnable = new Runnable() {
@Override
public void run() {
// Check if content changed in other processes
sharedKv.checkContentChangedByOuterProcess();
// Schedule next check
handler.postDelayed(this, 5000); // Check every 5 seconds
}
};
handler.post(checkRunnable);
}
private void handleContentChange(String mmapID) {
if ("shared_settings".equals(mmapID)) {
// Reload settings that might have changed
reloadSharedSettings();
}
}
private void reloadSharedSettings() {
String theme = sharedKv.decodeString("theme", "default");
boolean darkMode = sharedKv.decodeBool("dark_mode", false);
// Update UI with new settings
updateUI(theme, darkMode);
}
@Override
protected void onDestroy() {
super.onDestroy();
// Unregister notifications to prevent memory leaks
MMKV.unregisterContentChangeNotify();
}
}Validate that MMKV instances are used with correct process modes to prevent data corruption.
/**
* Manually enable the process mode checker.
* Automatically enabled in DEBUG build, disabled in RELEASE build.
* Throws exceptions when instances are created with mismatched process modes.
*/
public static void enableProcessModeChecker();
/**
* Manually disable the process mode checker.
* When enabled, MMKV throws exceptions for process mode mismatches.
*/
public static void disableProcessModeChecker();Usage Example:
public class ProcessModeValidationExample {
public void setupProcessModeValidation() {
if (BuildConfig.DEBUG) {
// Enable strict checking in debug builds
MMKV.enableProcessModeChecker();
}
try {
// This will work fine
MMKV singleKv = MMKV.mmkvWithID("single_data", MMKV.SINGLE_PROCESS_MODE);
// If another process already opened "single_data" with MULTI_PROCESS_MODE,
// this will throw IllegalArgumentException in debug mode
MMKV singleKv2 = MMKV.mmkvWithID("single_data", MMKV.SINGLE_PROCESS_MODE);
} catch (IllegalArgumentException e) {
Log.e("MMKV", "Process mode mismatch detected", e);
// Handle the error - maybe use MULTI_PROCESS_MODE instead
MMKV multiKv = MMKV.mmkvWithID("single_data", MMKV.MULTI_PROCESS_MODE);
}
}
}Use anonymous shared memory for inter-process communication without disk persistence.
/**
* Create an MMKV instance based on Anonymous Shared Memory.
* Not synced to any disk files - data is lost when all processes exit.
* @param context The Android app context
* @param mmapID The unique ID of the MMKV instance
* @param size The maximum size of Anonymous Shared Memory (cannot grow dynamically)
* @param mode The process mode
* @param cryptKey The encryption key
* @return The ashmem-based MMKV instance
*/
public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size,
int mode, String cryptKey);
/**
* Get the file descriptor of the ashmem MMKV file.
* @return The file descriptor
*/
public int ashmemFD();
/**
* Get the file descriptor of the ashmem MMKV crc file.
* @return The meta file descriptor
*/
public int ashmemMetaFD();
/**
* Create MMKV instance from ashmem file descriptors.
* @param mmapID The unique ID
* @param fd The ashmem file descriptor
* @param metaFD The ashmem meta file descriptor
* @param cryptKey The encryption key
* @return The MMKV instance
*/
public static MMKV mmkvWithAshmemFD(String mmapID, int fd, int metaFD, String cryptKey);Usage Example:
public class AshmemExample {
// In main process - create ashmem instance
public MMKV createSharedMemoryStorage(Context context) {
try {
// Create 1MB ashmem instance for inter-process sharing
MMKV ashmemKv = MMKV.mmkvWithAshmemID(
context,
"shared_memory_data",
1024 * 1024, // 1MB maximum size
MMKV.MULTI_PROCESS_MODE,
null // No encryption for simplicity
);
// Store some data
ashmemKv.encode("process_id", android.os.Process.myPid());
ashmemKv.encode("timestamp", System.currentTimeMillis());
// Get file descriptors for sharing with other processes
int fd = ashmemKv.ashmemFD();
int metaFD = ashmemKv.ashmemMetaFD();
Log.d("MMKV", String.format("Created ashmem MMKV: fd=%d, metaFD=%d", fd, metaFD));
return ashmemKv;
} catch (Exception e) {
Log.e("MMKV", "Failed to create ashmem MMKV", e);
return null;
}
}
// In other process - access via ContentProvider (handled automatically)
public MMKV getSharedMemoryStorage(Context context) {
try {
// This automatically uses MMKVContentProvider to get the ashmem instance
MMKV sharedKv = MMKV.mmkvWithAshmemID(
context,
"shared_memory_data",
1024 * 1024,
MMKV.MULTI_PROCESS_MODE,
null
);
// Read data shared from main process
int processId = sharedKv.decodeInt("process_id", -1);
long timestamp = sharedKv.decodeLong("timestamp", 0);
Log.d("MMKV", String.format("Shared data: pid=%d, time=%d", processId, timestamp));
return sharedKv;
} catch (Exception e) {
Log.e("MMKV", "Failed to access shared ashmem MMKV", e);
return null;
}
}
}MMKV automatically provides a ContentProvider for ashmem-based inter-process communication.
/**
* Helper ContentProvider for ashmem-based MMKV instances.
* Automatically handles file descriptor sharing between processes.
*/
public class MMKVContentProvider extends ContentProvider {
// Constants for ContentProvider communication
protected static final String KEY = "KEY";
protected static final String KEY_SIZE = "KEY_SIZE";
protected static final String KEY_MODE = "KEY_MODE";
protected static final String KEY_CRYPT = "KEY_CRYPT";
protected static final String FUNCTION_NAME = "mmkvFromAshmemID";
/**
* Get the content URI for this provider.
* @param context The Android app context
* @return The content URI or null if invalid authority
*/
protected static Uri contentUri(Context context);
/**
* Get the process name by process ID.
* @param context The Android app context
* @param pid The process ID
* @return The process name or null if not found
*/
protected static String getProcessNameByPID(Context context, int pid);
// ContentProvider lifecycle methods
@Override
public boolean onCreate();
/**
* Handle ashmem MMKV creation calls.
* @param method The method name (should be FUNCTION_NAME)
* @param mmapID The MMKV instance ID
* @param extras Bundle containing size, mode, and crypto key
* @return Bundle containing ParcelableMMKV instance
*/
@Override
public Bundle call(String method, String mmapID, Bundle extras);
@Override
public String getType(Uri uri);
// Unsupported ContentProvider methods (throw UnsupportedOperationException)
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
@Override
public Uri insert(Uri uri, ContentValues values);
@Override
public int delete(Uri uri, String selection, String[] selectionArgs);
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
}AndroidManifest.xml Configuration:
<!-- Add to your AndroidManifest.xml for ashmem support -->
<provider
android:name="com.tencent.mmkv.MMKVContentProvider"
android:authorities="${applicationId}.mmkv.provider"
android:exported="false" />Usage Example:
// The ContentProvider is used automatically when creating ashmem instances
// No manual setup required beyond AndroidManifest.xml declaration
public void demonstrateAutomaticContentProvider(Context context) {
// This automatically uses MMKVContentProvider internally
MMKV ashmemKv = MMKV.mmkvWithAshmemID(
context,
"auto_shared_data",
512 * 1024, // 512KB
MMKV.MULTI_PROCESS_MODE,
"shared-key"
);
// The ContentProvider handles the cross-process communication transparently
ashmemKv.encode("message", "Hello from " + getProcessName());
String processName = MMKVContentProvider.getProcessNameByPID(context,
android.os.Process.myPid());
Log.d("MMKV", "Current process: " + processName);
}Guidelines for effective multi-process MMKV usage.
Usage Example:
public class MultiProcessBestPractices {
/**
* Proper multi-process MMKV setup with error handling.
*/
public static MMKV createSafeMultiProcessMMKV(String instanceId) {
try {
// Always use MULTI_PROCESS_MODE for shared data
MMKV kv = MMKV.mmkvWithID(instanceId, MMKV.MULTI_PROCESS_MODE);
// Verify it's actually in multi-process mode
if (!kv.isMultiProcess()) {
Log.w("MMKV", "Expected multi-process mode but got single-process");
}
return kv;
} catch (RuntimeException e) {
Log.e("MMKV", "Failed to create multi-process MMKV: " + instanceId, e);
throw e;
}
}
/**
* Safe atomic operation with proper locking.
*/
public static void atomicUpdate(MMKV kv, String key, AtomicUpdateFunction updateFunc) {
if (!kv.isMultiProcess()) {
// No locking needed for single-process
updateFunc.update(kv, key);
return;
}
kv.lock();
try {
updateFunc.update(kv, key);
} finally {
kv.unlock();
}
}
/**
* Batch operations with single lock for better performance.
*/
public static void batchUpdate(MMKV kv, BatchOperation operation) {
if (kv.isMultiProcess()) {
kv.lock();
}
try {
operation.execute(kv);
} finally {
if (kv.isMultiProcess()) {
kv.unlock();
}
}
}
interface AtomicUpdateFunction {
void update(MMKV kv, String key);
}
interface BatchOperation {
void execute(MMKV kv);
}
}
// Usage examples
MMKV sharedKv = MultiProcessBestPractices.createSafeMultiProcessMMKV("user_settings");
// Atomic counter increment
MultiProcessBestPractices.atomicUpdate(sharedKv, "counter", (kv, key) -> {
int current = kv.decodeInt(key, 0);
kv.encode(key, current + 1);
});
// Batch update multiple values
MultiProcessBestPractices.batchUpdate(sharedKv, (kv) -> {
kv.encode("batch_update_time", System.currentTimeMillis());
kv.encode("batch_process_id", android.os.Process.myPid());
kv.encode("batch_counter", kv.decodeInt("batch_counter", 0) + 1);
});public interface MMKVContentChangeNotification {
/**
* Called when content is changed by another process.
* @param mmapID The unique ID of the changed MMKV instance
*/
void onContentChangedByOuterProcess(String mmapID);
}// Process mode constants
public static final int SINGLE_PROCESS_MODE = 1 << 0;
public static final int MULTI_PROCESS_MODE = 1 << 1;
public static final int READ_ONLY_MODE = 1 << 5;checkContentChangedByOuterProcess()Install with Tessl CLI
npx tessl i tessl/maven-com-tencent--mmkv