【问题标题】:Handler Memory Leak?处理程序内存泄漏?
【发布时间】:2015-01-08 19:37:04
【问题描述】:

我有一个在主线程上声明的处理程序:

  mainHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {

                switch (msg.what) {

                    case 1:

                        Bundle bundle = msg.getData();
                        mTextView.setText(bundle.getString("message"));
                        break;

                    . . .

                    default:
                        super.handleMessage(msg);
                        break;
                }
            }
        };

其中 mTextView 是定义 onCreate() 的 TextView。

我有一个在单独线程中使用的任务。 runnable 存储来自主线程的mainHandler 并告诉它发送消息:

public class SomeRunnable implements Runnable {

    private Handler mHandler;

    public SomeRunnable(Handler handler) throws IOException {
        . . .    
        mHandler = handler;
    }

    @Override
    public void run() {

        while (!Thread.currentThread().isInterrupted()) {
            try {
                    . . .

                    Message msg = mHandler.obtainMessage();
                    . . .
                    mHandler.sendMessage(msg);
                }

            } catch (IOException e) {
                Log.e("Error", e.hashCode() + ": " + e.getLocalizedMessage());
            }
        }
    }

我已经看到,如果 Handler 类不是静态的,则使用 Handler#postDelayed() 等方法可能会造成内存泄漏。但是,我使用的是Handler#sendMessage(),它会立即将一条消息放入消息队列中。

我还有内存泄漏的危险吗?即使有:

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

        mThread.interrupt();
        mainHandler.removeCallbacksAndMessages(null);
    }

谢谢!

【问题讨论】:

  • 从您发布的代码中不清楚,但您的HandlerRunnable 是否包含对非瞬态对象的任何引用?此外,与您似乎所说的相反,我希望 staticly 持有 Handler 比我在上面看到的更容易发生内存泄漏。
  • @323go 我的 Runnable "SomeRunnable" 与主线程中的 Handler "mainHandler" 拥有相同的地址。这是你定义为非瞬态的吗?我的 Handler 没有任何引用,尽管它确实引用了主线程中的 TextView。
  • 为了安全起见,您可以引用 mainHandlerWeakReference。否则看起来你会没事的。
  • Zack,如果你使用eclipse,另一种方法是使用Eclipse Memory Analyzer。并查看您的Application 是否泄漏内存,使用该工具您还可以确定导致程序泄漏的原因。见here
  • @Mike 我今天早上才下载这个。感谢您的建议。我一定会尝试一下并分析内存。只是想我会问在概念上我的实现是否可能存在潜在的内存泄漏。

标签: java android memory-leaks


【解决方案1】:

为了解决所有可能发生的情况,您可以将mHandler 设置为WeakReference,如下所示:

public class SomeRunnable implements Runnable {

    private WeakReference<Handler> mHandlerRef;

    public SomeRunnable(Handler handler) throws IOException {
        . . .    
        mHandlerRef = new WeakReference<Handler>( handler );
    }

    @Override
    public void run() {

        while (!Thread.currentThread().isInterrupted()) {
            try {
                    . . .
                    Handler mHandler = mHandlerRef.get();
                    if( mHandler != null ) {
                        Message msg = mHandler.obtainMessage();
                        . . .
                        mHandler.sendMessage(msg);
                    }
                }

            } catch (IOException e) {
                Log.e("Error", e.hashCode() + ": " + e.getLocalizedMessage());
            }
        }
    }
}

【讨论】:

  • 感谢您的建议!我将看看这对我的应用程序有何影响,我会告诉你我的最终解决方案。
  • 应该 mHandlerRef.get();每次都在 while 循环内调用,还是可以在循环外执行?
  • 取决于你在循环中做了多少工作,或者你是否屈服于那里的其他线程。 run 将在发布后很快执行;使用WeakReference 有帮助的唯一情况是,如果您有一个长时间运行的Runnable,您希望能够干净地中断/中止。
  • Runnable 在单独的线程中执行并持续到应用程序被终止。
  • 那么您肯定想在使用它之前调用get(),因为引用的Handler 可能已经消失了。如果它返回null,这也是一个停止线程执行的信号。
猜你喜欢
  • 2011-01-13
  • 2012-07-01
  • 2015-06-14
  • 1970-01-01
  • 2018-12-18
  • 2012-10-29
  • 2012-05-31
  • 1970-01-01
  • 2017-08-11
相关资源
最近更新 更多