I have some problem with Android AsyncTask. There is an Activity which contains some TextView a button and a picture. When an user entered this activity I start an asynctask to check whether the user can go toward from the activity (until the task not finish the button not active). Then I want to start another asyntask to get the picture.
So I made an inner class:
AsyncTask<String, Void, JSONObject>() authTask = new AsyncTask<String, Void, JSONObject>() {
#Override
protected JSONObject doInBackground(String... params) {
//call the rest api
}
#Override
protected void onPostExecute(JSONObject result) {
// check the result
// and make another asynctask
AsyncTask<String, Void, Bitmap> imageTask = new Async.... {
// get image
}
imageTask.execute();
}
}
and I call
authTask.execute(); from the UI thread.
I have a bad feeling about this, especially it seems doesn't work (it's ok few times but suddenly it "freeze": no exception just hanging and the progress bar is spinning. Nothing happens and the button won't be active.)
There is another way to get an information and when it's finished immediately start another task?
UDPATE:
I working with api level 10. In authTask I get some information which is needed to start imageTask (some id) so I have to call these tasks in a row. In api level 10 it's is possible?
Thanks in advance!
Br, Peter
you can use getStatus() checks whether the the AsyncTask is pending, running, or finished.and when finsh start your new task.like:
if(authTask .getStatus() == AsyncTask.Status.PENDING){
// My AsyncTask has not started yet
}
if(authTask .getStatus() == AsyncTask.Status.RUNNING){
// My AsyncTask is currently doing work in doInBackground()
}
if(authTask .getStatus() == AsyncTask.Status.FINISHED){
// START NEW TASK HERE
}
example for your app:
btn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
if (authTask != null && authTask.getStatus() == AsyncTask.Status.FINISHED) {
//START YOUR NEW TASK HERE
}
else
{
//IGNORE BUTTON CLICK
}
}
});
1:
You could write the code for authTask and then for imageTask, one after the other, within a single doInBackground(). This single AsyncTask instance would be fire by a single execute() statement. This may or may not be practical depending on needed UI interactions.
2:
Edit: as noted by kabuku this information is mostly for HoneyComb+. Pre HoneyComb I would definitely go with option 1 above. executeOnExecutor() is api level 11+
In receent versions, execute() will send your AsyncTasks in series by default (ICS+). If you want to make sure this happens, specify the serial executor.
In your case this would be:
authTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
// Image task will only be done AFTER textViewTask is done
imageTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
And for newer versions a simple
...
// ICS+ and pre honeycomb (I think)
authTask.execute();
// Image task will only be done AFTER textViewTask is done
imageTask.execute();
...
From the AsycnTask.execute() documentation:
Note: this function schedules the task on a queue for a single
background thread or pool of threads depending on the platform
version. When first introduced, AsyncTasks were executed serially on a
single background thread. Starting with DONUT, this was changed to a
pool of threads allowing multiple tasks to operate in parallel. After
HONEYCOMB, it is planned to change this back to a single thread to
avoid common application errors caused by parallel execution.
PS:
To run tasks independent of each other you must use the AsyncTask.THREAD_POOL_EXECUTOR. That requires a different executor:
// Go parallel! (NOT what you want)
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Its not a good design to nest AsyncTask. Do all the heavy lifting in doInBackground and simply post/update the results. In other words, combine the processing of second AsyncTask in your first one.
From the code that you showed it does not seem to make sense to spawn second task. Just get you image inside doInBackground of the first task right after authorization.
If you need to update UI in between, you can do it in progress update.
int count;
private void attemptConnect()
{
count = 0;
str_lang = "English";
str_wait = "Plaese Wait";
new AllQuestion().execute();
}
private class AllQuestion extends AsyncTask<String, String, String> {
ProgressDialog pg;
#Override
protected void onPreExecute() {
super.onPreExecute();
pg = new ProgressDialog(LanguageActivity.this);
pg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pg.setMessage(str_wait);
pg.setCancelable(false);
pg.show();
}
#Override
protected String doInBackground(String... strings) {
try {
SoapObject soapObject = new SoapObject(AppConstant.NAMESPACE, AppConstant.QUESTION_SOAP_METHOD);
soapObject.addProperty("language", str_lang);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE se = new HttpTransportSE(AppConstant.webUrl);
se.call(AppConstant.QUESTION_SOAP_ACTION, envelope);
Object responce = envelope.getResponse();
Log.d("Question List:=>>", "" + responce);
return responce.toString();
} catch (Exception e) {
e.printStackTrace();
pg.dismiss();
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (pg.isShowing()) {
pg.dismiss();
Log.i(TAG, s);
if (s != null || !s.equalsIgnoreCase("")) {
try {
JSONArray array = new JSONArray(s);
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
String queId = obj.getString(TAG_QID);
String que = obj.getString(TAG_QUE);
String str_Opt = obj.getString(TAG_OPT);
question = new Question(queId, que, str_lang, str_catId, str_Opt, manager.getDateTime());
helper.insertQuestion(question);
}
count++;
if (count < 5) {
if (count == 1) {
str_lang = "German";
str_wait = "bitte warte einen Moment";
new AllQuestion().execute();
}
if (count == 2) {
str_lang = "Italian";
str_wait = "per favore aspetta un momento";
new AllQuestion().execute();
}
if (count == 3) {
str_lang = "Chinese";
str_wait = "请稍候";
new AllQuestion().execute();
}
if (count == 4) {
str_lang = "French";
str_wait = "patientez s'il-vous-plait";
new AllQuestion().execute();
}
Log.d("All Question:-", question.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
I have an idea to make async series in just one async task:
protected Boolean doInBackground(String... params) {
if(params[0] == "taskA") {
//do somthing
params[0] = "taskB";
}
if(params[0] == "taskB") {
//do somthing
params[0] = "taskC";
}
if(params[0] == "taskC") {
//do somthing
params[0] = "taskD";
}
if(params[0] == "taskD") {
//do somthing
return true;
}
And in your main thread just call async task like this:
ShowMyProgress(); //if you like
new MyAsyncTask().execute("taskA");
And finally you can hide your progress on onPostExecute like:
protected void onPostExecute(final Boolean success) {
if (success) {
....
HideMyProgress();
}
}
I have solved this kind of problem when i had to download something from a database before login in the user into the app, with this i fixed this problem.
To use ObservableInteger you can do this
first declare it
private ObservableInteger mObsInt;
then in your onCreate you will have a listener waiting for the values of the mObsInt to change, after those values change you can do anything you want
//Listener
mObsInt = new ObservableInteger();
mObsInt.set(0);
mObsInt.setOnIntegerChangeListener(new OnIntegerChangeListener()
{
#Override
public void onIntegerChanged(int newValue)
{
if (mObsInt.get()==1)
//Do something if the first asyncTask finishes
if (mObsInt.get()==2){
//Do something if the second asyncTask finishes, in this case i just go to another activity when both asyncTasks finish
Intent mainIntent = new Intent().setClass(LoginActivity.this, Principal.class);
startActivity(mainIntent);
finish();
}
}
});
So, how it works
ObservableInteger will be looking for changes in the variable mObsInt, so lets say if mObsInt is equal to 1 it will do something, if is equal to 2 will do another thing, so, to solve this problem with 2 asynctasks is easy, when one of the asynctasks finishes mObsInt will be equal to 1 , if the other asyncTask finishes so mObsInt will be mObsInt++ , and then your mObsInt will be equal to 2, the listener will be waiting for the values, and then do what you want to do when the values match your if statment at the onCreate method
now, just in your asynctasks just put in your onPostExecute() method this line
mObsInt.set(mObsInt.get()+1);
so if the first async finish, mObsint == 1 , if the second finish mObsInt == 2, and then you handle what you want to do in your onCreate method
hope this helps for you, it helped me
You can get more info at this doc : https://developer.android.com/reference/android/databinding/ObservableInt.html
happy coding !
Related
According to the post below:
http://techtej.blogspot.com.es/2011/03/android-thread-constructspart-4.html. It says that:
In such cases, where your application is not shutdown, but any foreground tasks have been closed or changed, all the background tasks need to know of this and try to exit gracefully
To achieve this, I am calling the cancel function on all the AsyncTask instances. Is this the right way? Also sometimes in image lazy loading, I don't keep track of all the AsyncTasks alive (and fetching images), so how to say the Android OS to cancel them too?
You can cancel AsyncTask by checking thread(AsyncTask) object's status.
private Example ex = new Example();
class Example AsyncTask<Object, Void, Void> {
#Override
protected Void doInBackground(Object... params) {
String result = null;
if(!isCancelled())
result = getHttpRestManager().send();
if(!isCancelled()) {
// some codes
}
...
return null;
}
}
public boolean cancel() {
switch(ex.getStatus()) {
case RUNNING:
case PENDING:
return ex.cancel(true);
case FINISHED:
return false;
}
return false;
}
After you cancel a thread, it's status always returns RUNNING or FINISHED. If status is not PENDING, you cannot execute thread. So you have to initialize new thread object like ex = new Example() before every .execute().
if you don't want to go back in another activity then you can use
System.exit(0);
But if there are other activities in stack,then you have to check
// AsyncTask
private Example ex = new Example();
in onDestroy method you can check it if it is running
if(ex.getStatus() == AsyncTask.Status.PENDING){
}
if(ex.getStatus() == AsyncTask.Status.RUNNING){
}
if(ex.getStatus() == AsyncTask.Status.FINISHED){
}
I am calling another class's method from doInBackground of async task.
Now i need to stop the download when cancel is called. I am not sure where to check the value of isCancelled().
class myasync extends Asynctask{
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
}
class abc()
{
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
//some text to download
}
}
}
class myclass
{
myclass()
{
myasync = new myasync();
myasync.execute("http:\\");
}
stopDownload()
{
myasync.cancel(true);
}
}
EDIT:
Have used the below solution by combining the two answers below:
1. myclass.cancel1(true);
class myclass
{
myclass()
{
myasync = new myasync();
myasync.execute("http:\\");
}
stopDownload()
{
myasync.cancel1(true);
}
}
2.
class myasync extends Asynctask{
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
cancel1()
{
abc.cancel();
}
}
3.
class abc()
{
private boolean cancel = false;
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
//some text to download
if(cancel)
break;
}
}
cancel()
{
cancel = true;
}
}
The above method is working. However the methods myclass.stopDownload() is running in UI thread , and hence myasync.cancel1() and abc.cancel() are running the UI thread. And myAsync.doInBackground() and hence abc.getURLResult() are running in seperate thread. I dont know much about inter process communication. I hope this is right thing to do.
Not very nice, but you can do something like this by adding a static variable isDownloading:
protected String doInBackground(String... sURL) {
abc = new abc();
abc.getURLResult(sURL[0])
}
}
class abc()
{
getURLResult(String URL)
{
for(int i=0; i<fp.size(); i++){
if(!myclass.isDownloading){ //ADDED
break; // or Return or handle Cancel
}
//some text to dopwnload
}
}
class myclass
{
public static boolean isDownloading; // ADDED
myclass()
{
myasync = new myasync();
isDownloading = true; // ADDED
myasync.execute("http:\\");
}
stopDownload()
{
isDownloading = false; // ADDED
myclass.cancel(true);
}
}
Update:
From the AsyncTask Cancel doc. we have to check if the async task got cancelled as you say.
Calling this method will result in onCancelled(Object) being invoked
on the UI thread after doInBackground(Object[]) returns. Calling this
method guarantees that onPostExecute(Object) is never invoked. After
invoking this method, you should check the value returned by
isCancelled() periodically from doInBackground(Object[]) to finish the
task as early as possible.
To do that send the asyncTask itself to the getURLResult as parameter along with the URL:
protected String doInBackground(String... sURL) {
new abc().getURLResult("http://...", this); // this here is the asyncTask itself.
}
getURLResult(String URL, myasync myAsyncTask)
{
for(int i=0; i<fp.size(); i++){
if(myAsyncTask.isCancelled()){
break;
}
}
}
Don't use a boolean as other suggested. it's not safe at all since another AsyncTask could be started. and it is a background threads. you can't guarantee which will check the boolean first. could cancel all AsyncTasks.
Old post:
The only place you need to check for cancellation to guarantee the cancellation! is on the onPostExecute. You can't guarantee that the async task got cancelled on calling cancel method. Therefore, you need to check whether the client application asked to cancel it and the returned data is not wanted anymore.
private boolean askedForCancellation = true;
#Override
protected void onPostExecute(Object response) {
if (!askedForCancellation)
// parse response
else
// ignore. or send message to activity to stop loading if you didn't already did that when called cancel method.
}
To achieve that add the following cancel method to the AsyncTask:
public final boolean cancel(boolean mayInterruptIfRunning) {
askedForCancellation = true;
return mFuture.cancel(mayInterruptIfRunning);
}
In your class:
myasync.cancel(true);
myasync = null;
Set myasync to null is ok. because, you can't use it anymore for execution again. you will get a runtime error. you need to re-initialise it.
To check if AsyncTask asked for cancellation. check if the value of
myasync is equal to null. remember the AsyncTask asked to get
cancelled and not cancelled because there is no guarantee that it is
going to be cancelled on calling cancel. What you do is to ignore the
response on onPostExecute
I used this approach in more than 15 applications till now. No bugs and no unexpected behaviours.
I am trying to add this code to my doInBackGround so I can catch a flag that is being set when the user presses the back button
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
This my doInBackGround and the code I use to set the flag for the asynctask to be canceled
#Override
public void onBackPressed()
{
/** If user Pressed BackButton While Running Asynctask
this will close the ASynctask.
*/
if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED)
{
mTask.cancel(true);
}
super.onBackPressed();
finish();
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
/** If Activity is Destroyed While Running Asynctask
this will close the ASynctask. */
if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED)
{
mTask.cancel(true);
}
super.onDestroy();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
if (pDialog != null)
{
if(pDialog.isShowing())
{
pDialog.dismiss();
}
super.onPause();
}
}
class LoadAllData extends AsyncTask<String, String, String> {
protected String doInBackground(String... args) {
try {
Intent in = getIntent();
String searchTerm = in.getStringExtra("TAG_SEARCH");
String query = URLEncoder.encode(searchTerm, "utf-8");
String URL = "example.com";
JSONParsser jParser = new JSONParsser();
JSONObject json = jParser.readJSONFeed(URL);
try {
JSONArray questions = json.getJSONObject("all").getJSONArray("questions");
for(int i = 0; i < questions.length(); i++) {
JSONObject question = questions.getJSONObject(i);
String Subject = question.getString(TAG_QUESTION_SUBJECT);
String ChosenAnswer = question.getString(TAG_QUESTION_CHOSENANSWER);
String Content = question.getString(TAG_QUESTION_CONTENT);
The problem comes when I try to translate isCancelled into my asynctask. I get an error under Downloader that says "Downloader cannot be resolved" I also get one under publishProgress that says "The method publishProgress(String...) in the type AsyncTask is not applicable for the argument (int)" I'm jus tasking is somebody can help put the isCancelled into my AsyncTask. I have also done my research and seen that there are more than one way to use isCancelled in your code. I have realized that dealing with isCancelled can really be a hassle for somebody new to it.
1) To resolve error one make Downloader a public static data variable of the class.
2) Please make sure
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>
is the class declaration
After you called cancel() isCancelled() will return true, and after your doInBackground returned onCancelled is executed instead of onPostExecute. The Parameter will issue an interrupt on the background thread, so your long-time operations are closed. However, I'd assume you catch that somewhere?
Hope this Helps..:)..
If it doesnt solve the error..Please post logcat details
From SDK:
Cancelling a task
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled() to
return true. After invoking this method, onCancelled(Object), instead
of onPostExecute(Object) will be invoked after
doInBackground(Object[]) returns. To ensure that a task is cancelled
as quickly as possible, you should always check the return value of
isCancelled() periodically from doInBackground(Object[]), if possible
(inside a loop for instance.)
Also please use super.onpause() etc at the begining of the function block
Refer this:- link
In this case you will need to write a separate file for your Asynctask class. also make an interface callback.
you do not need to overide the onBackPress button, you just need to set your progressDialog to cancelable(True) and set onCancelListner(). here you can set a flag isCanceld = true;
the whole scenario is defined in this link.
android asynctask sending callbacks to ui
see the answer of Dmitry Zaitsev try this one and let me know if stuck in any where
hope this helps
I am learning how to cancel asyncTask so there is no uses on the code below.
I tried to called the asyncTask and execute it then cancel it and execute it.
MyAsyncTask asyncTask = new MyAsyncTask();
Log.i("cancel","cancel 1");
asyncTask.execute();
Log.i("cancel","cancel 2");
asyncTask.onCancelled();
Log.i("cancel","cancel 3");
asyncTask.execute();
Log.i("cancel","cancel done");
"cancel 1" and "cancel 2" is executed finely as it shown on logcat but ActivityThread.performLaunchActivity error is thrown when "cancel 3" is trying to execute.
(cancel 3 is not showing on logcat) Anything wrong with my asyncTask code?
private class MyAsyncTask extends AsyncTask<String,Integer,Void>{
private ArrayList<Map<String, String>> list;
private ProgressBar progressBar;
#Override
protected Void doInBackground(String... arg0) {
progressBar = (ProgressBar)findViewById(R.id.year_expense_progressbar);
progressBar.setVisibility(View.VISIBLE);
getListView().setVisibility(View.GONE);
textView.setVisibility(View.GONE);
list = new ArrayList<Map<String, String>>();
String time = "";
String category = "";
String expense = "";
String day = "";
String month = "";
totalExpense = 0;
Cursor c = SQLLiteAdapter.fetchAllItems();
while(c.moveToNext() != false){
if(isCancelled()){
Log.e("cancel","cancel inside background");
break;
}
// if there is nothing is input, don't execute verifyLevel
if(tokenizedResult.isEmpty()) break;
category = c.getString(c.getColumnIndex(SQLLiteAdapter.col_category));
expense = c.getString(c.getColumnIndex(SQLLiteAdapter.col_price));
time = c.getString(c.getColumnIndex(SQLLiteAdapter.col_time));
day = c.getString(c.getColumnIndex(SQLLiteAdapter.col_day));
month = c.getString(c.getColumnIndex(SQLLiteAdapter.col_month));
VerifyLevel a = new VerifyLevel(tokenizedResult,category,expense,time,day,month);
if(!a.isEmpty()){
list.add(a.addToList());
}
totalExpense += a.totalExpense;
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
progressBar.setVisibility(View.GONE);
getListView().setVisibility(View.VISIBLE);
textView.setVisibility(View.VISIBLE);
fillData(list);
textView.setText("Total Expense is "+convertNumeric(totalExpense));
Log.i("yearExpense","buildList is finished");
}
#Override
protected void onCancelled(){
super.onCancelled();
list.clear();
progressBar.setVisibility(View.GONE);
totalExpense = 0;
Log.i("yearEx","Cancel asyncTask");
Toast.makeText(getApplicationContext(), "cancelled", Toast.LENGTH_SHORT).show();
}
}
My approach is slightly different and perhaps, a little lengthy. But it has always worked without any issues.
This piece of code goes in the doInBackground(). If you have a for... loop in it, then use the break; statement. If you do not use a for.... loop, remove it.
// CHECK IF THE USER HAS CANCELLED THE TASK
if (isCancelled()) {
cancelTask = true; // (OPTIONAL) THIS IS AN ADDITIONAL CHECK I HAVE USED. THINK OF IT AS A FAIL SAFE.
break; // REMOVE IF NOT USED IN A FOR LOOP
}
You already have an Asynctask declared: MyAsyncTask asyncTask = new MyAsyncTask();. Along with that, also create two instances of boolean. Call them, for example:
boolean blMyAsyncTask;
boolean cancelTask;
Now, in the onPreExecute(), toggle the status of the blMyAsyncTask instance:
blMyAsyncTask = true;
And in the onPostExecute():
blMyAsyncTask = false;
And, in the same onPostExecute(), I also do the remainder functions after checking the state of the cancelTask boolean. For example:
if (cancelTask == false) {
// THE NORMAL CODE YOU HAVE IN YOUR onPostExecute()
}
Finally, in the onDestroy() (I use this, but I suspect the onPause() could work too. Never done it in the onPause() in all honesty), check the status of the boolean blMyAsyncTask
if (blMyAsyncTask== true) {
asyncTask.cancel(true);
}
As I said at the start, it is lengthy, perhaps even complicated, but it has never failed. I also think of this as a little modular if you would. If I have more Asycntasks added to the Activity, I can add another check in the onDestroy().
you should not call asyncTask.onCancelled(); directly. You can call cancel(true) and inside your doInBackground() you check for isCancelled().
To cancel a AsyncTask call the below line.
asyncTask.cancel();
onCancelled() is a override method that is executed when ever cancel is called.
You can use either
asynctask.cancel(true);
or
asyncTask.wait();
"true " if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete
An AsyncTask is executed on click:
List<RSSItem> list = new Vector<RSSItem>();
private OnClickListener click = new OnClickListener() {
public void onClick(View view) {
list.clear();
if((dft.getStatus().toString()).equals("RUNNING")) dft.cancel(true);
currentCategory = catigoriesHolder.indexOfChild(view);
dft = new DownloadFilesTask();
dft.execute(rssFeedURL[currentCategory]);
}
};
In doInBackGround method the variable list is filled up. How to prevent the list to be cleared at point at which it is used to fill ListView. How to be sure, that on next click the previous instance of AsyncTask have been destroyed and there is no further processing of it.
The issue is regarding version 1.6.
First of all async task's in general don't run at the same moment, but the execution of the same async task is actually a queue. so imagine if you create 2 instances of your DownloadFilesTask and execute them in the same method like:
task1.execute();
task2.execute();
this means that task 2 wont be run until task1 has finished the whole onPreExecute,DoInBg,onPostExecute process so you can be sure that that won't happen simultaniously. also the taskStatus is an ENUM. you can check it as such not as a string like:
task.getStatus()==Status.FINISHED
in your case if you don't want to queue multiple tasks until the currently running one is complete then do something like this:
if(task==null || task.getStatus()!=Status.FINISHED){
task = new DownloadFilesTask();
task.execute();
}
Canceling a task means that the doInBackground will run but postExecute wont. you can check if the task isRunning in order to cancel it during bg processing somewhere also.
You can store reference to AsyncTask in member variable. So your code would look like this:
List<RSSItem> list = new Vector<RSSItem>();
DownloadFilesTask downloadTask = null;
private OnClickListener click = new OnClickListener() {
public void onClick(View view) {
list.clear();
if((dft.getStatus().toString()).equals("RUNNING")) dft.cancel(true);
currentCategory = catigoriesHolder.indexOfChild(view);
if(downloadTask == null){
downloadTask = new DownloadFilesTask();
downloadTask.execute(rssFeedURL[currentCategory]);
} else {
//show warning here
}
}
}
Of course, you'll need to set downloadTask to null in onPostExecute() for this to work.
As an added benefit you now can cancel outstanding task if Activity is being destroyed:
#Override
onDestroy() {
if(downloadTask != null) {
downloadTask.cancel();
}
}
Which you should do anyway.