AsyncTask optimization regarding internal list - java

People , I have an async task which downloads some news items using a webservice . Here is the psuedo for that .
I have an operation that i perform in an asynctask . It downloads some news from a webservice .
AsyncTask
{
AsyncTask(Context context){}
doInBackground()
{
// download some news
..
..
return List<News>
}
onpostExecute(List <News>)
{
is the activity around ? if yes then
//find the list using context ,and populate it with news
}
}
My question is that , in midst of operation if the device is oriented , we will have a new AsyncTask . Now the old one already in progress has an internal List .
Would that be garbage collected ? . I think it wont . I smell the concept of weakreferences being applied on this situation , but I can't trust my nose here .
Also by weakreferences , I may know inside onPostExecute that the activity that spawned this Async is no longer around . But . I still believe I am missing a lot .
Or is this nothing I should worry about and the List would automatically be garbage collected once the asynctask finishes ?

You have a new AsyncTask because the activity is recreated, and you are probably spawning the task in onCreate | onStart | onResume.
You could disable activity restart on config changes, but this is tricky. Just assume your activity might be restarted, and implement it so that this can be done safely.
Since you are passing the context as parameter, I'd assume the task is an static inner class or an external class in its own file.
You could try to use the "application" context instead of the "activity" context, as advised in this blog post:
http://android-developers.blogspot.com.es/2009/01/avoiding-memory-leaks.html
The other option is, as you said, have a WeakReference to the context.
But other than this, the important thing here is whether those two tasks can safely run simultaneously. Also running a task to its completion when you know the result isn't going to be consumed by the visible activity is a waste of resources. I'd cancel the current task when the activity is shutting down (onStop | onPause | onDestroy), but this is also tricky (you need to implement it with the intent of cancelling it ASAP the thread is interrupted. More info here: Android - Cancel AsyncTask Forcefully).
The most robust way to go is to use a Service instead of the AsyncTask.

There is the thread
Is AsyncTask really conceptually flawed or am I just missing something?
Briefly, an Activity is a Controller from the MVC (Model-View-Controller) viewpoint, and your data belong to Model. So just do not invoke such AsyncTasks directly from the Activity.
One more writing: AsyncTask Missteps
http://www.shanekirk.com/2012/04/asynctask-missteps/

Related

How to stop UI thread until network operation is completed

I am using volley library to perform network operations. On Application launch, I hit the service, I want to stop all the operations until i get the response from the service.
So i want to perform this synchronously. As I am using Volley which by default works in a separate thread. So how can i do this.
I have created custom Interface/listener to handle this, but does Android provide some way to achieve this.
I have done following.
Splash Activity implements an interface, and it goes to Main Activity after data is loaded
#Override
public void onContainerLoaded() {
//startActivity(MainActivity)
}
Even if you want to, you should definitely never EVER run any network-related task synchronously.
What you can do instead is starting your activity normally, and replace your layout with a progressbar logo, that is set to visibility.gone when your task is completed.
EDIT : By the way, if you are just starting your app and you haven't done anything concrete yet, I would recommend you to use an AsyncTask instead of Volley, which is often causing layer-coupling mistakes.
Use some event bus such as Otto
Create an event, make your main activity subscribe to the event using the event bus, start your operation, display a "Loading..." or something ProgressDialog in your main activity. From your worker thread when it completes send an event to your main activity. Make your main activity close the "Loading" dialog when it receives the event,
I guess a better question would be why you want to force it on the main thread?
As far as I know, volley won't let you do that but you might be able to if you make your own network operation. After Honeycomb, you will get a NetworkOnMainThreadException so you will need to override the policies.

What are the Advantages and disadvantages of AsyncTask in Android framework?

I am learning Android app development from Udacity.com by Google engineers and they said,
"It is not a good idea to use 'AsyncTask' as it is not attached to an activity life cycle. The virtual machine will hold on to the activity object as long as the Asynctask is running, even after Android has called onDestroy( ) method for the activity and expect it to be discarded.
If you rotate your phone, the behavior is to destroy the current activity and instantiate a new one. The naive AsyncTask implementation now has two threads trying to do the same update. So it is not the best pattern for a potentially very long running background operation , such as fetching from web services. If you leave the app, the asyncTask will run as long as as the process is kept alive , but will run at a lower priority, and your process will be the first thing to be killed if the device needs more resources. "
1) If using AsyncTask is disadvantageous why was it created? What would have been the design philosophy or the cause to create it in spite of having services(or something similar to achieve same kind of functionality)?
2) What are the situations where Asynctask should be used for betterment compared to Services/similar options available in Android?
3) What are the situations/places Asynctask should never be used?
Please do not downvote this question. I searched Stackoverflow and I couldn't find a similar question.
Advantages of AsyncTask
Provides generic solution for all network calls
Publish progress to UI while executing.
Run Asynchronously
Easy to maintain and read.
Problems in AysncTask
When you rotate your screen, Activity gets destroyed, so AsyncTask will not have a valid reference to publish data from onPostExecute(). In order to retain it, you need to usesetRetainState(true) if calling from fragment or onConfigChanges() if calling from activity method of an activity.
If activity gets finished, AsyncTask execution will not cancelled automatically, you need to cancel them else they will keep on running in the background.
If any exception occurs while performing network task, you need to handle them manually.
Whereas AsycTask, Services, IntentService, Threads all run on different threads and all serve different purpose.
please read more detail here.
So you need to decide when to use which component while performing non UI operations.

runOnUiThread and callbacks strange behavior

At runtime, I am using a callback pattern to have generic code run and use the callback when finished to execute code on the GUI. So in my handler I use the runOnUiThread function against the callback.
These are all async http calls. At runtime if I interrupt and use the back button and go to another fragment for example, the system will swap out the fragments and run both callbacks (the new fragment one on the correct callback, and the old callback that should have ran on the old fragment on the new current fragment). This is wrong, the new fragment gets both callbacks but when it was initialized it was assigned to the other fragment...
This makes no sense and you can only observe the behavior by switching fragments at runtime before an async call finishes. I don't see how it's possible, in the code I check if callback is null so it should have been garbage collected and how it runs on the new callback I don't know how it's possible....there's no additional assignment happening to change this. It's almost like it's just looking in memory for the function signature and running it even though when it does it's on the wrong object.
Does anyone have any clue what's going on? I've surpressed it with an ugly piece code, I just don't know how this is possible?
I understand the obvious candidates are if I assigned it again somewhere, but the answer is no. When the fragment is created it creates an object, assigns itself as the callback, and processing begins so when the fragment is destroyed it should be too. But the async task is still executing which is fine, why it does a callback on the new thread on the main gui I guess is because of runOnUiThread, so that function is somehow changing what the callback object points to in memory?
Its your code :-)
There is no magic happening 'behind the scenes' in Android and no references are changed.
Many have problems like yours, and its always because they assume that the Android system is somehow cleaning up when a fragment or activity instance is replaced or removed. It isn't, and therefore the callbacks are still executed, on a obsolete fragment or activity.
runOnUiThread takes a Runnable and runs it when the UI thread have spare time (after invoking lifecycle methods and finished rendering). Its not magic either :-)

WeakReference/AsyncTask pattern in android

I have a question regarding this simple frequently occurring situation in android .
We have a main activity , we invoke an AsyncTask alongwith the reference of the mainactivity , so that that the AsyncTask can update the views on the MainActivity.
I will break down the event into steps
MainActivity creates an AyncTask , passes its reference to it .
AysncTask , starts it's work , downloading ten files for example
The user changed the orientation of the device. This results in an orphan pointer in the AsyncTask
When the AsyncTask completes , and tries to access the activity to update the status , it crashes , because of the null pointer .
The solution for the above is to keep a WeakReference in the AsyncTask as recommended by the book "Pro Android 4"
WeakReference<Activity> weakActivity;
in method onPostExecute
Activity activity = weakActivity.get();
if (activity != null) {
// do your stuff with activity here
}
How does this resolve the situation ?
My question it , if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
What would happen to the previous AsyncTask that was initially invoked ?
Thank you , and I apologize for the length of the question .
How does this resolve the situation ?
The WeakReference allows the Activity to be garbage collected, so you don't have a memory leak.
A null reference means that the AsyncTask cannot blindly try to update a user-interface that is no longer attached, which would throw exceptions (e.g. view not attached to window manager). Of course you have to check for null to avoid NPE.
if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
Depends on your implementation, but probably yes - if you don't deliberately do something to make a repeat download unnecessary, such as caching the results somewhere.
What would happen to the previous AsyncTask that was initially invoked ?
In earlier versions of Android it would run to completion, downloading all of the files only to throw them away (or perhaps cache them, depending on your implementation).
In newer Android's I am suspicious that AsyncTask's are being killed along with the Activity that started them, but my basis for suspicion is only that the memory-leak demo's for RoboSpice (see below) do not actually leak on my JellyBean devices.
If I may offer some advice: AsyncTask is not suitable for performing potentially long running tasks such as networking.
IntentService is a better (and still relatively simple) approach, if a single worker thread is acceptable to you. Use a (local) Service if you want control over the thread-pool - and be careful not to do work on the main thread!
RoboSpice seems good if you are looking for a way to reliably perform networking in the background (disclaimer: I have not tried it; I am not affiliated). There is a RoboSpice Motivations demo app in the play store which explains why you should use it by demo-ing all the things that can go wrong with AsyncTask - including the WeakReference workaround.
See also this thread: Is AsyncTask really conceptually flawed or am I just missing something?
Update:
I created a github project with an example of downloading using IntentService for another SO question (How to fix android.os.NetworkOnMainThreadException?), but it is also relevant here, I think. It has the added advantage that, by returning the result via onActivityResult, a download that is in flight when you rotate the device will deliver to the restarted Activity.
The WeakReference class basically just prevents the JRE to increase the reference counter for the given instance.
I won't go into Java's memory management and answer your question directly: The WeakReference resolves the situation by providing the AsyncTask a way to learn if its parent activity is still valid.
The orientation change itself will not automatically restart the AsyncTask. You have to code the desired behavior with the known mechanisms (onCreate/onDestroy, onSave/RestoreInstanceState).
Concerning the original AsyncTask, I'm not 100 % sure which of these options will happen:
Either Java stops the thread and disposes the AsyncTask, because the only object holding a reference to it (the original Activity) is destroyed
Or some internal Java object maintains a reference to the AsyncTask object, blocking its garbage collection, effectively leaving the AsyncTask to finish in the background
Either way, it would be good practice to abort/pause and restart/resume the AsyncTask manually (or handing it over to the new Activity), or use a Service instead.
How does this resolve the situation ?
It doesn't.
The referent of a WeakReference is set to null when the garbage collector determines that the referent is weakly reachable. This does not happen when an activity is paused, and does not necessarily happen immediately when the activity is destroyed and the framework discards all references to it. If the GC has not run, it is entirely possible for the AsyncTask to complete while its WeakReference still contains a reference to a dead activity.
Not only that, but this approach does nothing to prevent the AsyncTask from uselessly consuming CPU.
A better approach is to have the Activity maintain a strong reference to the AsyncTask and cancel(...) it in the appropriate teardown lifecycle method. The AsyncTask should monitor isCancelled() and stop working if it is no longer needed.
If you want an AsyncTask to survive across configuration changes (but not other forms of activity destruction) you can host it in a retained fragment.

Android - How to update ArrayAdapter Asynchronously?

I'm using an AsyncTask class to add, remove and clear items from my ArrayAdapter. Unfortunately, when any of those tasks are run, the ArrayAdapter wants to notifyDataSetChanged(), which requires that the thread be run on the uiThread.
If I use runOnUiThread(new Runnable(){...}); in order to update an ArrayAdapter Asynchronously, doesn't that defeat the purpose of trying to update it in a new Thread to begin with?
What is the best approach here?
Do .notifyDataSetChanged() insde onPostExecute() method. onPostExecute() method runs on Ui-Thread. If your app requires to periodically update during execution of doInBackground() method then use publishProgress() (which will envoke onProgressUpdate() method) and call notifyDataSetChanged() inside onProgressUpdate() method, which also runs on Ui-thread.
Just to add something to what everyone else already said, after your task is done running, you might want to set the adapter (or a new one) again for the view you are working with.
AFAIK depending on your code, after notifyDataSetChanged or after refreshing the cursor, the view might not update right away if you are not working with a reference.
If you leave your activity and when you come back, the view is updated, you probably need to set the adapter again.
To answer your first question, calling runOnUiThread doesn't defeat the purpose as long as the only thing you call on the UI thread is notifyDataSetChanged(). Do all your actual, potential long running, tasks in the AsycnAdpater and then when you're ready to tell the activity things have changed, call runOnUiThread(). It will only run the commands you specify in the Runnable you pass to it.
That said, why not instead of using an AsyncTask, try using an AsyncTaskLoader. AsycnTaskLoaders were designed almost specifically for populating lists asynchronously. They weren't introduced until API level 10, but you can still access them from older API levels by using the android Support Package.

Categories

Resources