Memory leak due to android.widget.BubblePopupHelper

I am using the MemoryAnalyzer tool to search for memory leaks in an Android application. Therefore, I launch my application, visit all the actions, and then squeeze until I get to the desktop. Then I use DDMS to get a memory dump (by clicking Cause GC several times ).

Then I use the OQL query select * from instanceof android.app.Activityto find the activity leak, and then click Combine the shortest path to GC Roots -> exclude all phantom / weak / soft / etc links on the leak 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.sHelperthat keeps a reference to the view EditTextfrom my activity, as a result of which all the activity goes on! But what is this BubblePopupHelper? I could not find information about this class in official documents. And how can I prevent the activity from being kept in memory due to a reference to this strange object?

I tested the LG L40 device by launching API19

+4
source share
1 answer

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

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

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

, , EditText BubblePopupHelper, , copy/paste . , , .

, , , .

? , SDK-, , LG, .

, , , - , OutOfMemory. , ,

, . , , . , sHelper, - mView . , ( ) , .

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) {
          }
        }
      });
    }
  });
}
+8

All Articles