Страница 1 из 2

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

Добавлено: 20 июн 2012, 23:00
damager82
В этом уроке:
- отменяем задачу в процессе выполнения


Click here to read this article!

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

Добавлено: 15 июл 2012, 05:51
chivorotkiv
Здравствуйте.

Хочу внести ясность в этот кусок текста:
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(), т.е. ведёт себя ровно так, как написано в документации.

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

Добавлено: 17 июл 2012, 10:56
damager82
Я запускал на Android 2.3.3. Значит, переписали они AsyncTask в новых версиях.
Спасибо за инфу! Попробую как-нить отметить этот нюанс в уроке.

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

Добавлено: 09 дек 2013, 16:46
EvilAngel
Добрый день!
Надеюсь тема ещё жива.

Не мог определиться, в ветку форума для какого урока поместить мой вопрос, потому продублировал его и в ветке 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]
Буду благодарен любым ответам.

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

Добавлено: 09 дек 2013, 20:22
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, но и этим методом?

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

Добавлено: 09 дек 2013, 20:43
altwin
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() о котором вы пишете во втором сообщении. И из того, что вы говорите не понятно ничего - лучше покажите что выводится в консоль при компиляции.... и желательно в какую -то одну тему ;)

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

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

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

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

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

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

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

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

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

Добавлено: 28 май 2014, 10:31
frostegater
Есть проблема

[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:

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

Добавлено: 28 май 2014, 11:21
Mikhail_dev
Статья о темной стороне AsynckTask http://emunix.org/posts/the-dark-side-of-asynctask/
Лично я перешел на либу Volley и не знаю проблем http://arnab.ch/blog/2013/08/asynchrono ... ng-volley/

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

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

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

Добавлено: 28 май 2014, 14:44
Mikhail_dev
Я просто код не читал, увидел только про отмену работы AsynckTask и предположил что речь идет про сеть =)

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

Добавлено: 20 авг 2014, 12:31
FastRus1804
А можно не отменить, а поставить на паузу AsynckTask ?

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

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

Только учтите, остановка потока может повлиять на работу остальных AsynckTask-ов.

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

Добавлено: 20 авг 2014, 14:34
Mikhail_dev
Вот не надо так делать никогда с асинкТасками

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

Добавлено: 20 авг 2014, 15:19
KamiSempai
Mikhail_dev писал(а):Вот не надо так делать никогда с асинкТасками
Я иногда так делаю. Только нужно понимать к чему это может привести. Об этом я предупредил :).

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

Добавлено: 20 авг 2014, 16:13
Mikhail_dev
Это может привести не только к тому, а к паузе потока, откуда этот асинкТаск вызывается, а это обычно UI поток.

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

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

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

Добавлено: 21 авг 2014, 12:07
KamiSempai
Mikhail_dev писал(а):Это может привести не только к тому, а к паузе потока, откуда этот асинкТаск вызывается, а это обычно UI поток.
Это случится если его так остановить в любом другом методе отличном от doInBackground. Если останавливать в doInBackground, максимум, что может произойти, остановка очереди, в результате чего остальные таски не запустятся пока этот не завершит свою работу.