【问题标题】:Memory leak due to android.widget.BubblePopupHelper由于 android.widget.BubblePopupHelper 导致的内存泄漏
【发布时间】:2014-12-03 08:41:23
【问题描述】:

我正在使用 MemoryAnalyzer 工具来查找我的 Android 应用程序中的内存泄漏。所以我运行我的应用程序,访问所有活动,然后按回直到我到达桌面。然后我使用 DDMS 来获取内存转储(按了几次 Cause GC)。

然后我使用 OQL 查询 select * from instanceof android.app.Activity 查找泄漏活动,然后在泄漏对象上按 Merge Shortest Path to GC Roots -> exclude all phantom/weak/soft/etc 引用。在这里我有这张照片:

所以似乎系统中的某个地方有一个静态对象BubblePopupHelper.sHelper,它保留了我的活动对EditText视图的引用,导致整个活动泄漏!但这是什么BubblePopupHelper?我在official docs 中找不到有关该课程的任何信息。以及如何防止我的活动由于被这个奇怪的对象引用而保留在内存中?

我在运行 API19 的 LG L40 设备上进行测试

【问题讨论】:

  • 我今天发现了这个,也是在带有 API19 的 LG 设备上。 @Blackbelt 这绝对是一个泄漏,因为活动被破坏并且应该被垃圾收集,但由于这个静态字段,它在堆上保持活动状态。在我的例子中,我在创建堆转储之前打开了一个简单的小活动,这样我就可以轻松识别任何其他活动的泄漏。

标签: android memory-leaks


【解决方案1】:

我的泄漏检测工具会定期报告相同的泄漏,仅来自 LG 手机:

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

制造商喜欢在底层更改 Android SDK 的私有 API。这是 LG 引入的内存泄漏。

据我所知,重点突出的 EditText 使用 BubblePopupHelper,可能是为了显示一些复制/粘贴弹出窗口或文本句柄。由于一次只有一个焦点编辑文本,因此他们将帮助程序设为单例,并保留对最新焦点编辑文本的引用。

这意味着整个 Activity 及其整个视图层次结构将泄漏,直到另一个编辑文本获得焦点。

你怎么能解决这个问题?遗憾的是,这是 SDK 代码,所以虽然这可能会在 LG 的未来版本中得到修复,但总会有一些用户遇到该错误。

虽然这个错误肯定不是你的错,但它仍然是内存泄漏,它可能会导致 OutOfMemory 错误增加。所以,值得尝试修复它,

有一种方法,但它并不漂亮。当activity被销毁时,可以使用反射来清除泄漏。例如,一种方法可能是清除 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) {
          }
        }
      });
    }
  });
}

【讨论】:

  • 尝试在 LG 网站上寻找联系人以获取错误报告,但它是一个可怕的黑洞。
猜你喜欢
  • 2016-02-15
  • 1970-01-01
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 2011-03-03
  • 1970-01-01
  • 2013-07-23
  • 2015-08-31
相关资源
最近更新 更多