1 package leakcanary
2 
3 import android.annotation.SuppressLint
4 import android.app.Activity
5 import android.app.Application
6 import android.os.Build.VERSION
7 import android.os.Bundle
8 import android.view.View
9 import android.view.ViewGroup
10 import android.widget.FrameLayout
11 import curtains.Curtains
12 import curtains.OnRootViewRemovedListener
13 import leakcanary.internal.friendly.checkMainThread
14 import leakcanary.internal.friendly.isMainThread
15 import leakcanary.internal.friendly.mainHandler
16 import leakcanary.internal.friendly.noOpDelegate
17 import leakcanary.internal.onAndroidXFragmentViewDestroyed
18 import shark.SharkLog
19 
20 /**
21  * @see [AndroidLeakFixes.VIEW_LOCATION_HOLDER].
22  */
23 @SuppressLint("NewApi")
24 object ViewLocationHolderLeakFix {
25 
26   private var groupAndOutChildren: Pair<ViewGroup, ArrayList<View>>? = null
27   private var failedClearing = false
28 
applyFixnull29   internal fun applyFix(application: Application) {
30     if (VERSION.SDK_INT != 28) {
31       return
32     }
33     // Takes care of child windows (e.g. dialogs)
34     Curtains.onRootViewsChangedListeners += OnRootViewRemovedListener {
35       if (isMainThread) {
36         uncheckedClearStaticPool(application)
37       } else {
38         mainHandler.post {
39           uncheckedClearStaticPool(application)
40         }
41       }
42     }
43     application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks
44     by noOpDelegate() {
45 
46       override fun onActivityCreated(
47         activity: Activity,
48         savedInstanceState: Bundle?
49       ) {
50         activity.onAndroidXFragmentViewDestroyed {
51           uncheckedClearStaticPool(application)
52         }
53       }
54     })
55   }
56 
57   /**
58    * Clears the ViewGroup.ViewLocationHolder.sPool static pool.
59    */
clearStaticPoolnull60   fun clearStaticPool(application: Application) {
61     checkMainThread()
62     if (VERSION.SDK_INT != 28) {
63       return
64     }
65     uncheckedClearStaticPool(application)
66   }
67 
uncheckedClearStaticPoolnull68   private fun uncheckedClearStaticPool(application: Application) {
69     if (failedClearing) {
70       return
71     }
72     try {
73       if (groupAndOutChildren == null) {
74         val viewGroup = FrameLayout(application)
75         // ViewLocationHolder.MAX_POOL_SIZE = 32
76         for (i in 0 until 32) {
77           val childView = View(application)
78           viewGroup.addView(childView)
79         }
80         groupAndOutChildren = viewGroup to ArrayList()
81       }
82       val (group, outChildren) = groupAndOutChildren!!
83       group.addChildrenForAccessibility(outChildren)
84     } catch (ignored: Throwable) {
85       SharkLog.d(ignored) {
86         "Failed to clear ViewLocationHolder leak, will not try again."
87       }
88       failedClearing = true
89     }
90   }
91 }
92