Application Flow Image (Cannot post directly - not enough reputation yet)
Introduction
We are creating an application that manages our products. We have several types of a products (1) and each type of product has dozens of those type (2) (ex. Videos).
We have an xml files on our website that holds all the information for each product. When a user selects the type of product (1) it reaches out to our website to download the xml file which populates the product list (2). The user then selects the product from the list which then passes the information to the product single activity (3).
Oh philosophy was when creating the application to create a place where the user can download the product and materials connected to it, but have the application manage which products you have downloaded. You download everything you need so when you don't have internet connection you are still able to use the application and view the products you have downloaded.
Problem
Some of the products in our app are fairly large and require an extended amount of time to download. When a user is on the product single activity (3) they can download the products and when a configuration change happens we understand we need to use fragments to maintain the progress bar and text on the button.
! The problem comes in when the user hits the back button.
We don't want to just cancel the download for the user.
It takes quite a while for the download to finish
We want the user to have the ability to download multiple products at once.
Not canceling the ASyncTask actually lets it continue running when the user hits the back button and even allows us to send Toast messages to show that it is still running. The problem comes in when the user navigates back to the activity the UI doesn't show the file is still downloading because we lost the reference to the ASyncTask that is running.
Solution
We are trying to come up with a solution so that the user can back out of the product single activity (3) and come back to it later and see the status of an ASyncTask if it was started earlier. The problem is if the activity is destroyed how do we hold on to a reference to the ASyncTask or Fragment.
The solution to your problem would be to use services. Services in Android are designed to be long running background tasks. Although you can't directly hook into the UI thread from services, you can use Handlers to post to the UI. Your service will stay around as long as they are needed (Android kills them only under low memory conditions and even then tries to stay away from the running ones).
AsyncTask is designed to be used only for short operations. It is possible to use it with some changes to run longer processes but Android prefers to not do it that way. Android specifically tries to steer you to use background threads and processes.
The problems that you are seeing are because AsyncTask (generally being defined from within the activity class) are tied to the Activity itself and maintains a reference to it. But when an activity goes to the background and has remained in the background for some time, the activity may be destroyed and GC'd at which point, the AsyncTask loses its reference to the parent. With services, it will not matter whether your activity is still available or not. You can connect to the service anytime you want and then query the service to find the status of the download.
first of all there are services that do background work even if your app is displayed on the screen or dead.. second of all there are threads that work and work and work..
thirdly there is a methon called onBind that binds an activity to a service..
your SOLUTION??
use threads in services and whenver your activity is called or app is opened
bind it to the service to get what you want...
NOTE: if your file takes some time to download, well then asynctask is not a recommended approach..that is if it takes a hell of a timee..
Hope im helpful...thnks
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 already know that using Services/Intent services we download multiple files in background. But this problem have one more complexity to solve.
I have to download multiple videos/images in different activities in parallel like whats app. I have seen in whats app that we can start download and then we can moves on different screens or press home button etc. But when we move back on that screen again the downloading is still continue with and showing the updated progress.
Like above there is 4 activities and user started downloading of ABC.mp4 on activity and A and then moves on to Activity B. Then moves on to C and started downloading of video/image. If user moves back on to the A and I want to show the progress of that ABC.mp4 file that how much downloaded and same case for the other activities.
Please let me know to you Services or any other way to do, so I can update the UI as well with the updated value of the downloading file. Any help is appreciated.
Well here's my piece of advice:
I did the same thing but with Pdf files a while a ago. First you need to choose a good Http client library to perform the downloads and forget about all the issues related to the downloads that you'll have if you perform this operations by yourself. I recommend you use the Ion librabry has a lot of useful features like cancel the downloads, attach callbacks to have an eye in the download progress, known if the download succeded, failed, etc.
Second, you obviously need a Service running in background, always, you can do this by making your own service, and It's very important that the Service process name in the android manifest is like android:name="com.company.app.services.MyService" and not like android:name=":MyRemoteService" doing so you'll make sure that the service will be running in background all the time (keep in mind that the OS can and will kill the service if It's neccesary, but the service will be restarted when the resources will made available again).
Third, to keep all the views, custom views, activities, fragments, etc. that display those files updated, you need trigger BroadcastReceivers from your Service. Keep in mind that doing so you can't use the LocalBroadcastReceiver, because your Service has its own process indipendent from your application.
Keep in mind that:
1) Theres no need to explicitly use an Activity, you can register your broadcasts anywhere where you see It fit. A RecyclerAdapter (most likely), a Fragment, or an Activity.
2) The communication App/Service is done through Intents so all data that you send to and from the Service must be Serializable.
3) You can use several different types of broadcast each one for differents situations (error, fail, success, progress, etc.) or you can use only one an send a enum that describe this kind of status through the Bundle of the Intent when you fire the broadcasts from your service
Hope this helps you, If you have any doubt, let me know.
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 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.
I have a weird problem. As I have asked in previous posts, I have a lot of data coming from NET in my app. The problem is that a lot of text and images r there, so though I can make the user wait wen he clicks on a list item, but I don't want to make him wait wen he clicks the back button.
In the sense that I don't want to re-download all data; data 1ce downloaded should remain.
Which 1 is the right method?
1. Using a special class which stores all data... and using the variables each time I go back,
or
2. Is there any specific save instance method for saving such data in the class
itself... if it is then do inform me how to call the saved instance Activity
when back is clicked.
By "user clicks the back button" I understand that the previous activity is displayed and returned at the top of the stack (see activity lifecycle).
This mean this activity is not created again, but resumed (unless it was killed by core because other applications need memory), so its fields and attributes keep their values.
So if you store your datas in some activity fields, you might be able to retrieve their values and display them without re-download them.
Of course you have to test these fields before accessing them in order to avoid NPE in case of activity first start.
You shouldn't rely on any Activity already existing when you start it or return to it.
Check out this previous question about caching already-downloaded data:
What is the ideal place to cache images?
You shouldn't have any pauses in the UI anyway when the user enters an Activity; you should be doing all the downloading on a background thread and showing some sort of progress indicator to the user. If you don't do this and the UI thread is blocked by a slow download (about 15 seconds), the user will be presented with an "Application Not Responding" dialog and asked to either kill your application or keep waiting. Not good.