You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
KMP: unify initialize() across platforms via auto-captured Android Context, with declarative configuration (WorkManager-style) #1689
Hi @lingol, first of all thanks for pushing KMP support forward — I've been trying it out and it feels pretty solid for something still experimental.
I was looking into how MMKV gets initialized in the KMP module and noticed something that might be worth discussing before I put any code together.
What I see today
MMKV.Companion.initialize() has a different signature per platform: Android needs a Context, while iOS/Windows/POSIX/HarmonyOS don't need any platform handle at all. Because of that, there's no common initialize() in commonMain today — each platform needs its own startup code.
What I'm thinking
Use AndroidX App Startup to auto-capture the ApplicationContext when the process starts, borrowing the same pattern WorkManager already uses for exactly this problem:
An MMKV Initializer<Context> would run before Application.onCreate(). Right there, it would check whether the Application implements a new interface, something like MMKVConfiguration.Provider (mirroring WorkManager's Configuration.Provider), and if it does, initialize native MMKV with that configuration (rootDir/handler/logLevel); if not, initialize with defaults. All decided synchronously at one single point, nothing deferred.
With this, Android could end up needing zero mandatory calls, even for custom configuration, declared through an interface instead of an imperative call — the same way you already solved it in WorkManager.
What I'd like to validate with you before touching any code
I saw in Core/MMKV.cpp that g_rootDir is only set on the first native call in the process — later calls with a different rootDir are silently ignored. WorkManager has the same underlying problem and solves it by requiring that, if the Application implements Configuration.Provider, the default provider must be manually removed from the manifest (tools:node="remove") so auto-init doesn't win the race against custom config.
Applied to MMKV, that would mean anyone who currently calls initialize(context, customRootDir, handler) by hand in Application.onCreate() would need to, on upgrade, either migrate to the new interface or explicitly disable MMKV's provider in their manifest — otherwise their custom rootDir would silently be ignored.
Does that compatibility cost seem reasonable for a library still marked experimental in KMP, or would you rather the configuration interface only kick in when auto-init is active by default, without touching anyone who already initializes manually? And more generally — is this something you'd be interested in as a contribution? If the direction sounds right to you, I'm happy to open a PR against dev.
New features, bugfixes, updates and other improvements are all handled mostly by @lingol in his free time.
To support @lingol , please consider 💖 sponsoring him on GitHub 💖.
Sponsored issues will be prioritized.
(If you are already a sponsor, thank you! We will get to this shortly.)
All input are welcome. Maybe you can checkout flutter/mmkv_android/android/src/main/java/com/tencent/mmkv/MMKVPlugin.java for a better idea that can handle both situations.
All input are welcome. Maybe you can checkout flutter/mmkv_android/android/src/main/java/com/tencent/mmkv/MMKVPlugin.java for a better idea that can handle both situations.
Thanks for the reference, @lingol. I checked MMKVPlugin.java (the Flutter binding), and it clarified exactly the point I was missing.
The key pattern there is that capturing the Context and calling initialize() are two separate things: onAttachedToEngine automatically saves the Context, but the actual native initialization only happens when Dart explicitly calls initializeMMKV (with its own rootDir/logLevel). It never triggers automatically.
That led me to simplify the original proposal. Instead of an automatic/deferred init upon first actual use (which posed a race condition risk regarding the "first-call-wins" behavior for rootDir in Core/MMKV.cpp), the design would now be:
An AndroidX App Startup Initializer<Context> that only captures and stores the Context—it doesn't call the native initialize().
A new MMKV.Companion.initialize(logLevel = Info) overload that doesn't require a Context; it uses the already-captured Context and remains an explicit call made by the user (just like on iOS/Windows/POSIX/HarmonyOS), but without needing to pass the Context.
The existing initialize(context, ...) / initialize(context, rootDir, ...) overloads remain unchanged for custom configurations.
This eliminates the risk of an automatic init overwriting a custom rootDir and avoids introducing "magic" behavior that differs from other platforms—initialization remains explicit everywhere; the only change is removing the Context argument on Android.
Does this approach seem reasonable to you for moving forward?
Activity
github-actions commentedon Sep 12, 2026
👋 Hello! Thanks for opening an issue.
Note
New features, bugfixes, updates and other improvements are all handled mostly by @lingol in his free time.
To support
@lingol, please consider 💖 sponsoring him on GitHub 💖.Sponsored issues will be prioritized.
(If you are already a sponsor, thank you! We will get to this shortly.)
lingol commentedon Sep 13, 2026
All input are welcome. Maybe you can checkout flutter/mmkv_android/android/src/main/java/com/tencent/mmkv/MMKVPlugin.java for a better idea that can handle both situations.
santimattius commentedon Sep 13, 2026
Thanks for the reference, @lingol. I checked MMKVPlugin.java (the Flutter binding), and it clarified exactly the point I was missing.
The key pattern there is that capturing the Context and calling initialize() are two separate things: onAttachedToEngine automatically saves the Context, but the actual native initialization only happens when Dart explicitly calls initializeMMKV (with its own rootDir/logLevel). It never triggers automatically.
That led me to simplify the original proposal. Instead of an automatic/deferred init upon first actual use (which posed a race condition risk regarding the "first-call-wins" behavior for rootDir in Core/MMKV.cpp), the design would now be:
Initializer<Context>that only captures and stores the Context—it doesn't call the nativeinitialize().MMKV.Companion.initialize(logLevel = Info)overload that doesn't require a Context; it uses the already-captured Context and remains an explicit call made by the user (just like on iOS/Windows/POSIX/HarmonyOS), but without needing to pass the Context.initialize(context, ...)/initialize(context, rootDir, ...)overloads remain unchanged for custom configurations.This eliminates the risk of an automatic init overwriting a custom rootDir and avoids introducing "magic" behavior that differs from other platforms—initialization remains explicit everywhere; the only change is removing the Context argument on Android.
Does this approach seem reasonable to you for moving forward?
lingol commentedon Sep 13, 2026
Well done. I would love to see it happen.
feat(kmp): capture Context via App Startup for zero-argument initiali…
lingol commentedon Oct 16, 2026
Released with v2.4.3.