Реализация сокета Android с принудительным закрытием AsyncTask

#java #android #sockets #android-asynctask

#java #Android #сокеты #android-asynctask

Вопрос:

Я сталкиваюсь с проблемой при попытке отправить «сигнал» моему AsyncTask классу, чтобы остановить выполнение и закрыть соединение с сокетом. В doInBackground способе я настраиваю соединение с сокетом, отправляю первый пакет полезной нагрузки и ожидаю входящих пакетов:

 mRunning = true;

        try {
            byte[] data = null;

            mSocket = mTlsSocketFactory.createSocket(mTlsSocketFactory.getServerAddress(), mTlsSocketFactory.getServerPort());
            LogHelper.printLogMsg("INFO socket created");

            out = new DataOutputStream(mSocket.getOutputStream());
            out.flush();
            inStream = new DataInputStream(mSocket.getInputStream());

            //send authenticate payload
            requestSendPayload(authenticatePayload, params);

            while (mRunning) {
                int type = inStream.readInt();
                type = Integer.reverseBytes(type);
                mResID = type;
                int length = inStream.readInt();
                length = Integer.reverseBytes(length);
                if (length > 0) {
                    data = new byte[length];
                    inStream.readFully(data);
                    publishProgress(data);
                }
                data = null;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) out.close();
                if (inStream != null) inStream.close();
                if (mSocket != null) mSocket.close();
            } catch (IOException e) {
                e.fillInStackTrace();
            }
        }
  

Когда я получаю пакет, который я хочу, я должен закрыть соединение. У меня есть открытый метод внутри AsyncTask класса:

 public void close() {
   mRunning = false;
}
  

Но проблема в том, что блок ‘while’ никогда не заканчивается и doInBackground никогда не завершался.
Есть много сообщений с подобной проблемой, но я пытался вызвать cancel (true) на моем, AsyncTask но без результата — doInBackground так и не закончил. Мой вопрос в том, как отправить ‘signal’ в doInBackground метод, чтобы мой цикл while мог завершиться?

Я могу переписать closeMethod на что-то вроде этого:

 new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (out != null) out.close();
                    if (inStream != null) inStream.close();
                    if (mSocket != null) mSocket.close();
                } catch (IOException e) {
                    e.fillInStackTrace();
                }
            }
        }).start();
  

И после этого я перехватываю исключение при попытке прочитать: DataInputStream.readInt() а затем doInBackground завершится. Но я не уверен, что это правильное решение.

Ответ №1:

Вы должны реализовать onCancelled в своем методе async task

 @Override
protected void onCancelled() {
   mRunning = false;
}
  

после этого вы просто вызываете

 mySocketAsyncTaskObject.cancel(false);
  

Другим вариантом было бы удалить ваше свойство mRunning и переопределить onCancelled и просто проверить наличие isCancelled()

 while (!IsCancelled()) {
                int type = inStream.readInt();
                type = Integer.reverseBytes(type);
                mResID = type;
                int length = inStream.readInt();
                length = Integer.reverseBytes(length);
                if (length > 0) {
                    data = new byte[length];
                    inStream.readFully(data);
                    publishProgress(data);
                }
                data = null;
            }
  

В качестве личного замечания я хочу добавить, что хорошей практикой является использование класса Service для инкапсуляции функциональности вашего сокета и использования простого объекта Thread, а не AsyncTask

Комментарии:

1. Спасибо за ответ, но первое решение не работает, потому что onCancelled никогда не вызывается. Из документации: «Вызов метода cancel() приведет к вызову onCancelled(Object) в потоке пользовательского интерфейса после возврата doInBackground(Object[])». Мой doInBackground никогда не заканчивается, поэтому onCancelled не вызывается. Я пробовал со вторым решением, но, похоже, это тоже не работает — я вызываю: myAsyncTaskObj.cancel(true); но doInBackground так и не был завершен. И да, я читал, что класс AsyncTask не такое уж хорошее решение, и, возможно, я подумываю о повторной реализации этого.

2. попробуйте передать false в метод cancel, таким образом, поток не будет прерван, и isCancelled() должен вернуть true

3. Нет, к сожалению, ничего не происходит.