此消息无法回收,因为它仍在使用中

This message cannot be recycled because it is still in use

我正在尝试使用 this article 创建异步 UDP 套接字。

所以我有这个代码:

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpThread
    extends HandlerThread {

    private static final String TAG = "UDP";
    private final Handler uiHandler, workerHandler;
    private final DatagramSocket socket = new DatagramSocket();

    public UdpThread(final Handler uiHandler, final String hostname, final int port) throws SocketException {
        super(TAG);
        this.uiHandler = uiHandler;
        start();
        workerHandler = new Handler(getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(final Message msg) {
                /*
                if (msg.what == port && msg.obj == hostname) {
                    final InetSocketAddress address = new InetSocketAddress(hostname, port);
                    Log.d(TAG, "Connecting to " + address);
                    try {
                        socket.connect(address);
                    } catch (SocketException se) {
                        throw new RuntimeException(se);
                    }
                }
                */
                msg.recycle(); //java.lang.IllegalStateException: This message cannot be recycled because it is still in use.
                return true;
            }
        });
        workerHandler.obtainMessage(port, hostname).sendToTarget();
    }
}

但是当我 运行 代码时,我在尝试回收消息时得到了提到的 java.lang.IllegalStateException: This message cannot be recycled because it is still in use.。为什么会这样,如何解决它并防止内存泄漏?

尝试使用AsyncTask在处理程序线程完成处理后删除消息。

//[..]
        //synchronized with the handler thread
        @Override
        public boolean handleMessage(final Message msg) {
            new MessageDestructor().execute(msg);
            return true;
        }
//[..]
private class MessageDestructor extends AsyncTask<Message, Void, Void> {
    Message msg;
    @Override
    protected String doInBackground(Message... params) {
        msg = (Message) params[0]; 
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
       msg.recycle(); //synchronized with the main thread
    }

    @Override
    protected void onPreExecute() {
    }

    @Override
    protected void onProgressUpdate(Void... values) {
    }
}

那么首先让我们看看 Message recycle() 方法是如何工作的。

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

因此,如果正在使用,您将得到 IllegalStateException

isInUse() 只是检查标志,看起来像:

boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }

当我们尝试阅读有关该标志的信息时,我们会看到描述:

If set message is in use.

This flag is set when the message is enqueued and remains set while it is delivered and afterwards when it is recycled. The flag is only cleared when a new message is created or obtained since that is the only time that applications are allowed to modify the contents of the message.

It is an error to attempt to enqueue or recycle a message that is already in use.

所以我们有什么

  1. 您无法回收邮件,直到它 "in use"
  2. "in use" 直到获得或创建新消息

如何解决问题

消息class里面有方法recycleUnchecked()回收消息对象甚至如果它在use.Thats你需要什么!描述:

Recycles a Message that may be in-use.

Used internally by the MessageQueue and Looper when disposing of queued Messages.

最糟糕的是它在内部使用并具有包访问权限。当您调用时它在内部使用的好东西:

handler.removeMessages(int what)

所以我想最终的解决方案是:

替换

msg.recycle();

try {
     msg.recycle(); //it can work in some situations
} catch (IllegalStateException e) {
     workerHandler.removeMessages(msg.what); //if recycle doesnt work we do it manually
}

你不应该自己调用msg.recycle(),消息在dispatched/processed之后(在你的handleMessage()returns之后)会被Looper自动回收,见source code.