How should you handle AsyncTasks when configuration changes occur. I am accessing a ReSTful API through these tasks, and in the cases where I need to get data back and then display it to the user I am experiencing problems. If a configuration change occurs, like an orientation change then the Activity is not properly update. I believe that the AsyncTask is updating the old activity that has been destroyed. I have found a solution to this that requires creating a boolean in the Application class that keeps track of whether the AsyncTask I care about is still running, and then on rotations I cancel the listener then recreate it in the new Activity, then when the AsyncTask is done, the listener then displays the data to the user. This works well, but it requires lots of extras and requires a lot of management. Is there a way that the async task can be tied to the new instance of the activity. Or a way to abstract what I have done, so it does not require as much management for each task you have.
Thanks
Have a look at asynctask-screen-rotation. This implementation is working quite good for me.
I struggled with that a lot too, the best solution I found was to add android:configChanges="orientation" to your activity in the AndroidManifest.xml file. This will stop your activity from being destroyed and recreated when the orientation changes.
Related
I have my MainActivity which gives the user a selection of pages to open, all of which involve downloading some data from the internet and displaying it. To save the user waiting when they choose their page I've made an AsyncTask as a subclass of MainActivity which produces an object DATAwhen the download is complete.
How would I pass DATA on to the SecondActivity in the following circumstances:
The user chooses the SecondActivity before the AsyncTask download has completed.
The download completes before the user chooses the SecondActivity.
the AsyncTask doesn't have to be a sub-class of MainActivity its just been tidy to do it that way so far,
thanks for the help!
Here's one way to do this:
Create a reference to your data in your Application. The Android Application is a good place to store global data. Next, populate the data via your AsyncTask (Watch out for the pitfalls of using an AsyncTask). You can now access your data via a call similar to this: ((MyApplication)getApplication).mydata
As you mentioned, two scenarios can come up. Either the data has been populated, or not. To handle this, use an observer that observes changes to the data. Have SecondActivity register as an observer when the data is null. When the data is available your SecondActivity's update method will get called and you can do whatever you please with it. Finally, make sure to unregister from being an observer.
Hope this helps.
Passing information directly between activities works only if it is Parcellable (via Intent). Almost anything could be made Parcellable but it is not always a good idea especially when the amount of data is large.
The next problem is that your AsyncTask most likely keeps the Context of your first activity alive when it is running longer than the activity lasts. Activity instances are quite often recreated when you rotate the device and naive implementations tend to start another asynctask in the new instance and end up with multiple tasks that download the same data. You would need to pass the reference of a running task between instances of the same Activity.
The simplest solution is probably to create a singleton (or a Service) accessible from both activities that hosts the AsyncTask & loads the data. If it requires a Context use getApplicationContext() since that's safe to use outside the lifetime of Activites.
Activities could register themselves as listeners for "data loaded" events while they are active.
I've recently struggled with AsyncTask and had difficulty having the UI behave while the task was running in the background. While there are comments around that services aren't really appropriate for the sort of thing you're describing, I've found them much easier to work with. You might check intentService as a middle ground. Good tut's can be found here and, specifically concerning intentService, here.
Im trying to use the pattern of Activity-Service-Messenger to comunicate my Activity and Service. (like explained here http://viktorbresan.blogspot.mx/2012/09/intentservice-and-inter-process.html) Basically it says that i should create a Handler inside my Activity, and then create a Messenger and send that via putExtra() to my Service. The Service would then post messages to the activity ussing the Messenger.
My problem is that if i rotate the emulator, the Handler associated with the Messenger holds a reference to a destroyed activity. This causes not to refresh the interface of the new activity. I tried to put Messenger in onSaveInstanceState(). Eventought i can save the Messenger, the Handler is still referencing my past activity and i cant find a way to retrieve my Handler from the Messenger to set the new activity.
Edit:
Im avoiding to using:
android:configChanges="orientation|keyboardHidden"
onRetainNonConfigurationInstance()
Edit:
I used HalR idea of using a singleton and keep the handler there. It works really good, althought i can see that this pattern implies a careful cleaning of the references on the singleton.
Finally im also testing on the idea of using Activity-Service that was commented by Hoan Nguyen
I'm not sure that its appropriate for this case, but there are many people who have been frustrated by losing their activity when it rotates, or having to set complex stuff up every time they get a new activity.
Some people will create singletons that they use for referencing, then keep the Handler in there.
Others will extend the application class and put stuff in there. If you have a lot of complex things you are wanting to set up once, those are techniques you can use.
Keeping your app fluid and your making your activities independent of one another is a better overall philosophy, so its best to avoid anything global, but sometimes you gotta do what you gotta do.
Rotating the device at least pauses and resumes your activity according to the lifecycle. I think you are aware of the consequences.
Maybe stopping and starting a new service is the only right solution here. i worked as well with global states, but it will just always be easier when, you make every activity independent like a "single application".
edit: ok it's a messenger service... so stopping and starting is not a solution. so maybe you can register and unregister your messengers.
In the past, orientation changes and AsyncTask (and other long running background tasks) have not played well with each other. There's always been the issue of knowing what tasks (or threads) are still running in the newly created activity (from an orientation change), and what to do when a task ends while an Activity isn't attached.
Even with Fragments and the LoaderManager, this still seems to be a problem to me.
What is the preferred way these days, to manage arbitrary long running tasks and orientation changes? To know what tasks are running in the newly created activity. To make sure a task doesn't try to deliver it's information when an Activity isn't attached.
Thank you
In my program I just put
android:configChanges="orientation|keyboardHidden|keyboard"
in my activities in the manifest and be done with it. After 1 year I have had 0 problems.
As other post suggests, you can use android:configChanges=xxx.
BUT, this is not always desired. Android is designed to kill activity on configuration change, and create new one, and you may benefit from this in some situation, by providing alternative screen layout.
This makes sense for example in multi-pane app, where landscape orientation shows different views than portrait orientation.
So to return to your question: I didn't read about preferred way to handle long running operations, but from own experiences I'd suggest to store such task in persistent activity state (saved/restored in onRetainNonConfigurationInstance/onCreate), or using persistent Fragments.
If your Activity detects that some task is already running, it can give it chance to recreate dialog to show its progress.
And note: orienation change is not the only that can make your activity to be recreated. Be prepared for language change, docking, and other possibilities :) But still, orientation change is the most common one.
Its the same as it always has been: use a service. Broadcast events from your service and catch them in your activity (or some intermediate layer). Your activity can then choose what to do with those events based on its state.
This is a rather broad question.
I have a ListActivity that instantiates an AsyncTask, makes a call to a web service, and populates the ListView with the results.
How should I handle device rotation while the AsyncTask is still running? Should I cancel it, save off whatever data I need, and start a new one when the ListActivity is recreated? Does Android somehow already handle such a case?
It seems like what you require here is a service, not an Asynchtask - as you are running a long query that should persist and deliver it's results the same way regardless of orientation.
BTW, killing the Asynctask is NOT straightforward - best not to go there at all, but let a service run truly in the background.
Do you require that your ListActivity is recreated on orientation change? I would expect that your users would not want that - but would rather have the populating of the ListView carry on.
I have an app that does a series of HTTP GETs and POSTs within a string of AsyncTasks, without restarting the activity each time the orientation changes. All you need is a line similar to the following in your manifest.
android:configChanges="orientation"
See the docs at http://developer.android.com/guide/topics/manifest/activity-element.html
Torid's suggestion of overridding the config changed handler works in most situations. However, I've found that some manufacturer's devices still recreate the activity, even when you've done this (I've seen 1 HTC phone that does it so far).
The proper solution is CommonsWare's answer in the following link:
Background task, progress dialog, orientation change - is there any 100% working solution?
I've been bugged by this for a while. How do I properly handle screen orientation changes while I have a separate Thread / AsyncTask running? Currently, I have
android:configChanges="orientation|keyboard|keyboardHidden"
in my AndroidManifest.xml, but that is not really encouraged:
Note: Using this attribute should be avoided and used only as a last-resort. Please read Handling Runtime Changes for more information about how to properly handle a restart due to a configuration change.
Also, in the 2.3 emulator, it works when switching to landscape, but switching back to portrait fails.
Now, the reason why I use configChanges is because when the user switches orientation, I might have an AsyncTask running, doing some network traffic, and I don't want it stopped.
Is there any other way of doing this, or is there a way of fixing 2.3 to switch back to portrait?
I know about onRetainNonConfigurationInstance, but I'm not sure it would be a good idea to "save" the AsyncTask instance, mainly because the class that extends AsyncTask is not static (so it is tied to the Activity) -- and it needs to be, because in onPostExecute() it calls methods from the Activity instance.
I had a similar problem to your and worked around it by implementing the AsyncTask as part of a class which inherits from Application class. An Application class is available all the life time of the application So you don't have to worry about your AsyncTask getting interrupted unless the whole application will be killed.
To get notified when the task has finished the Activity has to implement a interface which it uses to register itself to the Application class.
When your application is destroyed because of the screen rotation you can unregister your Activity from the Application class and re-register it when it is recreated. If the task finishes between destruction and recreation the result of the operation can be stored in the Application class meanwhile so the Activity can check whether the task is still running or whether the result is already available when it is recreated.
Another advantage is that you have direct access to the applications context because the Application class is a sub class of the Context class.
Take a look the droid-fu library BetterAsyncTask. It is meant to handle this exact case.
http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/
I already popped up similar question here.
Basically there is an example of how to pause/resume an AsynTask on device rotation. However it still does not fit for all cases (sometimes it is not possible to safely suspend the action, such as a new user creation on a remote server). For those "unsafe" cases you need to code somewhat I'd call a tricky "framework". You will see CommonsWare gives github links to the one.