Append encrypted updates so existing compaction handles IV rotation and sync, avoiding a synchronous flush on every update.
Add regression tests. Profiled on iPhone.
Close the cached instance before acquiring the deletion flock, matching the instance-mutex-before-file-lock order used by writers. Retain the independent CRC descriptor and cross-process exclusion during deletion.
Document instance lifetime requirements and add deterministic regression coverage for writer ordering, cached and uncached removal, and cross-process exclusion. The test passes under ASan and UBSan and detects the original lock inversion.
Validate numeric ranges, backing-buffer bounds, and typed-array alignment before forming slices. Preserve zero-copy writes and handle empty buffers safely.
Add portable regression coverage of the production callbacks with N-API and MMKV test doubles; 1,144 checks pass under ASan and UBSan.
- flutter/mmkv/README.md, OpenHarmony/MMKV/README.md: the reKey sample
calls kmmkv.reKey(), but the instance is named mmkv
- OpenHarmony/MMKV/README.md: the TypedArray sample calls
kv.decodeUint8Array() on an undefined kv; use mmkv
- OpenHarmony/MMKV/README.md: AES-258 -> AES-256
- CONTRIBUTING.md: accecpt -> accept, Additionly -> Additionally
importFrom() passes the destination no-copy views of the source's values
(KeyValueHolderCrypt::toMMBuffer() / KeyValueHolder::toMMBuffer()). An
encrypted destination keeps a value that is not stored as an offset with
KeyValueHolderCrypt(MMBuffer &&), which adopted the buffer's pointer without
checking that the buffer owns it, and freed it later on remove, overwrite or
clearAll(): a free of memory inside the source's map node or mmap, or a
double free of the source's heap buffer.
Copy a no-copy buffer instead of adopting it. Add a unit test importing plain
and encrypted stores into AES-128 and AES-256 destinations.
Add an AndroidX App Startup Initializer that only captures the application
Context on Android (never triggers native init), plus a new
MMKV.Companion.initialize(logLevel) overload that uses it. This matches the
explicit-but-Context-free initialize() contract every other MMKV platform
(iOS/Windows/POSIX/HarmonyOS) already has. Existing Context-taking overloads
are unchanged.
Closes#1689
The published com.tencent:mmkv AAR ships native libs for arm64-v8a and
x86_64 only. On an armeabi-v7a / x86 device onAttachedToEngine() throws
UnsatisfiedLinkError from loadLibrary() before `channel` is assigned. The
engine keeps running, but on activity destroy
FlutterEngineConnectionRegistry.removeAll() calls onDetachedFromEngine()
unconditionally -> channel.setMethodCallHandler(null) -> NullPointerException
-> "Unable to destroy activity" -> fatal crash on every app exit for that
whole ABI class.
- Catch the load failure so `channel` is still registered (MMKV method
calls then fail gracefully through the existing FFI path).
- Null-guard onDetachedFromEngine().
A first set after reopening can encode without the expiration suffix and still report success, leaving an unreadable value. Move the existing write locks and load check ahead of expiry-dependent encoding in each setter path, without adding lock acquisitions or scalar buffers. Route encoded vectors through the existing expiration helper so public templates do not need lock internals.
Keep a minimal set-succeeds/read-fails regression separate from the setter-overload coverage. Both explicitly avoid warming the lazy instance before set.
Read the expiration flag before the read-only metadata return. Filter expired entries in memory for count/allKeys instead of using fullWriteback, which refuses read-only stores. Test each API as the first operation on a fresh instance, for plain and encrypted stores, and verify filtering leaves stored entries intact.
A reopened instance starts with expiration disabled in memory. Load metadata before selecting the raw or expiration-aware read path; otherwise the first getter can return an expired value. Move the existing load check rather than adding another one.