【问题标题】:Handlers and memory leaks in AndroidAndroid 中的处理程序和内存泄漏
【发布时间】:2012-07-01 23:51:10
【问题描述】:

请看下面的代码:

public class MyGridFragment extends Fragment{
    
    Handler myhandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case 2:   
                    ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                    urls.addAll(theurls);
                    theimageAdapter.notifyDataSetChanged();
                    dismissBusyDialog();
                    break;
            }
        }
    }
}

当我像这样使用处理程序时,我收到一个警告“处理程序应该是静态的,否则它很容易出现内存泄漏。”谁能告诉我最好的方法是什么?

【问题讨论】:

  • 我不相信你正确使用了hander。看看这个指南:vogella.com/articles/AndroidPerformance/article.html。它没有在示例代码中声明为静态。 ://
  • 好吧,即使这样使用它也会给我同样的错误。直到我昨晚升级了我的 android sdk 之前,这种情况从未发生过。现在只需将处理程序声明为类变量就会弹出此 lint 警告
  • 那么将你的处理程序声明为静态怎么样?
  • @Zsombor 好吧,我指的是处理程序中的非静态对象
  • 查看此blog post 以获得更深入的分析

标签: android handler


【解决方案1】:

我最近在自己的代码中更新了类似的内容。我刚刚将匿名 Handler 类设置为受保护的内部类,并且 Lint 警告消失了。看看下面的代码是否适合你:

public class MyGridFragment extends Fragment{

    static class MyInnerHandler extends Handler{
        WeakReference<MyGridFragment> mFrag;

        MyInnerHandler(MyGridFragment aFragment) {
            mFrag = new WeakReference<MyGridFragment>(aFragment);
        }

        @Override
        public void handleMessage(Message message) {
            MyGridFragment theFrag = mFrag.get();
            switch (message.what) {
            case 2:
                ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                theFrag.urls.addAll(theurls);
                theFrag.theimageAdapter.notifyDataSetChanged();
                theFrag.dismissBusyDialog();
                break;
            }//end switch
        }
    }
    MyInnerHandler myHandler = new MyInnerHandler(this);
}

您可能需要更改我放置“theFrag”的位置。因为我只能猜测那些引用的内容。

【讨论】:

  • 这似乎正是我想要的。我还没有测试过它,但似乎没有理由让它不起作用..谢谢!
  • 不应该在 mFrag.get() 之后进行检查来检查 theFrag != null 是否因为它可能会被垃圾收集。
  • 取决于您的用例。在大多数情况下,我的个人代码将内部静态类定义设为私有,因为匿名类定义不应该在其他地方使用(即使是后代)。当仅限于定义它的类时,每当父 Fragment 被销毁时,我们的内部类引用应该在 Fragment 变为 NULL 之前被销毁。我们使用 Wea​​kReference,所以当 Fragment 被垃圾收集时,我们的内部类不会干扰。如果你公开你的内部类,或者不确定,请在使用前检查 theFrag!=null 是否。
  • 这个答案绝对救了我!感谢您发布此信息!
  • 与简单地使处理程序静态相比,这种方法有什么优势吗?
【解决方案2】:

这是我制作的一个有点有用的小类,你可以使用。遗憾的是它仍然很冗长,因为你不能有匿名的静态内部类。

import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;

/** A handler which keeps a weak reference to a fragment. According to
 * Android's lint, references to Handlers can be kept around for a long
 * time - longer than Fragments for example. So we should use handlers
 * that don't have strong references to the things they are handling for.
 * 
 * You can use this class to more or less forget about that requirement.
 * Unfortunately you can have anonymous static inner classes, so it is a
 * little more verbose.
 * 
 * Example use:
 * 
 *  private static class MsgHandler extends WeakReferenceHandler<MyFragment>
 *  {
 *      public MsgHandler(MyFragment fragment) { super(fragment); }
 * 
 *      @Override
 *      public void handleMessage(MyFragment fragment, Message msg)
 *      {
 *          fragment.doStuff(msg.arg1);
 *      }
 *  }
 * 
 *  // ...
 *  MsgHandler handler = new MsgHandler(this);
 */
public abstract class WeakReferenceHandler<T> extends Handler
{
    private WeakReference<T> mReference;

    public WeakReferenceHandler(T reference)
    {
        mReference = new WeakReference<T>(reference);
    }

    @Override
    public void handleMessage(Message msg)
    {
        if (mReference.get() == null)
            return;
        handleMessage(mReference.get(), msg);
    }

    protected abstract void handleMessage(T reference, Message msg);
}

【讨论】:

  • 如果我不需要重写handleMessage,只使用一个handler来post Runnable,我可以只使用new Handler()
  • if (mReference.get() == null) 返回;处理消息(mReference.get(),味精);是一种不好的方法,因为在 null 检查和 handleMessage 之间仍然可以 gc'ed 引用导致空指针异常,因此将返回值首先存储在 T reference = mReference.get() 中是一种更好的方法;然后检查 null 并将局部变量传递给 handleMessage 方法。
【解决方案3】:

根据ADT 20 Changes,您应该将其设为静态。

新的 Lint 检查:

检查以确保 Fragment 类是可实例化的。如果你不小心做了一个 片段内部类非静态,或者忘记有默认构造函数,可以点击运行时 当系统在配置更改后尝试重新实例化您的片段时出错。

查找处理程序泄漏:此检查确保处理程序内部类不包含 对其外部类的隐式引用。

【讨论】:

  • 那么您认为我应该如何处理上述问题。您是否需要查看更多代码。如果需要,请告诉我
  • 如果不能让Handler静态化,最好的办法可能是把它移到父Activity中,传递一个Fragment的引用。
  • 感谢您的回复 Geobits...但将其移动到父 Activity 仍然不会发出警告。尽管在我的情况下,内部类不会比外部类活得更长,但摆脱了警告是我比较关心的事情。
【解决方案4】:

如果您阅读有关 AccountManager 或 PendingIntent 的文档,您会看到某些方法将 Handler 作为参数之一。

For example:

  • onFinished - 发送完成时回调的对象,或 null 表示没有回调。
  • handler - 识别应该发生回调的线程的处理程序。如果为null,则回调将从进程的线程池中发生。

想象一下这种情况。有的Activity调用PendingIntent.send(...),放入Handler的非静态内部子类。然后活动被破坏。但是内部阶级生活。

内部类仍然拥有指向已销毁活动的链接,它不能被垃圾回收。

如果您不打算将处理程序发送到此类方法,则无需担心。

【讨论】:

  • 谢谢 QuickNick ...好吧,当您说“如果您不打算将处理程序发送到此类方法,则无需担心”时,我确实同意您的看法。但我需要摆脱那个警告
【解决方案5】:

我遇到了同样的问题,我发现它是这个主题之一,有很多问题和很少的答案。我的解决方案很简单,希望对某人有所帮助:

/* BEFORE */
private Handler mHandler= new Handler() {
        @Override public void handleMessage(Message msg) {
        this.doSomething();
    };
};

我们可以创建一个简单地运行 Runnable 的静态 Handler 子类。实际的处理程序实例将通过可以访问实例变量的可运行程序知道要做什么。

/* AFTER */
static class RunnableHandler extends Handler {
    private Runnable mRunnable;
    public RunnableHandler(Runnable runnable) { 
        mRunnable = runnable;
    }
    @Override public void handleMessage(Message msg) {
        mRunnable.run();
    };
}
private RunnableHandler mHandler = new RunnableHandler(new Runnable() {
    @Override public void run() {
        this.doSomething();
    } });

警告消失,但功能相同。

【讨论】:

  • 感谢您回复 Urkidi,但出于某种原因,我发现这更像是一种 hack。无论如何,我都不是专家,可能是非常错误的
  • 我喜欢这种方式,但我希望有更多知识的人告诉我这是否只是一个黑客攻击。
  • 这会隐藏 lint 警告,但不能解决根本问题。 runnable 将引用“this”,并且由于 RunnableHandler 保留对 runnable 的引用,因此您会遇到相同的错误,只是链更复杂。
【解决方案6】:

这种情况的一个简单解决方案可能是:

Handler handler=new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //do your stuff here
        return false;
    } });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多