Это сообщение не может быть переработано, потому что оно все еще используется

Я пытаюсь использовать эту статью для создания асинхронного сокета 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 ответа

Решение

Ну, во-первых, давайте посмотрим, как Messagerecycle() метод работает.

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. Используется, пока не получено или не создано новое сообщение.

Как решить проблему

Есть метод recycleUnchecked() внутри класса Message для переработки объекта сообщения, даже если он используется. Вот что вам нужно! Описание этого:

Перезаписывает сообщение, которое может быть использовано.

Используется внутри 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
}

Ты не должен звонить msg.recycle() сообщение отправляется автоматически Looper после отправки / обработки (после handleMessage() возвращается), см. исходный код.

Попробуй использовать 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) {
    }
}
Другие вопросы по тегам