我正在开发一个带有服务的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警告:
This Handler class should be static or leaks might occur: IncomingHandler
这是什么意思?
答案 0 :(得分:372)
如果IncomingHandler
类不是静态的,它将引用您的Service
对象。
Handler
个对象共享一个共同的Looper对象,它们将消息发送到该对象并从中读取。
由于消息包含目标Handler
,只要消息队列中存在带有目标处理程序的消息,就不能对处理程序进行垃圾回收。如果处理程序不是静态的,即使被销毁,您的Service
或Activity
也无法进行垃圾回收。
这可能会导致内存泄漏,至少在一段时间内 - 只要消息留在队列中。除非您发布长时间延迟的消息,否则这不是什么大问题。
您可以将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);
}
}
}
请参阅Romain Guy的post 以获取进一步的参考资料
答案 1 :(得分:61)
正如其他人提到的,Lint警告是因为潜在的内存泄漏。您可以在构建Handler.Callback
时传递Handler
来避免Lint警告(即,您不是Handler
的子类,并且没有Handler
非静态内部类:
Handler mIncomingHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
据我了解,这不会避免潜在的内存泄漏。 Message
个对象包含对mIncomingHandler
对象的引用,该对象包含对Handler.Callback
对象的引用,该对象包含对Service
对象的引用。只要Looper
消息队列中有消息,Service
就不会是GC。但是,除非您在消息队列中有很长的延迟消息,否则它不会是一个严重的问题。
答案 2 :(得分:30)
以下是使用弱引用和静态处理程序类来解决问题的一般示例(如Lint文档中所建议的那样):
public class MyClass{
//static inner class doesn't hold an implicit reference to the outer class
private static class MyHandler extends Handler {
//Using a weak reference means you won't prevent garbage collection
private final WeakReference<MyClass> myClassWeakReference;
public MyHandler(MyClass myClassInstance) {
myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
}
@Override
public void handleMessage(Message msg) {
MyClass myClass = myClassWeakReference.get();
if (myClass != null) {
...do work here...
}
}
}
/**
* An example getter to provide it to some external class
* or just use 'new MyHandler(this)' if you are using it internally.
* If you only use it internally you might even want it as final member:
* private final MyHandler mHandler = new MyHandler(this);
*/
public Handler getHandler() {
return new MyHandler(this);
}
}
答案 3 :(得分:23)
这种方式对我来说效果很好,通过保留在自己的内部类中处理消息的位置来保持代码清洁。
您希望使用的处理程序
Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());
内在类
class IncomingHandlerCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message message) {
// Handle message code
return true;
}
}
答案 4 :(得分:1)
在@Sogger的回答的帮助下,我创建了一个通用的Handler:
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);
}
}
}
界面:
public interface MessageHandler {
void handleMessage(Message 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;
}
}
...
}
答案 5 :(得分:0)
我不确定但您可以尝试在onDestroy()
中初始化处理程序为null答案 6 :(得分:0)
我很困惑。 我发现的示例完全避免使用static属性,而是使用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();
}
}
};
}
我喜欢这种解决方案的地方是尝试混合类和方法变量没有问题。