在静态方法中调用 new Runnable() 是不是可以避免内存泄漏?

Posted

技术标签:

【中文标题】在静态方法中调用 new Runnable() 是不是可以避免内存泄漏?【英文标题】:Is Calling new Runnable() inside Static method safe from Memory Leak?在静态方法中调用 new Runnable() 是否可以避免内存泄漏? 【发布时间】:2017-07-28 05:42:24 【问题描述】:

在我的 Activity 中,我希望在 Activity 加载 1 秒后出现一个浮动操作按钮。

为此,我创建了一个像这样的新静态方法。

public class utility 

public static void delayedShowFab(final FloatingActionButton fab)
    
        new Handler().postDelayed(new Runnable()
            @Override
            public void run() 
                fab.show();
            
        , 1000);
    

我这样调用方法:

@Override    
protected void onCreate(Bundle savedInstanceState) 
    final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    utility.delayedShowFab(fab);

我的代码对内存泄漏安全吗?新的 Runnable 和新的 Handler 对象会在 1 秒后自动销毁吗?

【问题讨论】:

我已经放弃了这样的设计,它们似乎对我来说永远无法正常工作。未知的崩溃一秒钟变成了五秒钟,因为其他一些进程正在占用 CPU。今天,我将完成构建 UI 的最后一步,让按钮可见,然后继续制作出色的应用程序。 【参考方案1】:

是的,不是的。在正常操作中,引用将被清除,内存将被释放。从技术上讲,如果 Activity 在经过一秒钟之前被杀死,则会发生内存泄漏。 FloatingActionButton 仍然持有对其父 Context 的引用。因此,Activity 将保留在内存中,直到 Handler 处理消息,在这种情况下,fab 将显示,但由于 Activity 不再在视图中,因此什么都不会发生。

这不会对性能产生非常显着的影响,因为它只有一秒钟。那一秒钟只有一些小开销,但没什么大不了的。 真正的问题是,如果您将该时间延长 1 分钟。

public class utility 

public static void delayedShowFab(final FloatingActionButton fab)
    
        new Handler().postDelayed(new Runnable()
            @Override
            public void run() 
                fab.show();
            
        , 1000 * 60);
    

现在,假设用户刚刚决定在那一分钟内将手机旋转一百次(也许他正在慢跑并且屏幕处于开启状态。谁知道呢?)。

每次屏幕旋转时,都会创建一个带有新 FAB 的新 Activity。您立即发布到主线程以等待一分钟。在运行此消息之前,创建的每个 Activity 都将存在。这意味着该 Activity 中包含的所有资源也将在该持续时间内存在。

要解决这个问题,有几个选项。

选项 1:取消操作。

保持 API 完整。您可以返回取消操作的方法。类似这样:

public class utility 

public interface Cancelable 
    void cancel();


public static void delayedShowFab(final FloatingActionButton fab) 
        final Handler handler = new Handler();
        final Runnable runnable = new Runnable()
            @Override
            public void run() 
                fab.show();
            
        ;

        handler.postDelayed(runnable, 1000);

        return new Cancelable() 
           public void cancel() 
              handler.removeCallbacks(runnable);
           
        
    

然后,在 Activity 的 onDestroy 方法中,只需在返回的对象上调用 cancel

选项 2:使用弱引用

WeakReferences 用于保存对对象的引用,而不包括在引用计数中。这样,当对该对象的所有引用都消失时,它就会消失。

所以在本例中,您将持有对 FloatingActionButon 的 WeakReference 如果它仍然存在,则只调用show

public class utility 

public static void delayedShowFab(final FloatingActionButton fab)
    
        final WeakReference<FloatingActionButton> ref = new WeakReference<>(fab);
        new Handler().postDelayed(new Runnable()
            @Override
            public void run() 
                FloatingActionButton fab = ref.get();
                // You always have to check because it may disappear.
                if(fab != null)  
                  fab.show();
                
            
        , 1000 * 60);
    

在此解决方案中,RunnableWeakReference 将在消息期间保留在内存中,但开销明显小于完整的 Activity。

【讨论】:

以上是关于在静态方法中调用 new Runnable() 是不是可以避免内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

javapote如何实现方法调用

__new__静态方法

JUC并发编程 -- 线程状态转换

JAVAJava 线程

静态方法调用内部类时候的new 问题

在静态方法中new 一个内部类对象和new 一个外部类对象的区别