Milos
Milos

Reputation: 404

Memory Leaking Android

Do you guys have any suggestion what can cause this memory leak that I got from the Leak Canary? Below is the description I am getting from the report.

Steps to reproduce are, In the Settings menu, click on the switch which will perform the theme change, after this action, leaking happening. Below this I will provide the code piece from the SettingsFragmentClass. Any help would be most welcomed guys.

Basically issue is happening when this line is being called. AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)

┬───
│ GC Root: Global variable in native code
│
├─ android.graphics.animation.RenderNodeAnimator instance
│    Leaking: UNKNOWN
│    Retaining 1.1 MB in 16344 objects
│    ↓ RenderNodeAnimator.mTarget
│                         ~~~~~~~
├─ android.graphics.RenderNode instance
│    Leaking: UNKNOWN
│    Retaining 1.1 MB in 16274 objects
│    ↓ RenderNode.mHostView
│                 ~~~~~~~~~
├─ com.google.android.material.switchmaterial.SwitchMaterial instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    Retaining 1.1 MB in 16272 objects
│    View not part of a window view hierarchy
│    View.mAttachInfo is null (view detached)
│    View.mID = R.id.null
│    View.mWindowAttachCount = 1
│    mContext instance of com.flixeron.my_passkeeper.main.MainActivity with
│    mDestroyed = true
│    ↓ View.mContext
╰→ com.flixeron.my_passkeeper.main.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.flixeron.
​     my_passkeeper.main.MainActivity received Activity#onDestroy() callback
​     and Activity#mDestroyed is true)
​     Retaining 251.2 kB in 5956 objects
​     key = 3c074833-b927-4971-b2b5-0a77b9e68bde
​     watchDurationMillis = 5216
​     retainedDurationMillis = 215
​     mApplication instance of com.flixeron.my_passkeeper.main.MainApplication
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper
METADATA
Build.VERSION.SDK_INT: 31
Build.MANUFACTURER: Xiaomi
LeakCanary version: 2.7
App process name: com.flixeron.my_passkeeper
Stats: LruCache[maxSize=3000,hits=6345,misses=185335,hitRate=3%]
RandomAccess[bytes=9694284,reads=185335,travel=115234430872,range=38720254,size=
46574102]
Heap dump reason: 7 retained objects, app is visible
Analysis duration: 15049 ms

Fragment Class

class SettingsFragment : Fragment() {

private val cardViewModel: CardViewModel by viewModel()

private var bindingProp: FragmentSettingsBinding? = null
private val binding get() = bindingProp!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    bindingProp = FragmentSettingsBinding.inflate(inflater, container, false)

    setTextLockTime()
    configureScreenShoots()
    themeSettings()

    // changing dark/light mode
    binding.switchTheme.setOnClickListener {
        if (!PreferenceProvider.darkMode) {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
            PreferenceProvider.darkMode = true
            binding.imageViewTheme.setImageResource(R.drawable.ic_dark_mode)
        } else {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
            PreferenceProvider.darkMode = false
            binding.imageViewTheme.setImageResource(R.drawable.ic_light_mode)
        }
    }

override fun onDestroyView() {
    super.onDestroyView()
    bindingProp = null
  }
}

Xml file

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/switchTheme"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="30dp"
                    app:layout_constraintBottom_toBottomOf="@+id/textViewChangeTheme"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toTopOf="@+id/textViewChangeTheme">

                    <com.google.android.material.switchmaterial.SwitchMaterial
                        android:id="@+id/switchTheme1"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:clickable="false"
                        android:focusable="false"
                        app:layout_constraintBottom_toBottomOf="@+id/switchTheme"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toTopOf="@+id/switchTheme"
                        android:scaleX="1.3"
                        android:scaleY="1.3"/>

                </androidx.constraintlayout.widget.ConstraintLayout>

Updated leak canary

┬───
│ GC Root: System class
│
├─ android.view.inputmethod.InputMethodManager class
│    Leaking: NO (InputMethodManager↓ is not leaking and a class is never
│    leaking)
│    ↓ static InputMethodManager.sInstance
├─ android.view.inputmethod.InputMethodManager instance
│    Leaking: NO (DecorView↓ is not leaking and InputMethodManager is a
│    singleton)
│    ↓ InputMethodManager.mCurRootView
├─ android.view.ViewRootImpl instance
│    Leaking: NO (DecorView↓ is not leaking)
│    mContext instance of com.android.internal.policy.DecorContext, wrapping
│    activity com.flixeron.my_passkeeper.main.MainActivity with mDestroyed =
│    false
│    ViewRootImpl#mView is not null
│    mWindowAttributes.mTitle = "com.flixeron.my_passkeeper/com.flixeron.
│    my_passkeeper.main.MainActivity"
│    mWindowAttributes.type = 1
│    ↓ ViewRootImpl.mView
├─ com.android.internal.policy.DecorView instance
│    Leaking: NO (View attached)
│    View is part of a window view hierarchy
│    View.mAttachInfo is not null (view attached)
│    View.mWindowAttachCount = 1
│    mContext instance of com.android.internal.policy.DecorContext, wrapping
│    activity com.flixeron.my_passkeeper.main.MainActivity with mDestroyed =
│    false
│    ↓ View.mResources
│           ~~~~~~~~~~
├─ dev.b3nedikt.app_locale.AppLocaleResources instance
│    Leaking: UNKNOWN
│    Retaining 1.9 kB in 55 objects
│    context instance of androidx.appcompat.view.ContextThemeWrapper
│    ↓ AppLocaleResources.context
│                         ~~~~~~~
├─ androidx.appcompat.view.ContextThemeWrapper instance
│    Leaking: UNKNOWN
│    Retaining 274.9 kB in 6390 objects
│    mBase instance of io.github.inflationx.viewpump.ViewPumpContextWrapper
│    ContextThemeWrapper does not wrap a known Android context
│    ↓ ContextWrapper.mBase
│                     ~~~~~
├─ io.github.inflationx.viewpump.ViewPumpContextWrapper instance
│    Leaking: UNKNOWN
│    Retaining 270.3 kB in 6242 objects
│    mBase instance of dev.b3nedikt.app_locale.AppLocaleContextWrapper
│    ViewPumpContextWrapper does not wrap a known Android context
│    ↓ ContextWrapper.mBase
│                     ~~~~~
├─ dev.b3nedikt.app_locale.AppLocaleContextWrapper instance
│    Leaking: UNKNOWN
│    Retaining 270.2 kB in 6236 objects
│    mBase instance of android.app.ContextImpl
│    AppLocaleContextWrapper does not wrap a known Android context
│    ↓ ContextWrapper.mBase
│                     ~~~~~
├─ android.app.ContextImpl instance
│    Leaking: YES (ContextImpl.mOuterContext is an instance of com.flixeron.
│    my_passkeeper.main.MainActivity with Activity.mDestroyed true)
│    Retaining 268.1 kB in 6175 objects
│    mAutofillClient instance of com.flixeron.my_passkeeper.main.MainActivity
│    with mDestroyed = true
│    mOuterContext instance of com.flixeron.my_passkeeper.main.MainActivity
│    with mDestroyed = true
│    ↓ ContextImpl.mAutofillClient
╰→ com.flixeron.my_passkeeper.main.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.flixeron.
​     my_passkeeper.main.MainActivity received Activity#onDestroy() callback
​     and Activity#mDestroyed is true)
​     Retaining 264.2 kB in 6091 objects
​     key = 44a30066-31c8-4abb-8660-907035d18457
​     watchDurationMillis = 5229
​     retainedDurationMillis = 224
​     mApplication instance of com.flixeron.my_passkeeper.main.MainApplication
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper
METADATA
Build.VERSION.SDK_INT: 31
Build.MANUFACTURER: Xiaomi
LeakCanary version: 2.7
App process name: com.flixeron.my_passkeeper
Count of retained yet cleared: 1 KeyedWeakReference instances
Stats: LruCache[maxSize=3000,hits=3959,misses=160418,hitRate=2%]
RandomAccess[bytes=8403631,reads=160418,travel=86543130456,range=36387679,size=4
4147138]
Heap dump reason: 6 retained objects, app is visible
Analysis duration: 11869 ms```

Upvotes: 0

Views: 1438

Answers (1)

snachmsm
snachmsm

Reputation: 19253

your leak is strictly related to GUI, which gets restared when user change theme with your option

conider releasing GUI-related objects by releasing binded Views when Fragment gets destroyed (and further/soon will be recreated with new theme)

override fun onDestroyView() {
    bindingProp = null
    super.onDestroyView()
}

Upvotes: 3

Related Questions