DEV Community

HarmonyOS
HarmonyOS

Posted on

Why Global State or Singletons Fail Between UIAbility and ExtensionAbility

Read the original article:Why Global State or Singletons Fail Between UIAbility and ExtensionAbility

Problem Description

Developers often rely on global state or singleton instances (e.g., a UserManager service or a custom DI container) to manage shared application state.

These instances work as expected inside UIAbility, but fail to persist or synchronize when accessed from an ExtensionAbility, resulting in inconsistent behavior, lost data, or unexpected re-initialization.

Background Knowledge

According to the HarmonyOS Process Model (Stage Model):

“For an application, all ExtensionAbility components of the same type run in an independent process, whereas the UIAbility, ServiceExtensionAbility, and DataShareExtensionAbility components run in another independent process.”

This means:

  • A HarmonyOS application is multi-process by default.
  • UIAbility and several extension types share one process.
  • Each ExtensionAbility type (e.g., FormExtensionAbility, InputMethodExtensionAbility) runs in a separate process.

Implication: Global objects, DI containers, and singleton instances initialized in UIAbility are not visible in ExtensionAbility due to process memory isolation.

Troubleshooting Process

  1. Registered a singleton service (e.g., AppThemeManager) using global variables or a DI system in UIAbility.
  2. Attempted to access the singleton from FormExtensionAbility.onAddForm and found the instance missing or re-initialized.
  3. Logged outputs inside both components — results confirmed different instances' initializations.
  4. Added logs to DI container initialization — both UIAbility and FormExtensionAbility were initializing the container independently.
  5. Verified process behavior matches the documentation on extension type isolation.

Analysis Conclusion

Due to the design of HarmonyOS NEXT, where components of different types are isolated into different processes, singleton instances and in-memory global state are not shared across:

  • UIAbility ↔ FormExtensionAbility
  • UIAbility ↔ InputMethodExtensionAbility
  • UIAbility ↔ any other ExtensionAbility of a different type

Each process gets its own runtime, so even if the class definition is shared, the state and memory space are not.

Solution

To ensure consistent data across all abilities:

  • Do not use global variables, static objects, or DI-managed singletons to pass data across ability types.
  • Use Application Data Persistence methods like:
    • Persisting User Preferences: PreferencesUtil or SettingsPreferences
    • Persisting RDB Store Data: DatabaseHelper
    • Persisting KV Store Data: DistributedKVStore for multi-device or background-safe sync
  • If needed, initialize your service or state holder per process, and hydrate it from persistent storage.
// Example: Safe per-process retrieval
let userId = PreferencesUtil.get("userId") ?? "";Copy codeCopy cod
Enter fullscreen mode Exit fullscreen mode

Verification Result

  • After switching from global singleton to preferences:
    • UIAbility and FormExtensionAbility loaded the same user ID.
    • Logs confirmed state consistency and absence of re-initialization.
    • App behavior stabilized across restarts and form regeneration.
  • No more memory leaks or invalid references due to unsynchronized singletons.

Related Documents or Links

Written by Bilal Basboz

Top comments (0)