Android 此处理程序类应该是静态的,否则可能会发生泄漏:IncomingHandler

Android 此处理程序类应该是静态的,否则可能会发生泄漏:IncomingHandler,android,memory-leaks,static-classes,android-lint,android-handler,Android,Memory Leaks,Static Classes,Android Lint,Android Handler,我正在开发一个带有服务的Android 2.3.3应用程序。我在该服务中有以下内容,用于与主要活动进行沟通: public class UDPListenerService extends Service { private static final String TAG = "UDPListenerService"; //private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");

我正在开发一个带有服务的Android 2.3.3应用程序。我在该服务中有以下内容,用于与主要活动进行沟通:

public class UDPListenerService extends Service
{
    private static final String TAG = "UDPListenerService";
    //private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
    private UDPListenerThread myThread;
    /**
     * Handler to communicate from WorkerThread to service.
     */
    private Handler mServiceHandler;

    // Used to receive messages from the Activity
    final Messenger inMessenger = new Messenger(new IncomingHandler());
    // Use to send message to the Activity
    private Messenger outMessenger;

    class IncomingHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
        }
    }

    /**
     * Target we publish for clients to send messages to Incoming Handler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    [ ... ]
}
这里,
final Messenger mMessenger=new Messenger(new IncomingHandler()),我得到以下Lint警告:

此处理程序类应该是静态的,否则可能会发生泄漏:IncomingHandler


这意味着什么?

如果
IncomingHandler
类不是静态的,它将引用您的
服务
对象

同一线程的
Handler
对象都共享一个公共的Looper对象,它们向该对象发布消息并从中读取消息

由于消息包含target
Handler
,只要消息队列中存在带有target Handler的消息,就不能对处理程序进行垃圾收集。如果处理程序不是静态的,您的
服务
活动
即使在被销毁后也不能被垃圾收集

这可能导致内存泄漏,至少在一段时间内——只要消息留在队列中。这不是什么大问题,除非你发布长时间延迟的消息

您可以将
IncomingHandler
设置为静态,并对您的服务进行
WeakReference

static class IncomingHandler extends Handler {
    private final WeakReference<UDPListenerService> mService; 

    IncomingHandler(UDPListenerService service) {
        mService = new WeakReference<UDPListenerService>(service);
    }
    @Override
    public void handleMessage(Message msg)
    {
         UDPListenerService service = mService.get();
         if (service != null) {
              service.handleMessage(msg);
         }
    }
}
静态类IncomingHandler扩展处理程序{
私人最终WeakReference mService;
收入管理员(UDPListenerService服务){
mService=新的WeakReference(服务);
}
@凌驾
公共无效handleMessage(消息消息消息)
{
UDPListenerService service=mService.get();
if(服务!=null){
服务.handleMessage(msg);
}
}
}

请参阅Romain Guy的这篇文章以获取进一步参考

,因为其他人提到了Lint警告是由于潜在的内存泄漏。在构造
处理程序
时,可以通过传递
处理程序.Callback
来避免Lint警告(即,您没有子类
处理程序
,也没有
处理程序
非静态内部类):


据我所知,这无法避免潜在的内存泄漏<代码>消息
对象包含对
mIncomingHandler
对象的引用,该对象包含对
处理程序的引用。回调
对象包含对
服务
对象的引用。只要
活套
消息队列中有消息,
服务
就不会被激活。但是,除非消息队列中有长延迟消息,否则这不会是一个严重的问题

这种方法对我来说效果很好,它通过在自己的内部类中保持处理消息的位置来保持代码的整洁

您希望使用的处理程序

Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
内部阶级

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}

我不确定,但您可以尝试在onDestroy()中将处理程序初始化为null这里是一个使用弱引用和静态处理程序类来解决问题的通用示例(如Lint文档中所建议的):

公共类MyClass{
//静态内部类不包含对外部类的隐式引用
私有静态类MyHandler扩展了Handler{
//使用弱引用意味着您不会阻止垃圾收集
私人最终WeakReference myClassWeakReference;
公共MyHandler(MyClass myClassInstance){
myClassWeakReference=新的WeakReference(myClassInstance);
}
@凌驾
公共无效handleMessage(消息消息消息){
MyClass MyClass=myClassWeakReference.get();
如果(myClass!=null){
……在这里工作。。。
}
}
}
/**
*将其提供给某个外部类的示例getter
*如果您在内部使用“new MyHandler(this)”,也可以直接使用它。
*如果您只在内部使用它,您甚至可能希望它成为最终成员:
*私有最终MyHandler mHandler=新的MyHandler(此);
*/
公共处理程序getHandler(){
返回新的MyHandler(此);
}
}

在@Sogger回答的帮助下,我创建了一个通用处理程序:

public class MainThreadHandler<T extends MessageHandler> extends Handler {

    private final WeakReference<T> mInstance;

    public MainThreadHandler(T clazz) {
        // Remove the following line to use the current thread.
        super(Looper.getMainLooper());
        mInstance = new WeakReference<>(clazz);
    }

    @Override
    public void handleMessage(Message msg) {
        T clazz = mInstance.get();
        if (clazz != null) {
            clazz.handleMessage(msg);
        }
    }
}
我使用它如下。但我不能100%确定这是不是安全的。也许有人可以对此发表评论:

public class MyClass implements MessageHandler {

    private static final int DO_IT_MSG = 123;

    private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);

    private void start() {
        // Do it in 5 seconds.
        mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DO_IT_MSG:
                doIt();
                break;
        }
    }

    ...

}
公共类MyClass实现MessageHandler{
私有静态最终int DO_IT_MSG=123;
private MainThreadHandler mHandler=新的MainThreadHandler(此);
私有void start(){
//5秒钟内完成。
mHandler.sendEmptyMessageDelayed(DO_IT_MSG,5*1000);
}
@凌驾
公共无效handleMessage(消息消息消息){
开关(msg.what){
case DO_IT_MSG:
doIt();
打破
}
}
...
}
我很困惑。 我发现的示例完全避免了静态属性,并使用UI线程:

    public class example extends Activity {
        final int HANDLE_FIX_SCREEN = 1000;
        public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                int imsg;
                imsg = msg.what;
                if (imsg == HANDLE_FIX_SCREEN) {
                    doSomething();
                }
            }
        };
    }

我喜欢这个解决方案的一点是,尝试混合类和方法变量是没有问题的。

Romain表明,对外部类的WeakReference是所需要的,静态嵌套类是不必要的。我想我更喜欢WeakReference方法,因为否则整个外部类会因为我需要的所有“静态”变量而急剧变化。如果你想使用嵌套类,它必须是静态的。否则,WeakReference不会改变任何东西。内部(嵌套但非静态)类始终保持对外部类的强引用。但不需要任何静态变量。@someonemserive是一个弱引用
get()
在引用对象被gc-ed时将返回null。在这种情况下,当服务停止时。注意:使IncomingHandler为静态后,我在“final Messenger inMessenger=new Messenger(new IncomingHandler());”行上收到错误“构造函数MyActivity.IncomingHandler()未定义”。解决方案是将该行更改为“final Messenger in Messenger=new Messenger(new IncomingHandler(this));”@Someone某处是的,Romain的帖子是错误的,因为他没有声明内部类static,这忽略了整个要点。除非他有超酷的编译器自动转换inne
public interface MessageHandler {

    void handleMessage(Message msg);

}
public class MyClass implements MessageHandler {

    private static final int DO_IT_MSG = 123;

    private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);

    private void start() {
        // Do it in 5 seconds.
        mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DO_IT_MSG:
                doIt();
                break;
        }
    }

    ...

}
    public class example extends Activity {
        final int HANDLE_FIX_SCREEN = 1000;
        public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                int imsg;
                imsg = msg.what;
                if (imsg == HANDLE_FIX_SCREEN) {
                    doSomething();
                }
            }
        };
    }