由于仍在使用,此消息无法回收利用。

6
我将尝试使用这篇文章来创建异步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.。为什么会这样,如何解决并防止内存泄漏?


2
我认为你不必使用回收。 - Dishonered
这看起来是正确的。即使我开始大量发送消息,内存消耗似乎也保持稳定。 - Pitel
3个回答

6

首先,让我们看一下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);
    }

当我们试图阅读有关该标志的信息时,可以看到以下描述:
如果使用消息,则设置此标志。当消息进入队列并在传递和回收时保持设置。只有在创建或获取新消息时,应用程序才允许修改消息的内容,因此该标志仅在此时才会清除。
尝试排队或回收已在使用中的消息是错误的。
因此,我们需要知道以下内容:
1.在消息“正在使用”期间无法对其进行回收。 2.只有在获取或创建新消息时,消息才被视为“正在使用”。
如何解决问题:
在Message类中存在一个名为recycleUnchecked()的方法,即使消息正在使用也可以回收消息对象。这就是您需要的! 下面是它的描述:
回收可能正在使用的消息。
当处理排队的消息时,MessageQueue和Looper内部使用。
最坏的事情是它是内部使用的,并且只能通过包访问。但好的一点是,当您调用时它是内部使用的:
handler.removeMessages(int what)

So I guess final solution is:

replace

msg.recycle();

to

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

4

在消息被分发/处理后(即在您的handleMessage()返回后),消息会自动由Looper回收,因此您不应该自己调用msg.recycle(),请参见源代码


0
尝试使用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) {
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接