Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper in InputMethodService onDestroy() Android 14 (34 Api) #2742

Open
Pretorian1 opened this issue Mar 14, 2025 · 0 comments

Comments

@Pretorian1
Copy link

Pretorian1 commented Mar 14, 2025

I implemented a custom software keyboard using Compose to build the UI part and InputMethodService() for IME function interaction.

It works as expected. However, memory leak WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper appear when changing screen orientation or switching to a different soft keyboard from the implemented one and vice versa. It only happens on Android API 34 on physical devices like Motorola Edge 40 Pro, Motorola Edge 30 Neo and Samsung Galaxy S24. Also, this leak reproduces on Android Emulators: Intel x86_64 Atom System Image API 34, Google Apis Intel x86_64 Atom System Image API 34. I noticed interesting behavior in Intel x86_64 Atom System Image API 35 and Google Apis Intel x86_64 Atom System Image API 35, where LeakCanary found some/the same leaks. But after a few seconds, the message changed to “All retained objects were garbage collected.”

Leaks absent on physical devices: Moto G5 Plus Android 8.1 (27 Api), Moto Hyper One Android 11 (30 Api). And Android Emulators: Intel x86_64 Atom System Image API 33, Google Apis Intel x86_64 Atom System Image API 33, Google Play Intel x86_64 Atom System Image API 33, Google Play Intel x86_64 Atom System Image API 34, Google Play Intel x86_64 Atom System Image API 35.

LeakTrace information

====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS

References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.

299455 bytes retained by leaking objects
Signature: 680efbf9ebebd6f8946131812c75d1798f996c35
┬───
│ GC Root: Global variable in native code
│
├─ android.window.WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper instance
│    Leaking: UNKNOWN
│    Retaining 300.0 kB in 6630 objects
│    WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper is a binder stub. Binder stubs will often be retained
│    long after the associated activity or service is destroyed, as by design stubs are retained until the other side
│    gets GCed. If WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper is not a *static* inner class then that's
│    most likely the root cause of this leak. Make it static. If
│    WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper is an Android Framework class, file a ticket here:
│    https://issuetracker.google.com/issues/new?component=192705
│    ↓ WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper.mCallbackRef
│                                                                 ~~~~~~~~~~~~
├─ android.window.WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper$CallbackRef instance
│    Leaking: UNKNOWN
│    Retaining 299.5 kB in 6629 objects
│    ↓ WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper$CallbackRef.mStrongRef
│                                                                             ~~~~~~~~~~
├─ android.inputmethodservice.InputMethodService$$ExternalSyntheticLambda3 instance
│    Leaking: UNKNOWN
│    Retaining 299.5 kB in 6628 objects
│    f$0 instance of com.example.customsoftkeyboard.service.IMEHexadecimalService
│    ↓ InputMethodService$$ExternalSyntheticLambda3.f$0
│                                                   ~~~
╰→ com.example.customsoftkeyboard.service.IMEHexadecimalService instance
​     Leaking: YES (ObjectWatcher was watching this because com.example.customsoftkeyboard.service.
​     IMEHexadecimalService received Service#onDestroy() callback and Service not held by ActivityThread)
​     Retaining 299.5 kB in 6627 objects
​     key = 81bcf4b0-11cc-407a-b4e5-66622602a44c
​     watchDurationMillis = 8244
​     retainedDurationMillis = 3242
​     mApplication instance of com.example.customsoftkeyboard.CustomSoftKeyboardApplication
​     mBase instance of android.app.ContextImpl
====================================
0 LIBRARY LEAKS

A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
====================================
0 UNREACHABLE OBJECTS

An unreachable object is still in memory but LeakCanary could not find a strong reference path
from GC roots.
====================================
METADATA

Please include this in bug reports and Stack Overflow questions.

Build.VERSION.SDK_INT: 34
Build.MANUFACTURER: unknown
LeakCanary version: 3.0-alpha-8
App process name: com.example.customsoftkeyboard
Class count: 30515
Instance count: 205029
Primitive array count: 147949
Object array count: 28315
Thread count: 22
Heap total bytes: 29012550
Bitmap count: 6
Bitmap total bytes: 31110
Large bitmap count: 0
Large bitmap total bytes: 0
Stats: LruCache[maxSize=3000,hits=106811,misses=137464,hitRate=43%]
RandomAccess[bytes=7127807,reads=137464,travel=54128216187,range=34936098,size=43515825]
Analysis duration: 16250 ms
Heap dump file path: /storage/emulated/0/Download/leakcanary-com.example.customsoftkeyboard/2025-03-13_16-40-15_225.
hprof
Heap dump timestamp: 1741884036842
Heap dump duration: Unknown
====================================

IMEHexadecimalService code:

import android.inputmethodservice.InputMethodService
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.example.customsoftkeyboard.view.ComposeHexadecimalKeyBoardView

class IMEHexadecimalService : InputMethodService(), LifecycleOwner,
    SavedStateRegistryOwner {

    private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)

    override val lifecycle: Lifecycle
        get() = lifecycleRegistry

    private val savedStateRegistryController = SavedStateRegistryController.create(this)

    override val savedStateRegistry: SavedStateRegistry
        get() = savedStateRegistryController.savedStateRegistry

    override fun onCreateInputView(): View {
        window?.window?.decorView?.let { decorView ->
            decorView.setViewTreeLifecycleOwner(this)
            decorView.setViewTreeSavedStateRegistryOwner(this)
        }
        return ComposeHexadecimalKeyBoardView(this)
    }

    override fun onCreate() {
        super.onCreate()
        savedStateRegistryController.performRestore(null)
        handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    }

    private fun handleLifecycleEvent(event: Lifecycle.Event) =
        lifecycleRegistry.handleLifecycleEvent(event)
} 

It seems that this interesting behavior is connected to the Android Framework. There is a similar problem with this leak WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper https://issuetracker.google.com/issues/229007483

@Pretorian1 Pretorian1 changed the title Memory leak WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper in InputMethodService onDestroy() in Android 14 (34 Api) Memory leak WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper in InputMethodService onDestroy() Android 14 (34 Api) Mar 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant