Skip to content

KMP: unify initialize() across platforms via auto-captured Android Context, with declarative configuration (WorkManager-style) #1689

Closed

Description

@santimattius

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 Application Context 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.

Thanks again for your time.

Activity

  1. github-actions commented on Sep 12, 2026

    @github-actions

    👋 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.)

  2. lingol commented on Sep 13, 2026

    @lingol
    Collaborator

    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.

  3. santimattius commented on Sep 13, 2026

    @santimattius
    ContributorAuthor

    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?

  4. lingol commented on Sep 13, 2026

    @lingol
    Collaborator

    Well done. I would love to see it happen.

  5. added a commit that references this issue on Sep 14, 2026
    f16572b
  6. lingol commented on Oct 16, 2026

    @lingol
    Collaborator

    Released with v2.4.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions