Урок 89. AsyncTask. Cancel – отменяем задачу в процессе выполнения

Обсуждение уроков
Аватара пользователя
damager82
Администратор
Сообщения: 1383
Зарегистрирован: 07 янв 2012, 11:32
Контактная информация:

Урок 89. AsyncTask. Cancel – отменяем задачу в процессе выполнения

Сообщение damager82 » 20 июн 2012, 23:00

В этом уроке:
- отменяем задачу в процессе выполнения


Click here to read this article!
Последний раз редактировалось damager82 20 май 2017, 20:11, всего редактировалось 5 раз.
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

chivorotkiv
Сообщения: 4
Зарегистрирован: 29 июн 2012, 03:14

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение chivorotkiv » 15 июл 2012, 05:51

Здравствуйте.

Хочу внести ясность в этот кусок текста:
08:17:51.956: D/myLogs(487): Begin
08:17:52.993: D/myLogs(487): isCancelled: false
08:17:53.998: D/myLogs(487): isCancelled: false
08:17:54.543: D/myLogs(487): cancel result: true
08:17:54.552: D/myLogs(487): Cancel
08:17:55.042: D/myLogs(487): isCancelled: true
08:17:56.061: D/myLogs(487): isCancelled: true
08:17:57.111: D/myLogs(487): isCancelled: true
Не знаю, как так получилось. Потому что, если запустить на четвёртом Андроиде, то будет так:
07-15 09:33:23.548: D/myLogs(5474): Begin
07-15 09:33:24.569: D/myLogs(5474): isCancelled: false
07-15 09:33:24.869: D/myLogs(5474): cancel result: true
07-15 09:33:25.570: D/myLogs(5474): isCancelled: true
07-15 09:33:26.581: D/myLogs(5474): isCancelled: true
07-15 09:33:27.582: D/myLogs(5474): isCancelled: true
07-15 09:33:28.583: D/myLogs(5474): isCancelled: true
07-15 09:33:28.583: D/myLogs(5474): Cancel
Разберёмся, почему так и должно быть.
В документации видим два метода:

onCancelled() - This method is invoked by the default implementation of onCancelled(Object)
onCancelled(Object) - Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.
The default implementation simply invokes onCancelled() and ignores the result. If you write your own implementation, do not call super.onCancelled(result).

Иными словами, onCancelled(Object) вызывамется вместо onPostExecute(Object) если был вызван метод отмены. А onCancelled() - лишь дефолтное поведение метода onCancelled(Object). К слову добавим, что параметр - это результат работы doInBackground, как и в onPostExecute.

Проверяем нашу догадку. Чуть модифицируем ваш код:

Код: Выделить всё

@Override
protected void onCancelled (Void result)
{
	    	Log.d(LOG_TAG, "onCancelled(Void) start");
	    	super.onCancelled(result);
	    	Log.d(LOG_TAG, "onCancelled(Void) finish");
}
	    
@Override
protected void onCancelled()
{
	      super.onCancelled();
	      tvInfo.setText("Cancel");
	      Log.d(LOG_TAG, "Cancel");

}
Т.е. мы оставили дефолтное поведение метода onCancelled(Void), но отслеживаем его начало и конец выполнения.

Смотрим, что получается:
07-15 09:47:04.509: D/myLogs(6027): Begin
07-15 09:47:05.530: D/myLogs(6027): isCancelled: false
07-15 09:47:06.541: D/myLogs(6027): isCancelled: false
07-15 09:47:07.342: D/myLogs(6027): cancel result: true
07-15 09:47:07.542: D/myLogs(6027): isCancelled: true
07-15 09:47:08.543: D/myLogs(6027): isCancelled: true
07-15 09:47:09.554: D/myLogs(6027): isCancelled: true
07-15 09:47:09.554: D/myLogs(6027): onCancelled(Void) start
07-15 09:47:09.554: D/myLogs(6027): Cancel
07-15 09:47:09.554: D/myLogs(6027): onCancelled(Void) finish
Видим, что родительский метод super.onCancelled(Void) вызывает наш метод onCancelled(), т.е. ведёт себя ровно так, как написано в документации.

Аватара пользователя
damager82
Администратор
Сообщения: 1383
Зарегистрирован: 07 янв 2012, 11:32
Контактная информация:

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение damager82 » 17 июл 2012, 10:56

Я запускал на Android 2.3.3. Значит, переписали они AsyncTask в новых версиях.
Спасибо за инфу! Попробую как-нить отметить этот нюанс в уроке.
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

EvilAngel
Сообщения: 28
Зарегистрирован: 16 апр 2013, 23:55

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение EvilAngel » 09 дек 2013, 16:46

Добрый день!
Надеюсь тема ещё жива.

Не мог определиться, в ветку форума для какого урока поместить мой вопрос, потому продублировал его и в ветке 90 урока.

Хотел проверить работу AsyncTask совместно с Handler и Runnable и вошел в стопор. Суть моих двух проблем в ниже представленном коде такова:
  • Почему при запуске задачи на экран сразу же выводится моё Toast-сообщение из onPostExecuted() ("AsyncTask has been stopped by setting CancelFlag"), тогда как AsyncTask работу свою ещё не завершил (о чём говорит циклически выполняющийся Log.d("MY_TEST_MESSAGE","atWorker still is working!");)?
  • Почему нет никакой реакции на вызов метода MyTask.cancel(false) и т.о. всё, что в onCancelled() не обрабатывается?
MainActivity.java
[syntax=java5]package com.mytextproject.test;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

private int myInteger = 0;
private boolean myBoolean = true;
private TextView txtResult;
private String startTag ="<html>";
private String endTag ="</html>";
private String title1 = "The variables from the main thread is set <b>via Increase Variable button</b>:";
private String title2 = "The variables from the main thread is set <b>via publishProgress() in the AsyncTask</b>:";
private MyAsyncTask TestTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

final TextView txt = (TextView)findViewById(R.id.maintextview);
txtResult = (TextView)findViewById(R.id.resulttextview);
txt.setText(Html.fromHtml(title1));
txtResult.setText(Html.fromHtml(title2));

//Меняем значения отображаемых переменных
Button myButtonIncreaseVariables = (Button)findViewById(R.id.increasevariables);
myButtonIncreaseVariables.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
++myInteger;
myBoolean=!myBoolean;
txt.setText(Html.fromHtml(startTag+title1+new String("<br>\tmyInteger="+myInteger+"<br>\tmyBoolean="+myBoolean)+endTag));
}
});

//Стартуем задачу
Button myButtonStart = (Button)findViewById(R.id.starttask);
myButtonStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(TestTask != null){
if( TestTask.CancelFlag == true )
{
TestTask = new MyAsyncTask();
Log.d("MY_TEST_MESSAGE","Before execution, getStatus()="+TestTask.getStatus().toString());
TestTask.execute();
Log.d("MY_TEST_MESSAGE","After the execution has been started, getStatus()="+TestTask.getStatus().toString());
}
}
else{
Log.d("MY_TEST_MESSAGE","TestTask=null");
TestTask = new MyAsyncTask();
Log.d("MY_TEST_MESSAGE","Before execution, getStatus()="+TestTask.getStatus().toString());
TestTask.execute();
Log.d("MY_TEST_MESSAGE","After the execution has been started, getStatus()="+TestTask.getStatus().toString());
}
}
});

//Завершаем задачу
Button myButtonStop = (Button)findViewById(R.id.stoptask);
myButtonStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TestTask.cancel(false);
}
});

//Устанавливаем наш флаг, контролирующий завершение выполнения задачи
Button myButtonSetStopFlag = (Button)findViewById(R.id.setstopflag);
myButtonSetStopFlag.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TestTask.CancelFlag = true;
}
});
}

private class MyAsyncTask extends AsyncTask<Void,Void,Void>{

boolean CancelFlag = false;

Handler atHandler = new Handler();

Runnable atWorker = new Runnable(){
public void run() {
Log.d("MY_TEST_MESSAGE","atWorker still is working!");
printText();
};
};

void printText(){
if(!this.isCancelled() && !CancelFlag){
publishProgress();
atHandler.post(atWorker);
}
else{
atHandler.removeCallbacks(atWorker);
}
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected Void doInBackground(Void... params) {
printText();
publishProgress();
return null;
}

@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
txtResult.setText(Html.fromHtml(startTag+title2+new String("<br>\tmyInteger="+myInteger+"<br>\tmyBoolean="+myBoolean)+endTag));
}

@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
publishProgress();
Toast.makeText(getApplicationContext(), "AsyncTask has been stopped by setting CancelFlag", 3000).show();
}

@Override
protected void onCancelled() {
super.onCancelled();
CancelFlag = true;
Log.d("MY_TEST_MESSAGE","CancelFlag="+CancelFlag);
}
}
}[/syntax]

main.xml:
[syntax=xml]<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:id="@+id/maintextview"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:background="@drawable/back" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center" >

<Button
android:id="@+id/starttask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Task" />

<Button
android:id="@+id/stoptask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Task" />
</LinearLayout>

<Button
android:id="@+id/setstopflag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Set Stop Flag" />

<Button
android:id="@+id/increasevariables"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Increase Variables" />

<TextView
android:id="@+id/resulttextview"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:background="@drawable/back" />

</LinearLayout>[/syntax]

back.xml:
[syntax=xml]<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="#ffffff" />
<stroke android:width="1dip" android:color="#4fa5d5"/>
</shape>[/syntax]
Буду благодарен любым ответам.

EvilAngel
Сообщения: 28
Зарегистрирован: 16 апр 2013, 23:55

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение EvilAngel » 09 дек 2013, 20:22

Посредством дебагинга я в принципе ответил на оба своих вопроса.
EvilAngel писал(а):
  • Почему при запуске задачи на экран сразу же выводится моё Toast-сообщение из onPostExecuted() ("AsyncTask has been stopped by setting CancelFlag"), тогда как AsyncTask работу свою ещё не завершил (о чём говорит циклически выполняющийся Log.d("MY_TEST_MESSAGE","atWorker still is working!");)?
  • Почему нет никакой реакции на вызов метода MyTask.cancel(false) и т.о. всё, что в onCancelled() не обрабатывается?
Т.е., после вызова TestTask.execute(); мы попадаем в doInBackground(), где входим в тело метода printText();, в котором, проверив выполнимость условий, инициируем onProgressUpdate(), затем отправляем сообщение на запуск atWorker, выходим из printText();, опять инициируем onProgressUpdate() и затем попадаем в onPostExecute(), после отработки которого, наша задача имеет статус FINISHED.

Однако, теперь не ясно где выполняется запущенное повторяющееся событие?
И, можно ли не беспокоиться об какой-либо утечке (не только в данном случае, но и для любого другого повторяющегося кода), когда стопирующий флаг CancelFlag принимает значение true для обрыва процесса повторения?

И со вторым вопросом остаётся неясность, почему на Android 2.3.3 ввиду немедленного завершения doInBackground() метод TextTask.cancel(false), как теперь понятно ожидаемо, возвращает false, а, например, на Android 4.2 хотя метод TextTask.cancel(false) и возвращает false и в onCancelled() мы не попадаем, но задача может быть остановлена не только через флаг CancelFlag, но и этим методом?

Аватара пользователя
altwin
Сообщения: 1951
Зарегистрирован: 13 ноя 2013, 14:46

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение altwin » 09 дек 2013, 20:43

EvilAngel писал(а):Посредством дебагинга я в принципе ответил на оба своих вопроса.
EvilAngel писал(а):
  • Почему при запуске задачи на экран сразу же выводится моё Toast-сообщение из onPostExecuted() ("AsyncTask has been stopped by setting CancelFlag"), тогда как AsyncTask работу свою ещё не завершил (о чём говорит циклически выполняющийся Log.d("MY_TEST_MESSAGE","atWorker still is working!");)?
  • Почему нет никакой реакции на вызов метода MyTask.cancel(false) и т.о. всё, что в onCancelled() не обрабатывается?
Т.е., после вызова TestTask.execute(); мы попадаем в doInBackground(), где входим в тело метода printText();, в котором, проверив выполнимость условий, инициируем onProgressUpdate(), затем отправляем сообщение на запуск atWorker, выходим из printText();, опять инициируем onProgressUpdate() и затем попадаем в onPostExecute(), после отработки которого, наша задача имеет статус FINISHED.

Однако, теперь не ясно где выполняется запущенное повторяющееся событие?
И, можно ли не беспокоиться об какой-либо утечке (не только в данном случае, но и для любого другого повторяющегося кода), когда стопирующий флаг CancelFlag принимает значение true для обрыва процесса повторения?

И со вторым вопросом остаётся неясность, почему на Android 2.3.3 ввиду немедленного завершения doInBackground() метод TextTask.cancel(false), как теперь понятно ожидаемо, возвращает false, а, например, на Android 4.2 хотя метод TextTask.cancel(false) и возвращает false и в onCancelled() мы не попадаем, но задача может быть остановлена не только через флаг CancelFlag, но и этим методом?
уже отвечал в теме, которую вы дублируете, но наверно стоит повторить, что нет в вашем коде onProgressUpdate() о котором вы пишете во втором сообщении. И из того, что вы говорите не понятно ничего - лучше покажите что выводится в консоль при компиляции.... и желательно в какую -то одну тему ;)
Изображение

EvilAngel
Сообщения: 28
Зарегистрирован: 16 апр 2013, 23:55

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение EvilAngel » 10 дек 2013, 02:05

altwin писал(а):уже отвечал в теме, которую вы дублируете, но наверно стоит повторить, что нет в вашем коде onProgressUpdate() о котором вы пишете во втором сообщении. И из того, что вы говорите не понятно ничего - лучше покажите что выводится в консоль при компиляции.... и желательно в какую -то одну тему ;)
Он есть - третьим методом с конца листинга (в теме к 90-ому уроку я расписал).

ShamanDS
Сообщения: 1
Зарегистрирован: 08 янв 2014, 16:05

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение ShamanDS » 08 янв 2014, 16:09

Здравствуйте! Спасибо за уроки, очень точно и понятно все описано)

Появился вопрос. Допустим, в doInBackground выполняется всего один метод из внешней библиотеки. Как в таком случае его прервать, по-логике такие моменты должны были быть предусмотрены? Или я не прав?

Аватара пользователя
damager82
Администратор
Сообщения: 1383
Зарегистрирован: 07 янв 2012, 11:32
Контактная информация:

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение damager82 » 08 янв 2014, 18:41

ShamanDS писал(а):Здравствуйте! Спасибо за уроки, очень точно и понятно все описано)

Появился вопрос. Допустим, в doInBackground выполняется всего один метод из внешней библиотеки. Как в таком случае его прервать, по-логике такие моменты должны были быть предусмотрены? Или я не прав?
Если внешний метод поддерживает остановку по interrupt, то он остановится при cancel(true). А если не поддерживает, то, думаю, что никак.
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

frostegater
Сообщения: 11
Зарегистрирован: 29 янв 2013, 15:14

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение frostegater » 28 май 2014, 10:31

Есть проблема

[syntax=java5]
static class blueServer
{
public static void setup()
{
// запуск сервера (ожидание подключения)
try
{
Log.d(TAG, myAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE? "Connectable|Discoverable" : "Unconnectable");

acceptServerThread = new AsyncTask<Void, Void, Void>()
{
BluetoothServerSocket btserver = myAdapter.listenUsingRfcommWithServiceRecord("AppToServer", uuid);
BluetoothSocket serverSocket;
@Override
protected Void doInBackground(Void... params)
{
try
{
Log.d(TAG, "MakeBluetoothConnection: onResume() : Server accepting");
serverSocket = btserver.accept();
Log.d(TAG, "MakeBluetoothConnection: onResume() : Server accepted, starting activity");
Socket = serverSocket;

//Intent intent = new Intent(MakeBluetoothConnection.this, AppsActivity.class);
//startActivity(intent);

} catch (IOException e)
{
Log.d(TAG, "Accept connection error: " + e.getMessage());
}
return null;
}

@Override
protected void onCancelled()
{
Log.d(TAG, "MakeBluetoothConnection: onResume() : Cancel accepting");

if(!serverSocket.isConnected())
try
{
serverSocket.close();
btserver.close();
}
catch (IOException e)
{
Log.d(TAG, "MakeBluetoothConnection: onResume() : Cancel accepting error : " + e.getMessage());
}

}
};
acceptServerThread.execute();
}
catch(IOException e)
{
Log.d(TAG, "Server starting error: " + e.getMessage());
}
}

public static void stop()
{
if(!acceptServerThread.isCancelled() && acceptServerThread != null) acceptServerThread.cancel(false);

}
}
[/syntax]

Вызываю close с аргументом true, потому что постоянно проверять isCancelled не могу, так как btserver.accept() вешает поток. Поток в таком случае не прерывается. Как поступить в этой ситуации?
Может создать Thread для проверки? :lol:


Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение KamiSempai » 28 май 2014, 14:01

Проблема не в AsynckTask, нечего на него гнать.
Проблема в BluetoothServerSocket. btserver.accept() нельзя прервать остановкой потока. Нужно вызывать close().
Должно помочь добавление следующего метода в AsynckTask:
[syntax=java]public void cancelAndCloseSocket() {
cancel();
btserver.close();
}[/syntax]
Естественно, безымянным классом AsynckTask, как в вашем коде, так не сделать. Нужно будет описывать свой класс MyAsynckTask и в него добавлять этот метод.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение Mikhail_dev » 28 май 2014, 14:44

Я просто код не читал, увидел только про отмену работы AsynckTask и предположил что речь идет про сеть =)

Аватара пользователя
FastRus1804
Сообщения: 49
Зарегистрирован: 22 июн 2014, 11:20

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение FastRus1804 » 20 авг 2014, 12:31

А можно не отменить, а поставить на паузу AsynckTask ?

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение KamiSempai » 20 авг 2014, 14:31

FastRus1804 писал(а):А можно не отменить, а поставить на паузу AsynckTask ?
Можно поставить на паузу поток, что его выполняет. Сделать это можно одним из способов:
1) Thread.sleep(1000);
2) someObject.wait();
Как этим пользоваться читайте в любой книге по Java, глава про потоки.

Только учтите, остановка потока может повлиять на работу остальных AsynckTask-ов.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.


Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение KamiSempai » 20 авг 2014, 15:19

Mikhail_dev писал(а):Вот не надо так делать никогда с асинкТасками
Я иногда так делаю. Только нужно понимать к чему это может привести. Об этом я предупредил :).
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение Mikhail_dev » 20 авг 2014, 16:13

Это может привести не только к тому, а к паузе потока, откуда этот асинкТаск вызывается, а это обычно UI поток.

Аватара пользователя
FastRus1804
Сообщения: 49
Зарегистрирован: 22 июн 2014, 11:20

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение FastRus1804 » 20 авг 2014, 18:46

Mikhail_dev писал(а):Это может привести не только к тому, а к паузе потока, откуда этот асинкТаск вызывается, а это обычно UI поток.
А почему так происходит?

Аватара пользователя
KamiSempai
Сообщения: 1339
Зарегистрирован: 17 фев 2012, 21:23
Откуда: Мордор

Re: Урок 89. AsyncTask. Cancel – отменяем задачу в процессе

Сообщение KamiSempai » 21 авг 2014, 12:07

Mikhail_dev писал(а):Это может привести не только к тому, а к паузе потока, откуда этот асинкТаск вызывается, а это обычно UI поток.
Это случится если его так остановить в любом другом методе отличном от doInBackground. Если останавливать в doInBackground, максимум, что может произойти, остановка очереди, в результате чего остальные таски не запустятся пока этот не завершит свою работу.
R.id.team
Хватит таскать макулатуру на тренировку! Используй T Note.

Ответить