I'm creating an app that will do the following:
If a mail comes in with a messageword. It needs to start to locate the phone by GPS functionality. after that it must show the result to the user.
I already have it working with using a broadcastreceiver for fetch the message. After that is starts an Activity. If don the userinterface is updated.
I have here two problems:
1) if the screen rotates all variables are set to zero... well can solve that with saving variables at onPause() and read those values at onResume()
2) if the user use the backbutton... the thread is cancelled.
Actually I want that the user can't cancel the GPS-action.
Is it a good idea to use an Intentservice for this?!
-message comes in
-BroadcastReceiver fetch the message and start the intentService
-if the processing is done... I can start an activity with the results (and the service is closed automaticly after the processing is done?!
Is there maybe a better way to attack the problem?
You certainly can and should use an IntentService for long running operations in the background. However, if the user backed out of your activity, they most probably did this for a reason, and popping up an activity with results they might not care about anymore, might not be such a good strategy. You could cache the result instead, and show it next time the user opens your activity.
Related
The title isn't good, please read bellow to understand better my problem
I have an Android application, one of its tasks is time consuming and will often take between 2-5 minutes to be finished... this task is done by a background service which has a reference to the starter activity.
99% of users doesn't want to wait all this time looking to a loading bar and will simple open another app or something like... what might lead to android destroying the referenced activity...
Ignoring the context leak of this story... my problem is:
When the service finishes its task it will try to call a method to return the values to the parent activity, but as it was destroyed now I can't do it...
So when user re-opens the app the last known state by that activity is "loading" so it keeps loading forever... (or retry to rerun the task what will lead to another 5 minutes wait and so on...)
how can i avoid this situation?
==============update=================
After getting a very good answer that would probably solve most of problems like mine i decided to add more information to let clear my problem.
The time consuming background service ISN'T process intensive, actually the reason it takes so long is because it is validating with the service some user "credentials" (when i say some, is really more than one)
So i can't store the result and trust it is valid on next run
I do know the this protocol needs to be improved but is going to take a bigger archtecture change so i would like to know if someone has any idea how to handle it on its actual requirements
Is there a need to do run the background task every time your Activity starts?
Typically, if your Activity needs these values to work and it takes so long to calculate or fetch these values, you would store these values after the background service has finished with them.
Basically, you're creating a cache for your data. So when your Activity starts, it checks the cache for data. If it finds the data, then it loads it immediately. If it doesn't find the data, then it runs the background task (typically the initial run).
It's also common to have a timestamp of when the data was stored in your cache, so if time apart is too long, you could either:
Show the outdated data, while running the background task. Background task finishes and stores new data to cache, then notifies Activity that there's new data. Activity updates with the new data.
Don't show outdated data, instead, you wipe the cache of the old data and run the background task.
Basically, this solution simply has your Service store the values calculated into a storage location, whether it's a database, SharedPreferences, files, or etc.
You avoid the need to have your Service return values to an Activity, since there's no guarantee the Activity still exists. Instead, the Service only notifies the Activity that it added newly updated data to the storage location, so if the Activity still exists, it'll update without a problem. And if the Activity is killed already, then it'll simply fetch the data when it starts up again.
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.
In Android it is generally a good practice to perform no database operation (or at least complex ones) in UI-Thread. I have an activity with a complex form and I want to ensure that all data is saved when the activity goes in the background (e.g. the user presses the home button or a phone call comes in). In the activity’s onPause()-method I can start an AsyncTask which stores the data in database but I can never be sure that the task finishes successfully because android can kill the process before the task finished because the activity and the whole app is in background.
I can save data synchron in the onPause-method but then it’s possible to run in to an ANR.
I know that Android restores the views after the activity was killed but this works only correct when View Ids are unique. I have a lot of programmatically added Views where I cannot ensure the Id’s uniqueness and to use the saveInstanceState-functionality is nearly impossible because I have to save very complex models.
Is there any possibility to ensure that data will be saved before android kills a process without doing it in the UI-Thread?
I created an application once where I had similar data consistency concerns. What I did there is delegate the storing of the data objects to a Service I created just for that purpose. Although this makes the starting/stopping/initialization of your Activity a lot harder (once the activity is started again, you will have to wait for the service to complete its previously started save action), this was the only "Android" way I could think of to deal with this problem.
You might look into using a service for that if you are afraid that the system kills your background-processes before they are completed. This might be over-kill, but will definitely work as expected =) Just google "Android Service Tutorial" if you are unsure how to use them.
-Services won't be killed unless you want them to!
Indeed, if you're running an AsyncTask in onPause(), Android can kill your applications's process without waiting for the worker thread to finish. But it won't kill the process if there's a running Service. So a nice solution here is to implement database synchronization logic using an IntentService.
I'm facing the same question here, when to save data: while the user completes a form or when the activity pauses. Also we must take into consideration screen rotations or other events that might result in data loss.
Here is what I found on the Android developer site:
For content provider data, we suggest that activities use a "edit in
place" user model. That is, any edits a user makes are effectively
made immediately without requiring an additional confirmation step.
Supporting this model is generally a simple matter of following two
rules:
When creating a new document, the backing database entry or file for
it is created immediately. For example, if the user chooses to write a
new e-mail, a new entry for that e-mail is created as soon as they
start entering data, so that if they go to any other activity after
that point this e-mail will now appear in the list of drafts.
When an activity's onPause() method is called, it should commit to the backing
content provider or file any changes the user has made. This ensures
that those changes will be seen by any other activity that is about to
run. You will probably want to commit your data even more aggressively
at key times during your activity's lifecycle: for example before
starting a new activity, before finishing your own activity, when the
user switches between input fields, etc.
This model is designed to
prevent data loss when a user is navigating between activities, and
allows the system to safely kill an activity (because system resources
are needed somewhere else) at any time after it has been paused. Note
this implies that the user pressing BACK from your activity does not
mean "cancel" -- it means to leave the activity with its current
contents saved away. Canceling edits in an activity must be provided
through some other mechanism, such as an explicit "revert" or "undo"
option.
You need to start a backgrounds service daemon with a notification to make sure your data is saved and shut down the service and notification as soon as the data is saved. The notification will be shown until the background service is running as it is mandatory to show services of background service otherwise your application would crash.
I have implemented some computationaly heavy functions into my application. Depending on the type of input it can take up to several minutes for a thread to return. During this time, I want the user to be able to work with other activities to perform different tasks (i.e. prepare data for the next run). Then, after the Service has finished it's computations, the user should be notified with a Toast that the result is ready and that he should check back to the Activity he started the Service in. Unfortunately I'm stuck at this point.
Is it possible to somehow communicate with an Activity which is destroyed at the moment? Like modifying the saved state, so that when it get's recreated the result will be displayed. The only way of communication I did find was via broadcasting from the Service, but this requires the Activity to listen, which is not possible as it doesn't exist at the moment the Service finishes.
The only solution that occured to me was writing a file when the Service is finished and then trying to read it in the Activity, but I would prefer not to work with the file system if that's possible.
Am I missing something here or thinking in the wrong direction?
use Asynctask
http://developer.android.com/reference/android/os/AsyncTask.html
You could just write to a SharedPreference from the Service.
The Activity can check the preference whenever it is started and you can have some marker to indicate that the result was from your computation.
You could also write to the same SharedPreference from the Activity to nullify it so that it can be used for the next result.
I would use an AsyncTask, and return the value from your computation to some stateful location (use an Application class, or SharedPreferences even).
Have the AsyncTask launch your Activity on completion, and get the value you statefully stored to display.
Basically, combine the 1st 2 answers and you should be set.
I have an application that uses AsyncTasks to make calls to a REST server.
Imagine that during a loading period (this is, the AsyncTask going to the REST server and gets data to the next screen) the user presses Home.
What is recommended:
Cancel the current AsyncTask(s) and restart when resuming the Activity
or
Continue the AsyncTasks but avoiding the startActivity by checking if the app is on background (to avoid the foreground of the new activity after sending the app to background). And onResume sending to the next activity
Worst case scenarios that you should foresee:
The app goes to background and is killed due to lack of memory
The asynctask fails due to timeout or other REST error
After both, the user goes back to the app...
Well I ll recommend Service which should use AsyncTask to do its work. Service will insulate your activity from orientation change or user exiting. AsycnTask will insulate from the UI thread being blocked. But do make sure to exit Service when you are done with REST api.
This will give you best of both. Also if you are not using DB as a local cache then you can try that too. So even if the user goes away, the service will store the fetched data in the DB and when the user comes back you can quickly display the screen.
EDIT: Just want to add IntentService are easy to implement.
Try Design Patterns for REST clients on Android by Google for more exhaustive explanation
Using AsyncTasks can get really messy during a configuration change. From personal experience I would recommend going down the IntentService/ResultReceiver route instead.
See this post for more info:
Restful API service
I guess what you want to know is which way is better from a users perspective. From my experience, a user expects the app to continue the download in the background, because if he presses home, he normaly either wants to check some other apps in between or he pressed it unintentionaly and wants to go back into your app as soon as possible. If a user wants to cancel the download, he normaly presses the back button or a specific button to cancel that is somewhere on the screen of your app. So as the user normaly wants to continue using the app, the more convenient behaviour of your app is to continue downloading data and hopefully already display the downloaded data to the user when he gets back into your app.
From a technical perspective, I would not use a service. I would just leave the AsyncTask running. And in the worst case when the app gets killed inbetween, the app automatically goes into the starting activity when the user gets back to the app, because the app is restarted anyway. In the case that the asynctask fails, you can check if the data has been succesfuly downloaded and if not showing the starting activity. You can easily implement this with storing the downloaded data in a variable which's initial value is null. If the variable is still null after downloading, the AsyncTask failed and you have to go into the starting activity. I think this is a pretty robust implementation.
downloading continues as does the android market app, but it shows a notification with the option to cancel. You can implement this, using a service to download.