我尝试使用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.
。为什么这样以及如何解决它并防止内存泄漏?
答案 0 :(得分:7)
首先,让我们看看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);
}
当我们尝试阅读该标志时,我们会看到描述:
如果正在使用设置信息。
当邮件入队时设置此标志并在此时保持设置 交货后及其后再生。国旗只是 在创建或获取新消息时清除,因为那是 只有允许应用程序修改内容的时间 消息。
尝试排队或回收消息是错误的 已经在使用中。
所以我们有什么
如何解决问题
Message类中有方法recycleUnchecked()
来回收消息对象甚至(如果它正在使用中)。说明你需要的东西!描述:
重新发送可能正在使用的消息。
在处理时由MessageQueue和Looper在内部使用 排队的消息。
最糟糕的是它在内部使用并具有包访问权限。当你打电话时它在内部使用的好东西:
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
}
答案 1 :(得分:1)
您不应该自己打电话给msg.recycle()
,消息在分发/处理之后(在您的handleMessage()
返回之后)会由Looper自动回收,请参阅source code。
答案 2 :(得分: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) {
}
}