【问题标题】:Memory leak with interface referance带有接口引用的内存泄漏
【发布时间】:2018-10-30 15:54:09
【问题描述】:

我有一个关于内存泄漏的问题。我有两个课程。

第一个是:

public class Utility {
private static Utility instance = null;
private UpdateListener listener;

//Make it a Singleton class
private Utility(){}
public static Utility getInstance() {
    if (instance == null)
        instance = new Utility();
    return instance;
}

public void setListener(UpdateListener listener) {
    this.listener = listener;
}

//Long running background thread
public void startNewTread() {
    new Thread (new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000 * 10);
                if (listener != null)
                    listener.onUpdate();
            } catch (InterruptedException e) {
                Log.d("Utility", e.getMessage());
            }
        }
    }).start();
}

//Listener interface
public interface UpdateListener {
    public void onUpdate();
}
}

第二类是:

public class ListenerLeak extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //Setting the listener
    Utility.getInstance().setListener(new Utility.UpdateListener() {
        @Override
        public void onUpdate() {
            Log.d("ListenerLeak", "Something is updated!");
        }
    });

    //Starting a background thread
    Utility.getInstance().startNewTread();
}

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

在这个活动中。新的 Utility.UpdateListener 可能会造成内存泄漏吗? 当activitydestoroyed时,只有Updatelistener可以存活。activity可以存活吗?

【问题讨论】:

    标签: android memory-leaks listener


    【解决方案1】:

    在 Utility 类中创建一个内部类,如下所示。然后将线程移动到该类。

     public void startNewTread() {
        new MyThread().start();
        }
    
     private static class MyThread extends Thread {
        @Override
        public void run() {
           try {
                Thread.sleep(1000 * 10);
                if (listener != null)
                    listener.onUpdate();
            } catch (InterruptedException e) {
                Log.d("Utility", e.getMessage());
            }
        }
      }
    

    原因:每次配置更改后,Android 系统都会创建一个新的 Activity,并将旧的 Activity 留在后面进行垃圾回收。但是,线程持有对旧 Activity 的隐式引用,并阻止它被回收。结果,每个新的 Activity 都会被泄露,并且与它们相关的所有资源都永远无法回收。 https://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html 将有助于理解它。

    【讨论】:

    • 有人告诉我泄漏原因是这段代码:Utility.getInstance().setListener(new Utility.UpdateListener() { @Override public void onUpdate() { Log.d("ListenerLeak", "Something已更新!"); } });
    • 这里也是线程不是活动的内部类。而是创建一个匿名的 UpdateListener
    • 你尝试了这两种解决方案吗?结果如何?
    • 不,我没有尝试。如何查看结果?机器人分析器?
    • 你可以在 android studio 中使用 profiler。 stackoverflow.com/questions/47501739/… 这对你有帮助。
    【解决方案2】:

    这可能有点晚了,其他人也有他们的意见,但我也想试一试:)。内存泄漏只是意味着 GC 无法释放对象实例使用的内存,因为它无法确定它是否正在使用。

    在您的情况下,简单地说:Utility 类被定义为 Singletone,它在类中有一个自身的静态实例。所以只要应用程序还活着,它就会在那里。当您使用 setListener() 函数从活动中设置监听器时,您将在活动中创建的实例传递给它,该实例具有有限的生命周期并绑定到活动的生命周期。因此可以说静态实用程序类可以比传递给实用程序的侦听器实例寿命更长并泄漏活动。因此,无论您是否使用线程,这都会泄漏活动实例,因为它可以比具有对父活动类的隐式引用的侦听器实例寿命更长。

    这里如何防止泄漏? 我认为对侦听器使用 Wea​​kReference 是一个很好的起点,同时确保在调用活动的 onDestroy() 方法后立即释放或删除侦听器。但正如文档所述,不能保证总是调用 onDestroy() 。所以在我看来,使用 onPause() 或 onStop() 是一个更好的主意。

    【讨论】:

      猜你喜欢
      • 2012-12-14
      • 2015-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多