在内存泄漏中调用Static方法中的新Runnable()是否安全?

时间:2017-03-07 20:49:27

标签: android multithreading memory-leaks

在我的Activity中,我想在活动加载后1秒钟出现一个浮动的Action按钮。

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

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秒后自动销毁吗?

1 个答案:

答案 0 :(得分:2)

是和否。在正常操作中,将清除引用并释放内存。从技术上讲,如果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创建一个新的活动。您立即发布到主线程等待一分钟。在运行此消息之前,创建的每个活动都将存在。这意味着该活动中包含的所有资源也将在该持续时间内存在。

为了解决这个问题,有一些选择。

选项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将在邮件持续时间内保留在内存中,但开销明显低于完整活动。