-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Description
Context: #15769
When calling Android.Views.View.Post(Action) the passed Action will be wrapped in a RunnableImplementor and passed to Android.Views.View.Post(IRunnable). The constructor for RunnableImplementor adds itself to a static dictionary (presumably to retain a reference to it within .NET), from which it is removed when IRunnable.Run() is called. If Run is never called, the RunnableImplementor is never removed from this dictionary and will persist for the lifetime of the app.
It is possible to enter this scenario when calling Android.Views.View.Post(Action) on a view that is not attached to a window (i.e. isn't part of the view hierarchy), as Android.Views.View.Post(IRunnable) will add the RunnableImplementor to a queue instead of passing it to the Handler: the contents of this queue are passed to the Handler when the View is attached to the view hierarchy, however if it is destroyed instead the queue is deleted and IRunnable.Run() is never called. When this happens, the RunnableImplementor, the Action and anything referenced by the Action will leak.
There is a method View.RemoveCallbacks(Action) to remove the RunnableImplementor associated with the passed Action from the dictionary, however this requires the end user to keep track of posted Actions themselves and prohibits the use of code like
view.Post(() =>
{
// do something on the UI thread
});
as a reference to the Action must be retained. An Action passed in the above way would be impossible to clean up later.
This issue is especially problematic in views like CollectionView, where child views are constantly being created, detached and recycled as the view is scrolled. If these children use Android.Views.View.Post(Action) without considering their current attach status, there is a risk of the Action leaking.
Steps to Reproduce
I'm working on a proper repro which I will update this post with, but in the mean time here are some steps to reproduce this issue:
- Instantiate an
Android.Views.Viewor derivative, but do not add it as a child to any other view - Call
View.Post(Action) - Dispose the
View, remove any references to it and run the garbage collector - The
RunnableImplementor,Actionand anything referenced within theActionwill leak. - Calling
View.Post(Action)repeatedly (e.g. in a loop, on a timer etc) will eventually cause the application to crash with the messageglobal reference table overflow
Link to public reproduction project repository
No response
Version with bug
7.0.101
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Tested on Android 7.1 and 11
Did you find any workaround?
Avoid using View.Post if there is the possibility that the View might not be reattached. In my code, I did this by instantiating another Handler in the derived view and Posting to this instead.
Relevant log output
No response