本文介绍了了解ViewTreeObserver泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用LeakCanary 1.3.1-SNAPSHOT.我发现与 ViewTreeObserver.OnScrollChangedListener 设置有关的泄漏,并按照以下代码进行修复:

I am using LeakCanary 1.3.1-SNAPSHOT.I found a leak concerning ViewTreeObserver.OnScrollChangedListener setup and I fixed it like in the following code:

  private ViewTreeObserver.OnScrollChangedListener scrollViewChangeListener;

  @Override protected void onFinishInflate() {
      super.onFinishInflate();
      ButterKnife.inject(this);
      scrollViewChangeListener = new ViewTreeObserver.OnScrollChangedListener() {
      @Override public void onScrollChanged() {
        EventDetailsView.this.onScrollChanged(scrollView.getScrollY());
      }
    };
      scrollView.getViewTreeObserver()
        .addOnScrollChangedListener(scrollViewChangeListener);
  }
  @Override public void onDetachedFromWindow() {
      super.onDetachedFromWindow();
      scrollView.getViewTreeObserver().removeOnScrollChangedListener(scrollViewChangeListener);
  }

但是LeakCanary仍然将其报告为泄漏,知道为什么吗?

However LeakCanary still report it as a leak, any idea why?

* com.couchsurfing.mobile.ui.events.detail.EventDetailsScreen$Presenter has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$1.this$0 (anonymous class extends com.android.internal.view.IInputMethodClient$Stub)
* references android.view.inputmethod.InputMethodManager.mCurRootView
* references com.android.internal.policy.impl.PhoneWindow$DecorView.mAttachInfo
* references android.view.View$AttachInfo.mTreeObserver
* references android.view.ViewTreeObserver.mOnScrollChangedListeners
* references android.view.ViewTreeObserver$CopyOnWriteArray.mData
* references java.util.ArrayList.array
* references array java.lang.Object[].[0]
* references com.couchsurfing.mobile.ui.events.detail.EventDetailsView$1.this$0 (anonymous class implements android.view.ViewTreeObserver$OnScrollChangedListener)
* references com.couchsurfing.mobile.ui.events.detail.EventDetailsView.presenter
* leaks com.couchsurfing.mobile.ui.events.detail.EventDetailsScreen$Presenter instance* Reference Key: 69d0a429-ae27-48fc-a8e0-033c920dd07c
* Device: LGE google Nexus 5 hammerhead
* Android Version: 5.1 API: 22 LeakCanary: 1.3.1-SNAPSHOT
* Durations: watch=5032ms, gc=165ms, heap dump=2932ms, analysis=29907ms* Details:
* Instance of android.view.inputmethod.InputMethodManager$1
|   this$0 = android.view.inputmethod.InputMethodManager [id=0x130239c0]
|   mDescriptor = java.lang.String [id=0x6f5e3f38]
|   mObject = -1601862176
|   mOwner = android.view.inputmethod.InputMethodManager$1 [id=0x13112da0]
* Instance of android.view.inputmethod.InputMethodManager
|   static $staticOverhead = byte[] [id=0x6fe25d29;length=240;size=256]
|   static CONTROL_START_INITIAL = 256
|   static CONTROL_WINDOW_FIRST = 4
|   static CONTROL_WINDOW_IS_TEXT_EDITOR = 2
|   static CONTROL_WINDOW_VIEW_HAS_FOCUS = 1
|   static DEBUG = false
|   static DISPATCH_HANDLED = 1
|   static DISPATCH_IN_PROGRESS = -1
|   static DISPATCH_NOT_HANDLED = 0
|   static HIDE_IMPLICIT_ONLY = 1
|   static HIDE_NOT_ALWAYS = 2
|   static INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500
|   static MSG_BIND = 2
|   static MSG_DUMP = 1
|   static MSG_FLUSH_INPUT_EVENT = 7
|   static MSG_SEND_INPUT_EVENT = 5
|   static MSG_SET_ACTIVE = 4
|   static MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9
|   static MSG_TIMEOUT_INPUT_EVENT = 6
|   static MSG_UNBIND = 3
|   static NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1
|   static PENDING_EVENT_COUNTER = java.lang.String [id=0x6f5bb948]
|   static REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0
|   static RESULT_HIDDEN = 3
|   static RESULT_SHOWN = 2
|   static RESULT_UNCHANGED_HIDDEN = 1
|   static RESULT_UNCHANGED_SHOWN = 0
|   static SHOW_FORCED = 2
|   static SHOW_IMPLICIT = 1
|   static TAG = java.lang.String [id=0x6f5a76e0]
|   static sInstance = android.view.inputmethod.InputMethodManager [id=0x130239c0]
|   mActive = true
|   mBindSequence = 1523
|   mClient = android.view.inputmethod.InputMethodManager$1 [id=0x13112da0]
|   mCompletions = null
|   mCurChannel = android.view.InputChannel [id=0x1304a850]
|   mCurId = java.lang.String [id=0x1325dd80]
|   mCurMethod = com.android.internal.view.IInputMethodSession$Stub$Proxy [id=0x1304a840]
|   mCurRootView = com.android.internal.policy.impl.PhoneWindow$DecorView [id=0x12eac000]
|   mCurSender = android.view.inputmethod.InputMethodManager$ImeInputEventSender [id=0x12c72850]
|   mCurrentTextBoxAttribute = android.view.inputmethod.EditorInfo [id=0x133036c0]
|   mCursorAnchorInfo = null
|   mCursorCandEnd = 0
|   mCursorCandStart = 0
|   mCursorRect = android.graphics.Rect [id=0x13112d40]
|   mCursorSelEnd = 0
|   mCursorSelStart = 0
|   mDummyInputConnection = android.view.inputmethod.BaseInputConnection [id=0x13112dc0]
|   mFullscreenMode = false
|   mH = android.view.inputmethod.InputMethodManager$H [id=0x13112de0]
|   mHasBeenInactive = false
|   mIInputContext = android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper [id=0x13113310]
|   mLastSentUserActionNotificationSequenceNumber = -1
|   mMainLooper = android.os.Looper [id=0x12c76be0]
|   mNextServedView = com.couchsurfing.mobile.ui.drawer.DrawerView [id=0x131f8c00]
|   mNextUserActionNotificationSequenceNumber = 1
|   mPendingEventPool = android.util.Pools$SimplePool [id=0x13110fe0]
|   mPendingEvents = android.util.SparseArray [id=0x13112d80]
|   mRequestUpdateCursorAnchorInfoMonitorMode = 0
|   mServedConnecting = false
|   mServedInputConnection = null
|   mServedInputConnectionWrapper = null
|   mServedView = com.couchsurfing.mobile.ui.drawer.DrawerView [id=0x131f8c00]
|   mService = com.android.internal.view.IInputMethodManager$Stub$Proxy [id=0x13110fc0]
|   mTmpCursorRect = android.graphics.Rect [id=0x13112d20]
|   mViewToScreenMatrix = android.graphics.Matrix [id=0x13110fd0]
|   mViewTopLeft = int[] [id=0x13112d60;length=2;size=24]
* Instance of com.android.internal.policy.impl.PhoneWindow$DecorView
|   mActionMode = null
|   mActionModePopup = null
|   mActionModeView = null
|   mBackgroundFallback = com.android.internal.widget.BackgroundFallback [id=0x12fdd8e0]
|   mBackgroundPadding = android.graphics.Rect [id=0x12ffd9a0]
|   mBarEnterExitDuration = 250
|   mChanging = false
|   mDefaultOpacity = -1
|   mDownY = 0
|   mDrawingBounds = android.graphics.Rect [id=0x12ffd980]
|   mFeatureId = -1
|   mFrameOffsets = android.graphics.Rect [id=0x12ffd9e0]
|   mFramePadding = android.graphics.Rect [id=0x12ffd9c0]
|   mHideInterpolator = android.view.animation.PathInterpolator [id=0x12ffdb00]
|   mLastBottomInset = 144
|   mLastHasBottomStableInset = true
|   mLastHasTopStableInset = true
|   mLastRightInset = 0
|   mLastTopInset = 75
|   mLastWindowFlags = -2122252032
|   mMenuBackground = null
|   mNavigationColorViewState = com.android.internal.policy.impl.PhoneWindow$ColorViewState [id=0x12ff2c70]
|   mNavigationGuard = null
|   mRootScrollY = 0
|   mShowActionModePopup = null
|   mShowInterpolator = android.view.animation.PathInterpolator [id=0x12ffda60]
|   mStatusColorViewState = com.android.internal.policy.impl.PhoneWindow$ColorViewState [id=0x12ff2c40]
|   mStatusGuard = null
|   mWatchingForMenu = false
|   this$0 = com.android.internal.policy.impl.PhoneWindow [id=0x12db9e00]
|   mForeground = null
|   mForegroundBoundsChanged = true
|   mForegroundGravity = 119
|   mForegroundInPadding = true
|   mForegroundPaddingBottom = 0
|   mForegroundPaddingLeft = 0
|   mForegroundPaddingRight = 0
|   mForegroundPaddingTop = 0
|   mForegroundTintList = null
|   mForegroundTintMode = null
|   mHasForegroundTint = false
|   mHasForegroundTintMode = false
|   mMatchParentChildren = java.util.ArrayList [id=0x12ffd960]
|   mMeasureAllChildren = false
|   mOverlayBounds = android.graphics.Rect [id=0x12ffd940]
|   mSelfBounds = android.graphics.Rect [id=0x12ffd920]
|   mAnimationListener = null
|   mCachePaint = null
|   mChildAcceptsDrag = false
|   mChildCountWithTransientState = 0
|   mChildTransformation = null
|   mChildren = android.view.View[] [id=0x130064c0;length=12]
|   mChildrenCount = 3
|   mCurrentDrag = null
|   mCurrentDragView = null
|   mDisappearingChildren = null
|   mDragNotifiedChildren = null
|   mFirstHoverTarget = null
|   mFirstTouchTarget = null
|   mFocused = android.widget.LinearLayout [id=0x12eac800]
|   mGroupFlags = 2375763
|   mHoveredSelf = false
|   mInvalidateRegion = null
|   mInvalidationTransformation = null
|   mLastTouchDownIndex = 0
|   mLastTouchDownTime = 137539724
|   mLastTouchDownX = 605.0
|   mLastTouchDownY = 1177.0
|   mLayoutAnimationController = null
|   mLayoutCalledWhileSuppressed = false
|   mLayoutMode = 0
|   mLayoutTransitionListener = android.view.ViewGroup$3 [id=0x12fdd850]
|   mLocalPoint = null
|   mNestedScrollAxes = 0
|   mOnHierarchyChangeListener = null
|   mPersistentDrawingCache = 2
|   mPreSortedChildren = null
|   mSuppressLayout = false
|   mTempPoint = float[] [id=0x12c0a220;length=2;size=24]
|   mTransition = null
|   mTransitioningViews = null
|   mVisibilityChangingChildren = null
|   mAccessibilityCursorPosition = -1
|   mAccessibilityDelegate = null
|   mAccessibilityTraversalAfterId = -1
|   mAccessibilityTraversalBeforeId = -1
|   mAccessibilityViewId = -1
|   mAnimator = null
|   mAttachInfo = android.view.View$AttachInfo [id=0x12c4fcc0]
|   mAttributes = null
|   mBackground = android.graphics.drawable.ColorDrawable [id=0x13014f80]
|   mBackgroundRenderNode = android.view.RenderNode [id=0x12c73740]
|   mBackgroundResource = 0
|   mBackgroundSizeChanged = false
|   mBackgroundTint = null
|   mBottom = 1920
|   mCachingFailed = false
|   mClipBounds = null
|   mContentDescription = null
|   mContext = com.couchsurfing.mobile.ui.MainActivity [id=0x12db9c80]
|   mCurrentAnimation = null
|   mDrawableState = null
|   mDrawingCache = null
|   mDrawingCacheBackgroundColor = 0
|   mFloatingTreeObserver = null
|   mGhostView = null
|   mHasPerformedLongPress = false
|   mID = -1
|   mInputEventConsistencyVerifier = null
|   mKeyedTags = null
|   mLabelForId = -1
|   mLastIsOpaque = true
|   mLayerPaint = null
|   mLayerType = 0
|   mLayoutInsets = null
|   mLayoutParams = android.view.WindowManager$LayoutParams [id=0x12f1f7e0]
|   mLeft = 0
|   mLeftPaddingDefined = true
|   mListenerInfo = android.view.View$ListenerInfo [id=0x13109940]
|   mMatchIdPredicate = null
|   mMatchLabelForPredicate = null
|   mMeasureCache = android.util.LongSparseLongArray [id=0x13400120]
|   mMeasuredHeight = 1920
|   mMeasuredWidth = 1080
|   mMinHeight = 0
|   mMinWidth = 0
|   mNestedScrollingParent = null
|   mNextFocusDownId = -1
|   mNextFocusForwardId = -1
|   mNextFocusLeftId = -1
|   mNextFocusRightId = -1
|   mNextFocusUpId = -1
|   mOldHeightMeasureSpec = 1073743744
|   mOldWidthMeasureSpec = 1073742904
|   mOutlineProvider = android.view.ViewOutlineProvider$1 [id=0x6fcd7240]
|   mOverScrollMode = 1
|   mOverlay = null
|   mPaddingBottom = 0
|   mPaddingLeft = 0
|   mPaddingRight = 0
|   mPaddingTop = 0
|   mParent = android.view.ViewRootImpl [id=0x13313400]
|   mPendingCheckForLongPress = null
|   mPendingCheckForTap = null
|   mPerformClick = null
|   mPrivateFlags = 25201976
|   mPrivateFlags2 = 1611867680
|   mPrivateFlags3 = 4
|   mRecreateDisplayList = false
|   mRenderNode = android.view.RenderNode [id=0x12ffd880]
|   mResources = android.content.res.Resources [id=0x12c078e0]
|   mRight = 1080
|   mRightPaddingDefined = true
|   mScrollCache = null
|   mScrollX = 0
|   mScrollY = 0
|   mSendViewScrolledAccessibilityEvent = null
|   mSendViewStateChangedAccessibilityEvent = null
|   mSendingHoverAccessibilityEvents = false
|   mStateListAnimator = null
|   mSystemUiVisibility = 0
|   mTag = null
|   mTempNestedScrollConsumed = null
|   mTop = 0
|   mTouchDelegate = null
|   mTouchSlop = 24
|   mTransformationInfo = android.view.View$TransformationInfo [id=0x1349e7c0]
|   mTransientStateCount = 0
|   mTransitionName = null
|   mUnscaledDrawingCache = null
|   mUnsetPressedState = null
|   mUserPaddingBottom = 0
|   mUserPaddingEnd = -2147483648
|   mUserPaddingLeft = 0
|   mUserPaddingLeftInitial = 0
|   mUserPaddingRight = 0
|   mUserPaddingRightInitial = 0
|   mUserPaddingStart = -2147483648
|   mVerticalScrollFactor = 0.0
|   mVerticalScrollbarPosition = 0
|   mViewFlags = 402655360
|   mWindowAttachCount = 1
* Instance of android.view.View$AttachInfo
|   mAccessibilityFetchFlags = 0
|   mAccessibilityFocusDrawable = null
|   mAccessibilityWindowId = 2147483647
|   mApplicationScale = 1.0
|   mCanvas = null
|   mContentInsets = android.graphics.Rect [id=0x13364ee0]
|   mDebugLayout = false
|   mDisabledSystemUiVisibility = 0
|   mDisplay = android.view.Display [id=0x12f75b50]
|   mDisplayState = 2
|   mDrawingTime = 137551407
|   mForceReportNewAttributes = false
|   mGivenInternalInsets = android.view.ViewTreeObserver$InternalInsetsInfo [id=0x13364f40]
|   mGlobalSystemUiVisibility = 0
|   mHandler = android.view.ViewRootImpl$ViewRootHandler [id=0x13364d00]
|   mHardwareAccelerated = true
|   mHardwareAccelerationRequested = true
|   mHardwareRenderer = android.view.ThreadedRenderer [id=0x13323dc0]
|   mHasNonEmptyGivenInternalInsets = false
|   mHasSystemUiListeners = true
|   mHasWindowFocus = true
|   mHighContrastText = false
|   mIWindowId = null
|   mIgnoreDirtyState = false
|   mInTouchMode = true
|   mInvalidateChildLocation = int[] [id=0x13370060;length=2;size=24]
|   mKeepScreenOn = false
|   mKeyDispatchState = android.view.KeyEvent$DispatcherState [id=0x13364fc0]
|   mOverscanInsets = android.graphics.Rect [id=0x13364ec0]
|   mOverscanRequested = false
|   mPanelParentWindowToken = null
|   mPendingAnimatingRenderNodes = null
|   mPoint = android.graphics.Point [id=0x133582f0]
|   mRecomputeGlobalAttributes = false
|   mRootCallbacks = android.view.ViewRootImpl [id=0x13313400]
|   mRootView = com.android.internal.policy.impl.PhoneWindow$DecorView [id=0x12eac000]
|   mScalingRequired = false
|   mScrollContainers = java.util.ArrayList [id=0x13364fa0]
|   mSession = android.view.IWindowSession$Stub$Proxy [id=0x13358290]
|   mSetIgnoreDirtyState = true
|   mStableInsets = android.graphics.Rect [id=0x13364f20]
|   mSystemUiVisibility = 0
|   mTempArrayList = java.util.ArrayList [id=0x133701a0]
|   mTmpInvalRect = android.graphics.Rect [id=0x133700c0]
|   mTmpLocation = int[] [id=0x13370080;length=2;size=24]
|   mTmpMatrix = android.graphics.Matrix [id=0x133582d0]
|   mTmpOutline = android.graphics.Outline [id=0x13370180]
|   mTmpRectList = java.util.ArrayList [id=0x13370120]
|   mTmpTransformLocation = float[] [id=0x133700a0;length=2;size=24]
|   mTmpTransformRect = android.graphics.RectF [id=0x133700e0]
|   mTmpTransformRect1 = android.graphics.RectF [id=0x13370100]
|   mTmpTransformation = android.view.animation.Transformation [id=0x13370140]
|   mTransparentLocation = int[] [id=0x13370040;length=2;size=24]
|   mTreeObserver = android.view.ViewTreeObserver [id=0x133656c0]
|   mTurnOffWindowResizeAnim = false
|   mUnbufferedDispatchRequested = false
|   mUse32BitDrawingCache = true
|   mViewRequestingLayout = null
|   mViewRootImpl = android.view.ViewRootImpl [id=0x13313400]
|   mViewScrollChanged = false
|   mViewVisibilityChanged = false
|   mVisibleInsets = android.graphics.Rect [id=0x13364f00]
|   mWindow = android.view.ViewRootImpl$W [id=0x13364e80]
|   mWindowId = null
|   mWindowLeft = 0
|   mWindowToken = android.view.ViewRootImpl$W [id=0x13364e80]
|   mWindowTop = 0
|   mWindowVisibility = 0
* Instance of android.view.ViewTreeObserver
|   mAlive = true
|   mOnComputeInternalInsetsListeners = null
|   mOnDrawListeners = null
|   mOnEnterAnimationCompleteListeners = null
|   mOnGlobalFocusListeners = null
|   mOnGlobalLayoutListeners = android.view.ViewTreeObserver$CopyOnWriteArray [id=0x1315a300]
|   mOnPreDrawListeners = android.view.ViewTreeObserver$CopyOnWriteArray [id=0x13345760]
|   mOnScrollChangedListeners = android.view.ViewTreeObserver$CopyOnWriteArray [id=0x12fd3220]
|   mOnTouchModeChangeListeners = java.util.concurrent.CopyOnWriteArrayList [id=0x133a1420]
|   mOnWindowAttachListeners = null
|   mOnWindowFocusListeners = null
|   mOnWindowShownListeners = null
|   mWindowShown = false
* Instance of android.view.ViewTreeObserver$CopyOnWriteArray
|   mAccess = android.view.ViewTreeObserver$CopyOnWriteArray$Access [id=0x12fa1960]
|   mData = java.util.ArrayList [id=0x12fd3240]
|   mDataCopy = null
|   mStart = false
* Instance of java.util.ArrayList
|   static $staticOverhead = byte[] [id=0x6fcffb29;length=16;size=32]
|   static MIN_CAPACITY_INCREMENT = 12
|   static serialVersionUID = 8683452581122892189
|   array = java.lang.Object[] [id=0x13094a40;length=12]
|   size = 1
|   modCount = 1
* Array of java.lang.Object[]
|   [0] = com.couchsurfing.mobile.ui.events.detail.EventDetailsView$1 [id=0x12fa1950]
|   [1] = null
|   [2] = null
|   [3] = null
|   [4] = null
|   [5] = null
|   [6] = null
|   [7] = null
|   [8] = null
|   [9] = null
|   [10] = null
|   [ 

推荐答案

尝试更改您的代码,该代码将删除侦听器,以便在将View实际上从窗口分离之前运行,如下所示:

Try to change your code that removes the listener to run before the View is actually detached from the window, like this:

@Override public void onDetachedFromWindow() {
    scrollView.getViewTreeObserver().removeOnScrollChangedListener(scrollViewChangeListener);
    super.onDetachedFromWindow();
}

原因是,从窗口分离后, getViewTreeObserver()返回一个不同的实例(浮动树观察器"),因此您不会从同一对象中删除侦听器,您添加了它.

The reason is that after being detached from a window, getViewTreeObserver() returns a different instance (the "floating tree observer"), so you are not gonna remove your listener from the same object where you added it.

更新

由于您正在使用子视图的 ViewTreeObserver ,因此行为稍微复杂一些,一种可能的解决方案是将 OnAttachStateChangeListener 添加到您的 scrollView ,然后从其中添加/删除您的 OnScrollChangedListener .

Since you are using a ViewTreeObserver of a child view, the behavior is slightly more complex and one possible solution would involve adding an OnAttachStateChangeListener to your scrollView and add/remove your OnScrollChangedListener from there.

无论如何,关于发生泄漏的原因: getViewTreeObserver()不会在从窗口中分离 View 后返回相同的实例.调用 removeOnScrollChangedListener()可能没有任何效果,将原始的 OnScrollChangedListener 仍附加到旧的 ViewTreeObserver 上,从而泄漏了 Context .

Anyway in regard of the reason why there was a leak: getViewTreeObserver() is not going to return the same instance after the View has been detached from the window. Calling removeOnScrollChangedListener() may have no effect, keeping your original OnScrollChangedListener still attached to the old ViewTreeObserver, and so leaking your Context.

这篇关于了解ViewTreeObserver泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-11 01:34