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

时间:2017-05-17 08:53:34

标签: android android-handler

我尝试使用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.。为什么这样以及如何解决它并防止内存泄漏?

3 个答案:

答案 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);
    }

当我们尝试阅读该标志时,我们会看到描述:

  

如果正在使用设置信息。

     

当邮件入队时设置此标志并在此时保持设置   交货后及其后再生。国旗只是   在创建或获取新消息时清除,因为那是   只有允许应用程序修改内容的时间   消息。

     

尝试排队或回收消息是错误的   已经在使用中。

所以我们有什么

  1. 您无法回复消息,直到其正在使用"
  2. 在使用中""直到获得或创建新消息
  3. 如何解决问题

    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) {
    }
}