CardboardActivity 中的内存泄漏

Memory leak in CardboardActivity

在分析我的 Google Cardboard 应用程序时,我发现每次离开 activity 3D 图形时都会发生非常大的内存泄漏(15Mb!)。

经过漫长而痛苦的调查,我发现问题的根源是每次关闭 CardboardActivity 子类时发生的 Context leak

解决方案可以在接受的答案中找到*

* 哇...这很尴尬...注意任何类型(和有经验)审阅者:我正在写一个问题给我认识的人已经回答了:我是否应该为风格做些事情,比如添加一些假悬念("will our heroes prevail?! Find out in the accepted answer!"),就像在旧的蝙蝠侠电视剧中一样?

在对我的 CardboardActivity subclass 进行切块和切片后,直到只剩下底座 class,我不得不得出结论,底座 class 本身正在泄漏上下文。

我在网上搜索并发现 this post 解释了有问题的 activity 是如何通过未能注销 class 的私有实例的侦听器来泄露上下文的。

在尝试手动调用上述方法(使用反射)时,我发现在当前版本的 Cardboard SDK(撰写本文时为 0.5.4)中,该字段不再存在。

长话短说:所有传感器现在都由未记录(尚未 public)SensorConnection class 在 CardboardActivity 中作为 sensorConnection 字段实例化,它仍然受到我第一个 link.

中详述的错误的困扰

这让我想到了这个解决方案:

  • 通过反射获取CardboardActivity中的sensorConnection字段
  • 使用它来获取 magneticSensor 字段,再次通过反射
  • 使用 null 参数调用 setOnCardboardTheaterListener,以清除绑定 Activity onDestroy 方法中对 Context 的引用。

归结为以下代码:

private void workAroundLeak() {
    try {
        // Get the sensor Connection
        Class<?> c1 = Class.forName("com.google.vrtoolkit.cardboard.CardboardActivity");
        Field sensorsField = c1.getDeclaredField("sensorConnection");
        sensorsField.setAccessible(true);
        SensorConnection sc = (SensorConnection) sensorsField.get(this);
        if(sc == null) return;

        // Get the magnetSensor
        Class<?> c2 = Class.forName("com.google.vrtoolkit.cardboard.sensors.SensorConnection");
        Field magnetField = c2.getDeclaredField("magnetSensor");
        magnetField.setAccessible(true);
        MagnetSensor ms = (MagnetSensor) magnetField.get(sc);
        if(ms == null) return;

        ms.setOnCardboardTriggerListener(null);
    } catch(Exception e) {}
}

@Override
protected void onDestroy() {
    workAroundLeak();
    super.onDestroy();
}

完全解决了问题。

致智者的一句话:由于此解决方案依赖于反射,它可能会在 Google更新 SDK(可能以干净的方式解决问题)。

希望这对某人有所帮助