Skip to content

[Android] Memory Leak in PermissionsControllerImpl due to Activity reference retention during Configuration Changes #181

@0ximDigital

Description

@0ximDigital

Description

The PermissionsControllerImpl implementaiton in the Moko permissions library (v0.19.1 and v0.20.0) causes memory leaks by retaining references to old Activity instances during configuration changes (such as screen rotation).
This is due to the use of suspendCoroutine with callback closures that capture the ActivityResultLauncher, which in turn holds a reference to the activity context. As a result, the old activity cannot be garbage collected, leading to memory leaks (confirmed with LeakCanary).

Root Cause

  1. suspendCoroutine captures the continuation in a closure.
  2. The closure references the ActivityResultLauncher instance.
  3. The launcher holds a reference to the activity context.
  4. During configuration changes, the activity is recreated, but the old continuation still holds references to the old activity through the launcher.

Proposed Solution

A custom implementation (NoLeakPermissionsController) avoids this leak by:

  • Using kotlinx.coroutines.flow.MutableSharedFlow to emit permission request results instead of suspendCoroutine.
  • Unbinding any existing activity references when binding a new one.
  • Passing the activity as a LifecycleOwner to the ActivityResultLauncher - making sure the launcher is cleared when the activity is destroyed.

Proposed Fix PR : Link

References

Steps to Reproduce

Super simple sample app to reproduce

  1. Use the default PermissionsControllerImpl in an activity.
  2. Request permission.
  3. While the system permission dialog is visible - Rotate the device or trigger a configuration change.
  4. Observe with LeakCanary: the old activity instance is retained.

Expected Behavior

Old activity instances should be garbage collected after configuration changes.

Environment

  • Moko permissions version: 0.19.1 and 0.20.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions