Android 此处理程序类应该是静态的,否则可能会发生泄漏:IncomingHandler
我正在开发一个带有服务的Android 2.3.3应用程序。我在该服务中有以下内容,用于与主要活动进行沟通: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");
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对象,它们向该对象发布消息并从中读取消息
由于消息包含targetHandler
,只要消息队列中存在带有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();
}
}
};
}