Handling background tasks and activity configuration changes - android

Here is problem which I'm trying to solve for three days:
For example, REST client app perfoms a lot of background work (network calls) and posts result back to UI thread. It is obvious that it should be done asynchronously. Android allows to do this in several ways (for example AsyncTasks and IntentServices).
The biggest problem of running asynchronous tasks from activity is configuration change.
For example we have activity which starts download process in AsyncTask and shows ProgressDialog. After screen rotation, activity is being recreated by OS. As the result when asynctask will try to dismiss progressdialog of old (dead) activity, it will fail.
There are several solutions to this problem:
first one is to retain asynctask in worker fragment: Great post how to do that is here. Big advantage of this solution is that OS knows when to call onPostExecute() method. In some cases onPostExecute() maybe called in moment when old activity is destroyed and new one is still not created. But that doesn't happen because OS doesn't allow execution of onPostExecute() before onAttach() is called. Disadvantage is that AsyncTasks are not suitable for long term operations and behaves differently depending on OS version.
the second approach is using LocalBroadcastManager and IntentService. Here is another post which shows how to use it. What will happen if service will send message to BroadcastReceiver when it is unregistered (the moment when activity is being recreated)?
the third solution is dirty hack: declare this in mainfest android:configChanges="keyboardHidden|orientation". Is not appropriate for me.
Maybe someone knows another solution?

You can prevent screen orientations before the task with setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) and restore it after.
The othet other idea is a service. You're worried that when one orientation is closed and before the other is loaded you may miss a broadcast. In that case the service should store the data somewhere the fresh activity can load it. (SqlLite, a file, sharedPrefs)
Also note broadcastRecievers can be registered in the manifest. So always on, so to speak.

Related

Confused by asynchronous mechanism

I am confused by the fact that android is multi-threaded. If the code of a particular activity method is executing, can it be interrupted in the middle by onPause(), onStop() or onDestroy() or by another method like the onPostExecute() of an asynctask?
Edit 1
This question is not about activities life-cycle. What i am really asking is if the onPause() method can interrupt the onClick() method (just an example) and i would like to read more about how exactly android manages activities, calls their async methods etc...
Edit 2
Ok, it seems that i have found the relevant information i needed in the developer docs (don't know if it is explained better somewhere else): Threading Performance:
Internals
The main thread has a very simple design: Its only job is to take and
execute blocks of work from a thread-safe work queue until its app is
terminated. The framework generates some of these blocks of work from
a variety of places. These places include callbacks associated with
lifecycle information, user events such as input, or events coming
from other apps and processes. In addition, app can explicitly enqueue
blocks on their own, without using the framework.
It states that callbacks relative to activity lifecycle, user events such as input and other code are all managed using a "thread-safe work queue". This is central to understanding Android asynchronous programming. It explains a lots of things, such as why onClick() will be never be interrupted by activity onPause(), or why onClick() will be never be interrupted by a runnable posted using a Handler object (allocated in the main thread). So, for example, the onPostExecute() of an AsyncTask cannot interrupt the onClick() method (or onStart(), onResume(), onPause() etc...). As a novice android programmer, it was a bit confusing at first.
According to your question, I think you need to understand basic about Android Application's life cycle.
Basically, the life cycle deals with the state of application in different different situation.
Like what will be state when application goes, in foreground or background and all that So These cycles state that you had mentioned like onPause(),onDestroy(), onStart(), onCreate().
Now talk for AsynchTask(), If you want to execute long running process like downloading image or listening musing or any other then we will use BackGround services. So don't get confusion between life cycle method and background services. .
Third, App will close only some rare condition like if memory is not sufficient or some other fatal issue occur in App then only it will terminate.
So, findings are that Thread is meant for long running process and Activity Life Cycles method are meant for various activity state
Please follow this tutorial and I hope you will get clarification
This is a good question to ask I think. However, Here is my understanding:
When an Application starts, a Dalvik Virtual Machine (DVM) is assigned to it and works separately. So no two Applications interrupt each other as each of the application is managed by separate DVM.
Coming to Activity, it is a component of Android and runs on main thread. Android OS manages priority level to it's components. It gives high priority level to Activity, Services and may be other component(s). The OS maintains the non interruption behaviour of main thread.
Further when you run an AsyncTask, it is a long running task in background but dependent on Application context. So the OS does not guarantee to manage it.
Now regarding activity life cycle. As #nihal_softy said, activity has it's own life cycle which start from onCreate() and encounters onStart(), onResume(), onPause(), onStop() and onDestroy(). You can call these methods from anywhere of your Activity or from an AsyncTask and it will called but does not mean that the Activity will go in background or will destroy. In a simpler word, if you call onPause() from an AsyncTask, it does not mean that the Activity will be sent to background (if in foreground). Because when an Activity will go in background then it calls it's life cycle methods such as onPause() and onStop() as callback. But converse is not true.
I hope my understanding will help you to get the answer.

Async task stopped when App terminates (onDestroyed is called)

I have an activity in which I do server sync with a back end server using a subclass of asyctask.
Given it is a network task and might take couple of seconds. I am afraid of the following scenario to take place.
The activity starts, and whenever the asynctask should start to run, it does so.
The onPrexecute() is called, executed, and over. Than the doInBackground() is called, and is done so, however, just when the method is being executed, the user presses the home button and swipes the app from the RECENT APPS. (this ofcourse causes the app to terminate and all the onDestroy methods get called of the alive activities..(Correct me if I'm wrong on this one)).
In my onPostExecute() method, I am inserting the data to DB and updating the VIEWs.
But since the app is 'terminated' the onPostExecute() method never runs.
my questions are :
When the user presses the home button and gets out of the app and swipes the app, is doInBackground halted at that moment ? that is, it is cut in the middle and does not continue what it does ?
What happens to the data that I was going to get from the server and put inside the DB ? Is it advisable to do put the data in the db inside the doInBackground ?
AsyncTask is a background task it will continue to run even if the app is closed and onDestroy() is called. The problem would be when the task enters onPostExecute() and tries to update any views associated with the activity it is bound to, as that activity no longer exists. If you're concerned about this, one thing I've done is to keep a reference to that AsyncTask in the calling activity, and then call the method myAsyncTaskReference.cancel(true) to cancel it in the onDestroy() of the calling activity.
Yes, I would put the DB operations in the doInBackground() method, as that runs in the background on a separate thread, and doesn't require a reference to the app activity.
Have you considered using a service for this type of thing instead? I would strongly recommend an IntentService, which is a subclass of service which is specifically designed to perform a single task in the background (similar to AsyncTask), and then kill itself once completed. It's quite easy to implement and usually only involves overriding one method - onHandleIntent() - which is called when the service starts.
It can handle all your DB operations and it has no reference to an activity and so the problem you're worried about in #1 would never occur. If you need to update a view, you can have your IntentService generate a broadcast once it's completed, and your Activity can register for that broadcast and update it's views accordingly when it receives it. And if your activity is no longer around once the broadcast is sent then it doesn't matter and nothing will fail.
When user presses 'Home', your Activtiy will pause but doInBackground will NOT, but may or may not terminate by system when system feels like it. Activity's onPause will be called. Your Asynctask doInBackGround will NOT halt. It will continue to run until the system kills your App process.
Yes, Db operations can take long. Its advisable to put in doInBackground because it runs on another Thread. onpre/onpostexcute runs on the main thread. If you are worried that System may terminate half way of your db operations, you shouse set Transcation, and only when you are done, you called commit.
Check out this post, I have tested it.
no, it doesn't stop.
It is relly better to put it to datastorage of some kind and then work with it
It is always better to use service for such goals. AsyncTasks just don't fit here. Ask your service to do the work, and then you may start or quit activities as you wish to.
If swiping app from recent stack, it is equivalent to close the app hence it will kill all tasks related to the process so async task will also get killed by the android system. ( even intent service is also get killed)
It is device dependent also. Manufacturers customised removing app from recents behaviour

Good Design: Worker Threads and Activity Restarts

It is well known that Android places our application components (Activities, Services) under threat of being killed at any moment. This makes things really complicated if you want to provide a robust, non-leaky solution, while at the same time keeping the code clean and addressing separation of concerns.
Problem:
An activity starts a time-consuming task (in form of a Runnable, AsyncTask or whichever other mechanism). A progress dialog should be displayed. The task will open several connections, but it should have a means of updating a progress bar, and it might need to display a choice dialog half-way to completion. It also should be able to close the dialog and show a Toast when an error occurs, or when it completes.
When an Activity is killed and later a new instance is recreated, I can think of two options:
Try to kill the running task as well, and spawn another task instance when a new substitute activity instance is created. This is not an option for a long running task, as it is unacceptable from a user point of view to start the task and see the progress bar indicator going from 80% back to 0% with no apparent reason. This however is the approach followed in the Shelves example by Romain Guy. Apparently this is the approach recommended in the official Developer Guide.
Keep the task running: So we could have an activity subscribing to (and possibly starting) the task for updates at onResume, and unsubscribing (and possibly pausing) it at onPause.
Following the second option means that we should prevent the task of being collected in case the activity is temporarily destroyed. For instance, we could declare it inside a custom Application class, or even offer it as a service to the rest of the application (using a Singleton? an Android Service?) I'm no fan of this, though, since the task is used only by the activity, so it doesn't make sense to make it available to other classes. Android Services on the other hand also have to deal with life cycle.
Option #2 also involves ensuring the old activity is not leaked. We should not try to update the GUI if no activity is present. The whole think should be thread-safe. Finally the task should not remain in memory after we are sure it is no longer needed. But at the same time, if the initial activity is destroyed, the task keeps running, and a new substitute activity kicks in, the task has to be around to update the GUI immediately and display the task current status (or result in case it completed).
The fact that the task might be running when no activity is shown is another problem to deal with, since we could need user interaction (choice dialog) synchronously at some point. This could be solved if the activity had the ability to immediately pause the task when reaching onPause, but the task could take some time to pause itself or even be unable to do so.
I know similar questions has been asked before, but I'm not asking specifically about how to solve the problem, but about what design would be recommended in order to achieve loose coupling and isolate the activity from the task as much as possible.
In short:
- This is a recurring problem in Android development and someone has probably come up with a clever design before. Is there a design pattern, or a well tested library that simplifies this?
- If you were to implement #2, which class would you choose to for the task (Runnable, Service, etc) and how would it communicate with the activity?
P.S. Please refrain from posting solutions based on the "orientation | keyboardHidden" hack XD. Other than this, any help would be appreciated.
UPDATE:
My first attempt has got a bit messy. The app is a bluetooth printing utility, which involves Detecting BT status (and asking the user to enable it if it were disabled), retrieving paired devices (and asking the user to pick one if there were more than one), then sending the data and finally inform the user about the result. As this task was 80% IO code, 20% GUI-related operations, I had to group the GUI code at the beginning and end of the task, then moving it out of the task back to the activity. I have an IntentService that does the heavy lifting, then reports back to the activity via BroadcastReceiver. This solves most of the problems but there are a few issues with such a design. First, there are all those constant strings used as keys to put and retrieve fields from the input and output Intents, which are introducing semantic coupling. I'd have preferred type-safety in the activity<->service communication. And I yet have to solve how to pass complex custom objects to the service in the Intent, by now I'm stuck with Parcelables and primitive parameters. Finally, both the activity and the service are using the same API (bluetooth), I'd have preferred to have al the BT-related code in a single class. I think I'll throw away this design, and try again with a custom printing queue based on threads, despite it will make harder dealing with killed activities.
why not have your activity start a service to host the long running task? a service is your best-bet for keeping things running long term. you can provide hints to the OS to leave it running. see startForeground() and START_STICKY.
you can communicate back to the activity by broadcasting from the service. have your activity programmatically register a broadcast receiver to listen for those intents. if the activity is paused, unregister your receiver, that way you won't respond when when you aren't in the foreground.
if the OS destroys your service, then it's killing the process, so your task is doomed anyway. the best you can do is restart it. anyway, this won't happen except in the most extreme conditions if you consider the above.
EDIT: summarizing some thoughts captured in the comments ... keeping a long running worker processe and automatically restarting it is almost always the wrong thing to do. mobile devices do not have the power source to make this feasible. the author stated this he has a special condition that negates this concern ... that would only be true if the device is connected to a power source almost always.
the android model is to provide a robust set of intents (notifications) to which your application can listen. these intents wake up your device / application when it's time to do something. your app should handle the notification, and then allow the device to go back to sleep immediately.
if there's no stock intent that maps to your event, you can use Google Cloud Messaging to wake up devices from a server. in other words, let the long-running processes run on the server, and wake up the device when required.
if you can do that, an alternative would be to use AlarmManager to wake up periodically and check some state. even if you did this frequently (e.g., every 5 minutes, which is also a no-no), it'd be much better than keeping the device awake always as is being suggested. also, make use of inexact wakeup intervals (see documentation linked above).

What do I use to restore my Activity's state when it has been recreated?

I've been looking around at many forum discussions, but none of them answers properly how to handle runtime changes that forces the actual activity to restart.
In my case, for example, my activity uses an AsyncTask to show a ProgressBar while fetching data from server. But, if user just rotates the screen, the activity is restarted and repeats the request to server.
If screen rotation is done while the AsyncTask is still in doInBackground(), waiting for server's response, I get a android.view.WindowLeaked error.
I just want to, whenever the screen gets rotated or another interruption occurs, my activity continues doing its job from where it stopped, could that be this hard?
onSaveInsanceState() is recommended? How could I save a partially received JSON in a Bundle? Or maybe I should use onRetainNonConfigurationInstance()...
Will send a bottle of Brazilian's drink "caipirinha" for the good soul that helps me...
You need to use a different architecture.
Move the background operation from the AsyncTask into a Service. A service runs in the background independent of any Activity, and does not get restarted when you change the orientation of the device.
The background Service can communicate with the foreground Activity via broadcasts. Within the service, you can send broadcasts w/ progress info using sendBroadcast(). You can receive the broadcasts in the Activity using a BroadcastReceiver.
EDIT: For less drastic approaches that may be suitable depending on your situation, see:
http://developer.android.com/guide/topics/resources/runtime-changes.html. In particular, if you do not use different resources for landscape and portrait, then the second method (handling the configuration change yourself) may work well.
EDIT2: Some additional info here:
Activity restart on rotation Android

AsyncTask doubts regarding the activities flow

I got some concerns about AsyncTask that I could not clarify when reading the documentation.
My app main Activity runs several AsyncTask's when it's created. These AsyncTask's mostly download data or retrieve data from the DB.
If I go to a different Activity, will the AsyncTask's created on
this one continue executing? Or will they stop working and leave the
task half done? If so, will they go on somehow when getting back to
this activity?
In order to start one of the activities from the
one that is running the AsyncTask's, I need one of the AsyncTask's
to be fully executed. How do I set this constraint? Could you show
me some sample code of this, please?
Thanks
If I go to a different Activity, will the AsyncTask's created on this one continue executing? Or will they stop working and leave the task half done? If so, will they go on somehow when getting back to this activity?
Yes, it will continue to run. This can be a bit of a double-edged sword as it will typically also hold on to the Activity instance, causing it to live beyond its onDestroy() callback, which is not ideal. If the task you are running does not have the same lifecycle needs as the Activity itself, it may be better placed into a Service.
In order to start one of the activities from the one that is running the AsyncTask's, I need one of the AsyncTask's to be fully executed. How do I set this constraint? Could you show me some sample code of this, please?
There are way too many ways to do this, many depending on the architecture of your application, to provide specific sample code...but here are a few higher level ideas. Since AsyncTask provides a simple callback method on the main thread (onPostExecute) when the task execution is complete, you can set a flag at that point. Or perhaps simply check for the existence of whatever data the AsyncTask is retrieving from any code where the Activity would be launched. Again, a Service would provide good context for this, as multiple Activities could connect to the service and check the task status before moving forward.
Another option, depending on your application, is to have the result of the task dumped into a ContentProvider. ContentProviders include a nice interface for notifying observers of changes without resorting to global Broadcast Intents.
HTH
Ive done something similar to this before using Intents and Broadcast Receivers.
If you get the Async tasks to send a global or app-wide intent when they are finished, its easy enough to have a receiver pick it up if you construct them properly. By having a receiver created when you push a new activity onto the stack, and have the receiver close when an activity is paused, there will always be a receiver out there to grab the intent and be told that the (example: data is downloaded from the db) and act on that.
If you try and start an activity that requires that data, you can either deny the creation of that activity if the intent hasn't been found yet, or create the activity and have it put up a progress bar or something while the data finishes downloading, then have its receiver act on the intent when it arrives.
Hope this helps.

Categories

Resources