0
# Multi-Process Operations
1
2
Safe multi-process access with file locking, content change notifications, and process-aware instance management for applications with multiple processes.
3
4
## Capabilities
5
6
### Process Mode Configuration
7
8
Configure MMKV instances for multi-process access with proper synchronization.
9
10
```java { .api }
11
/**
12
* Check if this instance is in multi-process mode.
13
* @return True if multi-process, false if single-process
14
*/
15
public boolean isMultiProcess();
16
17
/**
18
* Check if this instance is in read-only mode.
19
* @return True if read-only, false if read-write
20
*/
21
public boolean isReadOnly();
22
```
23
24
**Usage Example:**
25
26
```java
27
import com.tencent.mmkv.MMKV;
28
29
// Create multi-process instance
30
MMKV multiProcessKv = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE);
31
32
// Create single-process instance
33
MMKV singleProcessKv = MMKV.mmkvWithID("local_data", MMKV.SINGLE_PROCESS_MODE);
34
35
// Create read-only instance
36
MMKV readOnlyKv = MMKV.mmkvWithID("config_data", MMKV.READ_ONLY_MODE);
37
38
// Check process modes
39
boolean isMulti = multiProcessKv.isMultiProcess(); // true
40
boolean isSingle = singleProcessKv.isMultiProcess(); // false
41
boolean isReadOnly = readOnlyKv.isReadOnly(); // true
42
43
Log.d("MMKV", String.format("Multi: %b, Single: %b, ReadOnly: %b",
44
isMulti, isSingle, isReadOnly));
45
```
46
47
### File Locking
48
49
Exclusive inter-process locking for critical sections that require atomic operations across processes.
50
51
```java { .api }
52
/**
53
* Exclusively inter-process lock the MMKV instance.
54
* Blocks and waits until it successfully locks the file.
55
* No effect if the instance is in SINGLE_PROCESS_MODE.
56
*/
57
public void lock();
58
59
/**
60
* Exclusively inter-process unlock the MMKV instance.
61
* No effect if the instance is in SINGLE_PROCESS_MODE.
62
*/
63
public void unlock();
64
65
/**
66
* Try exclusively inter-process lock the MMKV instance.
67
* Does not block if the file is already locked by another process.
68
* No effect if the instance is in SINGLE_PROCESS_MODE.
69
* @return True if successfully locked, false if already locked
70
*/
71
public boolean tryLock();
72
```
73
74
**Usage Example:**
75
76
```java
77
MMKV sharedKv = MMKV.mmkvWithID("shared_counter", MMKV.MULTI_PROCESS_MODE);
78
79
// Atomic increment operation across processes
80
public void incrementCounter() {
81
sharedKv.lock();
82
try {
83
int currentValue = sharedKv.decodeInt("counter", 0);
84
int newValue = currentValue + 1;
85
sharedKv.encode("counter", newValue);
86
Log.d("MMKV", "Counter incremented to: " + newValue);
87
} finally {
88
sharedKv.unlock();
89
}
90
}
91
92
// Non-blocking lock attempt
93
public boolean tryIncrementCounter() {
94
if (sharedKv.tryLock()) {
95
try {
96
int currentValue = sharedKv.decodeInt("counter", 0);
97
int newValue = currentValue + 1;
98
sharedKv.encode("counter", newValue);
99
Log.d("MMKV", "Counter incremented to: " + newValue);
100
return true;
101
} finally {
102
sharedKv.unlock();
103
}
104
} else {
105
Log.d("MMKV", "Could not acquire lock, counter not incremented");
106
return false;
107
}
108
}
109
```
110
111
### Content Change Notifications
112
113
Monitor changes made by other processes to shared MMKV instances.
114
115
```java { .api }
116
/**
117
* Check inter-process content change manually.
118
* Call this to detect if another process has modified the MMKV instance.
119
*/
120
public void checkContentChangedByOuterProcess();
121
122
/**
123
* Register for MMKV inter-process content change notification.
124
* The notification triggers only when methods are manually called on the MMKV instance.
125
* @param notify The notification handler
126
*/
127
public static void registerContentChangeNotify(MMKVContentChangeNotification notify);
128
129
/**
130
* Unregister for MMKV inter-process content change notification.
131
*/
132
public static void unregisterContentChangeNotify();
133
```
134
135
**Usage Example:**
136
137
```java
138
import com.tencent.mmkv.MMKVContentChangeNotification;
139
140
public class MultiProcessExample {
141
142
private MMKV sharedKv;
143
144
public void setupMultiProcessNotifications() {
145
// Register global content change notification
146
MMKV.registerContentChangeNotify(new MMKVContentChangeNotification() {
147
@Override
148
public void onContentChangedByOuterProcess(String mmapID) {
149
Log.d("MMKV", "Content changed in instance: " + mmapID);
150
// Handle the change - maybe refresh UI or reload data
151
handleContentChange(mmapID);
152
}
153
});
154
155
sharedKv = MMKV.mmkvWithID("shared_settings", MMKV.MULTI_PROCESS_MODE);
156
157
// Start periodic checking for changes
158
startPeriodicChangeCheck();
159
}
160
161
private void startPeriodicChangeCheck() {
162
Handler handler = new Handler(Looper.getMainLooper());
163
Runnable checkRunnable = new Runnable() {
164
@Override
165
public void run() {
166
// Check if content changed in other processes
167
sharedKv.checkContentChangedByOuterProcess();
168
169
// Schedule next check
170
handler.postDelayed(this, 5000); // Check every 5 seconds
171
}
172
};
173
handler.post(checkRunnable);
174
}
175
176
private void handleContentChange(String mmapID) {
177
if ("shared_settings".equals(mmapID)) {
178
// Reload settings that might have changed
179
reloadSharedSettings();
180
}
181
}
182
183
private void reloadSharedSettings() {
184
String theme = sharedKv.decodeString("theme", "default");
185
boolean darkMode = sharedKv.decodeBool("dark_mode", false);
186
// Update UI with new settings
187
updateUI(theme, darkMode);
188
}
189
190
@Override
191
protected void onDestroy() {
192
super.onDestroy();
193
// Unregister notifications to prevent memory leaks
194
MMKV.unregisterContentChangeNotify();
195
}
196
}
197
```
198
199
### Process Mode Validation
200
201
Validate that MMKV instances are used with correct process modes to prevent data corruption.
202
203
```java { .api }
204
/**
205
* Manually enable the process mode checker.
206
* Automatically enabled in DEBUG build, disabled in RELEASE build.
207
* Throws exceptions when instances are created with mismatched process modes.
208
*/
209
public static void enableProcessModeChecker();
210
211
/**
212
* Manually disable the process mode checker.
213
* When enabled, MMKV throws exceptions for process mode mismatches.
214
*/
215
public static void disableProcessModeChecker();
216
```
217
218
**Usage Example:**
219
220
```java
221
public class ProcessModeValidationExample {
222
223
public void setupProcessModeValidation() {
224
if (BuildConfig.DEBUG) {
225
// Enable strict checking in debug builds
226
MMKV.enableProcessModeChecker();
227
}
228
229
try {
230
// This will work fine
231
MMKV singleKv = MMKV.mmkvWithID("single_data", MMKV.SINGLE_PROCESS_MODE);
232
233
// If another process already opened "single_data" with MULTI_PROCESS_MODE,
234
// this will throw IllegalArgumentException in debug mode
235
MMKV singleKv2 = MMKV.mmkvWithID("single_data", MMKV.SINGLE_PROCESS_MODE);
236
237
} catch (IllegalArgumentException e) {
238
Log.e("MMKV", "Process mode mismatch detected", e);
239
// Handle the error - maybe use MULTI_PROCESS_MODE instead
240
MMKV multiKv = MMKV.mmkvWithID("single_data", MMKV.MULTI_PROCESS_MODE);
241
}
242
}
243
}
244
```
245
246
### Anonymous Shared Memory (Ashmem)
247
248
Use anonymous shared memory for inter-process communication without disk persistence.
249
250
```java { .api }
251
/**
252
* Create an MMKV instance based on Anonymous Shared Memory.
253
* Not synced to any disk files - data is lost when all processes exit.
254
* @param context The Android app context
255
* @param mmapID The unique ID of the MMKV instance
256
* @param size The maximum size of Anonymous Shared Memory (cannot grow dynamically)
257
* @param mode The process mode
258
* @param cryptKey The encryption key
259
* @return The ashmem-based MMKV instance
260
*/
261
public static MMKV mmkvWithAshmemID(Context context, String mmapID, int size,
262
int mode, String cryptKey);
263
264
/**
265
* Get the file descriptor of the ashmem MMKV file.
266
* @return The file descriptor
267
*/
268
public int ashmemFD();
269
270
/**
271
* Get the file descriptor of the ashmem MMKV crc file.
272
* @return The meta file descriptor
273
*/
274
public int ashmemMetaFD();
275
276
/**
277
* Create MMKV instance from ashmem file descriptors.
278
* @param mmapID The unique ID
279
* @param fd The ashmem file descriptor
280
* @param metaFD The ashmem meta file descriptor
281
* @param cryptKey The encryption key
282
* @return The MMKV instance
283
*/
284
public static MMKV mmkvWithAshmemFD(String mmapID, int fd, int metaFD, String cryptKey);
285
```
286
287
**Usage Example:**
288
289
```java
290
public class AshmemExample {
291
292
// In main process - create ashmem instance
293
public MMKV createSharedMemoryStorage(Context context) {
294
try {
295
// Create 1MB ashmem instance for inter-process sharing
296
MMKV ashmemKv = MMKV.mmkvWithAshmemID(
297
context,
298
"shared_memory_data",
299
1024 * 1024, // 1MB maximum size
300
MMKV.MULTI_PROCESS_MODE,
301
null // No encryption for simplicity
302
);
303
304
// Store some data
305
ashmemKv.encode("process_id", android.os.Process.myPid());
306
ashmemKv.encode("timestamp", System.currentTimeMillis());
307
308
// Get file descriptors for sharing with other processes
309
int fd = ashmemKv.ashmemFD();
310
int metaFD = ashmemKv.ashmemMetaFD();
311
312
Log.d("MMKV", String.format("Created ashmem MMKV: fd=%d, metaFD=%d", fd, metaFD));
313
314
return ashmemKv;
315
316
} catch (Exception e) {
317
Log.e("MMKV", "Failed to create ashmem MMKV", e);
318
return null;
319
}
320
}
321
322
// In other process - access via ContentProvider (handled automatically)
323
public MMKV getSharedMemoryStorage(Context context) {
324
try {
325
// This automatically uses MMKVContentProvider to get the ashmem instance
326
MMKV sharedKv = MMKV.mmkvWithAshmemID(
327
context,
328
"shared_memory_data",
329
1024 * 1024,
330
MMKV.MULTI_PROCESS_MODE,
331
null
332
);
333
334
// Read data shared from main process
335
int processId = sharedKv.decodeInt("process_id", -1);
336
long timestamp = sharedKv.decodeLong("timestamp", 0);
337
338
Log.d("MMKV", String.format("Shared data: pid=%d, time=%d", processId, timestamp));
339
340
return sharedKv;
341
342
} catch (Exception e) {
343
Log.e("MMKV", "Failed to access shared ashmem MMKV", e);
344
return null;
345
}
346
}
347
}
348
```
349
350
### ContentProvider Integration
351
352
MMKV automatically provides a ContentProvider for ashmem-based inter-process communication.
353
354
```java { .api }
355
/**
356
* Helper ContentProvider for ashmem-based MMKV instances.
357
* Automatically handles file descriptor sharing between processes.
358
*/
359
public class MMKVContentProvider extends ContentProvider {
360
// Constants for ContentProvider communication
361
protected static final String KEY = "KEY";
362
protected static final String KEY_SIZE = "KEY_SIZE";
363
protected static final String KEY_MODE = "KEY_MODE";
364
protected static final String KEY_CRYPT = "KEY_CRYPT";
365
protected static final String FUNCTION_NAME = "mmkvFromAshmemID";
366
367
/**
368
* Get the content URI for this provider.
369
* @param context The Android app context
370
* @return The content URI or null if invalid authority
371
*/
372
protected static Uri contentUri(Context context);
373
374
/**
375
* Get the process name by process ID.
376
* @param context The Android app context
377
* @param pid The process ID
378
* @return The process name or null if not found
379
*/
380
protected static String getProcessNameByPID(Context context, int pid);
381
382
// ContentProvider lifecycle methods
383
@Override
384
public boolean onCreate();
385
386
/**
387
* Handle ashmem MMKV creation calls.
388
* @param method The method name (should be FUNCTION_NAME)
389
* @param mmapID The MMKV instance ID
390
* @param extras Bundle containing size, mode, and crypto key
391
* @return Bundle containing ParcelableMMKV instance
392
*/
393
@Override
394
public Bundle call(String method, String mmapID, Bundle extras);
395
396
@Override
397
public String getType(Uri uri);
398
399
// Unsupported ContentProvider methods (throw UnsupportedOperationException)
400
@Override
401
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
402
403
@Override
404
public Uri insert(Uri uri, ContentValues values);
405
406
@Override
407
public int delete(Uri uri, String selection, String[] selectionArgs);
408
409
@Override
410
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
411
}
412
```
413
414
**AndroidManifest.xml Configuration:**
415
416
```xml
417
<!-- Add to your AndroidManifest.xml for ashmem support -->
418
<provider
419
android:name="com.tencent.mmkv.MMKVContentProvider"
420
android:authorities="${applicationId}.mmkv.provider"
421
android:exported="false" />
422
```
423
424
**Usage Example:**
425
426
```java
427
// The ContentProvider is used automatically when creating ashmem instances
428
// No manual setup required beyond AndroidManifest.xml declaration
429
430
public void demonstrateAutomaticContentProvider(Context context) {
431
// This automatically uses MMKVContentProvider internally
432
MMKV ashmemKv = MMKV.mmkvWithAshmemID(
433
context,
434
"auto_shared_data",
435
512 * 1024, // 512KB
436
MMKV.MULTI_PROCESS_MODE,
437
"shared-key"
438
);
439
440
// The ContentProvider handles the cross-process communication transparently
441
ashmemKv.encode("message", "Hello from " + getProcessName());
442
443
String processName = MMKVContentProvider.getProcessNameByPID(context,
444
android.os.Process.myPid());
445
Log.d("MMKV", "Current process: " + processName);
446
}
447
```
448
449
### Best Practices for Multi-Process Usage
450
451
Guidelines for effective multi-process MMKV usage.
452
453
**Usage Example:**
454
455
```java
456
public class MultiProcessBestPractices {
457
458
/**
459
* Proper multi-process MMKV setup with error handling.
460
*/
461
public static MMKV createSafeMultiProcessMMKV(String instanceId) {
462
try {
463
// Always use MULTI_PROCESS_MODE for shared data
464
MMKV kv = MMKV.mmkvWithID(instanceId, MMKV.MULTI_PROCESS_MODE);
465
466
// Verify it's actually in multi-process mode
467
if (!kv.isMultiProcess()) {
468
Log.w("MMKV", "Expected multi-process mode but got single-process");
469
}
470
471
return kv;
472
473
} catch (RuntimeException e) {
474
Log.e("MMKV", "Failed to create multi-process MMKV: " + instanceId, e);
475
throw e;
476
}
477
}
478
479
/**
480
* Safe atomic operation with proper locking.
481
*/
482
public static void atomicUpdate(MMKV kv, String key, AtomicUpdateFunction updateFunc) {
483
if (!kv.isMultiProcess()) {
484
// No locking needed for single-process
485
updateFunc.update(kv, key);
486
return;
487
}
488
489
kv.lock();
490
try {
491
updateFunc.update(kv, key);
492
} finally {
493
kv.unlock();
494
}
495
}
496
497
/**
498
* Batch operations with single lock for better performance.
499
*/
500
public static void batchUpdate(MMKV kv, BatchOperation operation) {
501
if (kv.isMultiProcess()) {
502
kv.lock();
503
}
504
505
try {
506
operation.execute(kv);
507
} finally {
508
if (kv.isMultiProcess()) {
509
kv.unlock();
510
}
511
}
512
}
513
514
interface AtomicUpdateFunction {
515
void update(MMKV kv, String key);
516
}
517
518
interface BatchOperation {
519
void execute(MMKV kv);
520
}
521
}
522
523
// Usage examples
524
MMKV sharedKv = MultiProcessBestPractices.createSafeMultiProcessMMKV("user_settings");
525
526
// Atomic counter increment
527
MultiProcessBestPractices.atomicUpdate(sharedKv, "counter", (kv, key) -> {
528
int current = kv.decodeInt(key, 0);
529
kv.encode(key, current + 1);
530
});
531
532
// Batch update multiple values
533
MultiProcessBestPractices.batchUpdate(sharedKv, (kv) -> {
534
kv.encode("batch_update_time", System.currentTimeMillis());
535
kv.encode("batch_process_id", android.os.Process.myPid());
536
kv.encode("batch_counter", kv.decodeInt("batch_counter", 0) + 1);
537
});
538
```
539
540
## Types
541
542
```java { .api }
543
public interface MMKVContentChangeNotification {
544
/**
545
* Called when content is changed by another process.
546
* @param mmapID The unique ID of the changed MMKV instance
547
*/
548
void onContentChangedByOuterProcess(String mmapID);
549
}
550
```
551
552
## Constants
553
554
```java { .api }
555
// Process mode constants
556
public static final int SINGLE_PROCESS_MODE = 1 << 0;
557
public static final int MULTI_PROCESS_MODE = 1 << 1;
558
public static final int READ_ONLY_MODE = 1 << 5;
559
```
560
561
## Important Notes
562
563
1. **Process Mode Consistency**: All processes must use the same process mode for a given MMKV instance ID
564
2. **Locking**: Use explicit locking only for atomic operations that span multiple read/write calls
565
3. **Performance**: Multi-process mode has additional overhead compared to single-process mode
566
4. **Ashmem Size**: Anonymous shared memory size cannot be changed after creation - plan appropriately
567
5. **ContentProvider**: Required in AndroidManifest.xml for ashmem functionality
568
6. **Change Notifications**: Require manual triggering via `checkContentChangedByOuterProcess()`
569
7. **Process Mode Checker**: Enable in debug builds to catch configuration errors early