Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leak due to android.widget.BubblePopupHelper

I'm using MemoryAnalyzer tool to find memory leaks in my Android applicaton. So I run my application, visit all the activities, then press back until I get to the desktop. Then I use DDMS to get a memory dump (having pressed Cause GC several times).

Then I use an OQL query select * from instanceof android.app.Activity to find leaking activities, and then press Merge Shortest Path to GC Roots -> exclude all phantom/weak/soft/etc references on a leaked object. And here I have this picture:

enter image description here

So it seems that somewhere in the system there is a static object BubblePopupHelper.sHelper, which retains a reference to an EditText view from my activity, causing the entire activity to leak! But what is this BubblePopupHelper? I couldn't find any information on this class in the official docs. And how can I prevent my activity from being keeped in memory due to being referenced by this strange object?

I was testing on LG L40 device, running API19

like image 656
netimen Avatar asked Dec 03 '14 08:12

netimen


People also ask

What causes memory leaks in Android?

Memory leaks occur when an application allocates memory for an object, but then fails to release the memory when the object is no longer being used. Over time, leaked memory accumulates and results in poor app performance and even crashes.

How do you find memory leaks in Android applications?

The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes. It shows a realtime graph of your app's memory use and lets you capture a heap dump, force garbage collections, and track memory allocations.


1 Answers

My leak detection tools report the same leak on a regular basis, from LG phones only:

object com.squareup.SomeActivity
`-mContext of object android.widget.EditText
  `-mView of object android.widget.BubblePopupHelper
    `-sHelper of class android.widget.BubblePopupHelper

Manufacturers like to change the private APIs of the Android SDK under the hood. This is a memory leak introduced by LG.

From what I can gather, the focused EditText uses that BubblePopupHelper, probably to display some copy/paste popup or the text handle. Since there's only one focused edit text at a time, they made the helper a singleton, and it keeps a reference to the latest edit text focused.

So that means an entire Activity & its entire view hierarchy will leak, until another edit text gets focused.

How can you fix that? Sadly, this is SDK code, so while this might be fixed in future releases from LG, there will always be some users with that bug.

While this bug is certainly not your fault, it still is a memory leak, which can leak to increased OutOfMemory Errors. So, it's worth attempting to fix it,

There is a way, but it's not pretty. When the activity is destroyed, you can use reflection to clear the leak. For instance, one way could be to clear the sHelper field, or another one would be to clear the mView field on the helper. Either way, you should try that on the device (I don't have it right now) and see if it works.

private static final Executor backgroundExecutor =
    newCachedThreadPool(backgroundThreadFactory("android-leaks"));

public static void fixLGBubblePopupHelper(final Application application) {
  backgroundExecutor.execute(new Runnable() {
    @Override public void run() {
      final Field sHelperField;
      try {
        Class<?> bubbleClass = Class.forName("android.widget.BubblePopupHelper");
        sHelperField = bubbleClass.getDeclaredField("sHelper");
        sHelperField.setAccessible(true);
      } catch (Exception ignored) {
        // We have no guarantee that this class / field exists.
        return;
      }
      application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
          try {
            sHelperField.set(null, null);
          } catch (IllegalAccessException ignored) {
          }
        }
      });
    }
  });
}
like image 168
Pierre-Yves Ricau Avatar answered Oct 17 '22 20:10

Pierre-Yves Ricau