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