CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-tencent--mmkv

High-performance mobile key-value storage framework with memory mapping, encryption, and multi-process support for Android applications.

Pending
Overview
Eval results
Files

multi-process.mddocs/

Multi-Process Operations

Safe multi-process access with file locking, content change notifications, and process-aware instance management for applications with multiple processes.

Capabilities

Process Mode Configuration

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));

File Locking

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;
    }
}

Content Change Notifications

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();
    }
}

Process Mode Validation

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);
        }
    }
}

Anonymous Shared Memory (Ashmem)

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;
        }
    }
}

ContentProvider Integration

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);
}

Best Practices for Multi-Process Usage

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);
});

Types

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);
}

Constants

// 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;

Important Notes

  1. Process Mode Consistency: All processes must use the same process mode for a given MMKV instance ID
  2. Locking: Use explicit locking only for atomic operations that span multiple read/write calls
  3. Performance: Multi-process mode has additional overhead compared to single-process mode
  4. Ashmem Size: Anonymous shared memory size cannot be changed after creation - plan appropriately
  5. ContentProvider: Required in AndroidManifest.xml for ashmem functionality
  6. Change Notifications: Require manual triggering via checkContentChangedByOuterProcess()
  7. Process Mode Checker: Enable in debug builds to catch configuration errors early

Install with Tessl CLI

npx tessl i tessl/maven-com-tencent--mmkv

docs

data-management.md

data-storage.md

encryption.md

index.md

initialization.md

instance-management.md

multi-process.md

namespace.md

tile.json