android - Understanding ViewTreeObserver leak -
i using leakcanary 1.3.1-snapshot. found leak concerning viewtreeobserver.onscrollchangedlistener
setup , fixed in 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); }
however leakcanary still report leak, 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 | [
try change code removes listener run before view detached window, this:
@override public void ondetachedfromwindow() { scrollview.getviewtreeobserver().removeonscrollchangedlistener(scrollviewchangelistener); super.ondetachedfromwindow(); }
the reason after being detached window, getviewtreeobserver()
returns different instance (the "floating tree observer"), not gonna remove listener same object added it.
update
since using viewtreeobserver
of child view, behavior more complex , 1 possible solution involve adding onattachstatechangelistener
scrollview
, add/remove onscrollchangedlistener
there.
anyway in regard of reason why there leak: getviewtreeobserver()
not going return same instance after view
has been detached window. calling removeonscrollchangedlistener()
may have no effect, keeping original onscrollchangedlistener
still attached old viewtreeobserver
, , leaking context
.
Comments
Post a Comment