From 0781fac6d9e9703eb30c040b24a474ea0a1fd536 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Mon, 6 May 2024 12:22:51 -0400 Subject: [PATCH] [Mono.Android] Dispose of the `RunnableImplementor` on error (#8907) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: 3ce27c9f507cbc96cfeeae76b29cf83b1e5e6e25 Commit 3ce27c9f had a TODO: > TODO: Address [^0] and dispose of the `RunnableImplementor` instance > when `View.Post()` returns `false`. > > [^0]: we leak if `View.post(Runnable)` returns *false*. The time is now! Review all `new RunnableImplementor(…, true)` calls, and if a "removable" `RunnableImplementor` is passed to a method which can return an error such as [`Handler.post()`][0] or [`View.post()`][1], dispose of the `RunnableImplementor` instance on error. [0]: https://developer.android.com/reference/android/os/Handler#post(java.lang.Runnable) [1]: https://developer.android.com/reference/android/view/View#post(java.lang.Runnable) --- src/Mono.Android/Android.OS/Handler.cs | 35 ++++++++++++++++++++++---- src/Mono.Android/Android.Views/View.cs | 14 +++++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/Mono.Android/Android.OS/Handler.cs b/src/Mono.Android/Android.OS/Handler.cs index f438614eb43..40634af756f 100644 --- a/src/Mono.Android/Android.OS/Handler.cs +++ b/src/Mono.Android/Android.OS/Handler.cs @@ -13,27 +13,52 @@ public Handler (Action handler) public bool Post (Action action) { - return Post (new Java.Lang.Thread.RunnableImplementor (action, true)); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (Post (runnable)) { + return true; + } + runnable.Dispose (); + return false; } public bool PostAtFrontOfQueue (Action action) { - return PostAtFrontOfQueue (new Java.Lang.Thread.RunnableImplementor (action, true)); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (PostAtFrontOfQueue (runnable)) { + return true; + } + runnable.Dispose (); + return false; } public bool PostAtTime (Action action, long uptimeMillis) { - return PostAtTime (new Java.Lang.Thread.RunnableImplementor (action, true), uptimeMillis); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (PostAtTime (runnable, uptimeMillis)) { + return true; + } + runnable.Dispose (); + return false; } public bool PostAtTime (Action action, Java.Lang.Object token, long uptimeMillis) { - return PostAtTime (new Java.Lang.Thread.RunnableImplementor (action, true), token, uptimeMillis); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (PostAtTime (runnable, token, uptimeMillis)) { + return true; + } + runnable.Dispose (); + return false; } public bool PostDelayed (Action action, long delayMillis) { - return PostDelayed (new Java.Lang.Thread.RunnableImplementor (action, true), delayMillis); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (PostDelayed (runnable, delayMillis)) { + return true; + } + runnable.Dispose (); + return false; } public void RemoveCallbacks (Action action) diff --git a/src/Mono.Android/Android.Views/View.cs b/src/Mono.Android/Android.Views/View.cs index 58de9a2c027..5ef341f243b 100644 --- a/src/Mono.Android/Android.Views/View.cs +++ b/src/Mono.Android/Android.Views/View.cs @@ -49,12 +49,22 @@ public T RequireViewById< public bool Post (Action action) { - return Post (new Java.Lang.Thread.RunnableImplementor (action, true)); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (Post (runnable)) { + return true; + } + runnable.Dispose (); + return false; } public bool PostDelayed (Action action, long delayMillis) { - return PostDelayed (new Java.Lang.Thread.RunnableImplementor (action, true), delayMillis); + var runnable = new Java.Lang.Thread.RunnableImplementor (action, removable: true); + if (PostDelayed (runnable, delayMillis)) { + return true; + } + runnable.Dispose (); + return false; } public bool RemoveCallbacks (Action action)