i need to save some crucial importance data when user closes my app. i prefere to do this in Activity's onPause(), as it is the latest method guaranteed to be called before the application is destroyed no matter what is the reason of its destruction. however, i save the data in a database, so i use an AsyncTask. from my AsyncTask, when it's finished, i call a callback method on my Activity reference i have saved in the AsyncTask before, where i put some variables to SharedPreferences indicating that the data has been successfully put to database.
my question is: is the AsyncTask guaranteed to finish and the callback to be called on an alive Activity reference before it gets destroyed? If not, how to ensure that data is written to database before Activity is destroyed?
You don't need to implement a CallBack method to save in your SharedPreferences that you have updated the Database.
You can simply, if you really need a database for your info, create a flag in your SharedPreferences called isDataExistToLoad
This flag has to be initially False, and within the Thread that will store your info into the Database set it to True.
public void updateUserStatus(ArrayList<Object> data){
try{
DBController.save(data);
SharedPReferencesController.setDataExistToLoad(true);
}catch(Exception e){
e.printStackTrace();
SharedPReferencesController.setDataExistToLoad(false);
}
}
Whenever you open your app, check on this flag. If it is True go load from database then set it to False. If it is False leave it as it is because the previous transaction was not successfully done.
My question is: is the AsyncTask guaranteed to finish and the callback
to be called on an alive Activity reference before it gets destroyed?
If not, how to ensure that data is written to database before Activity
is destroyed?
My Answer is: Part of the AsyncTask runs in a separate Thread which is DoInBackground and you cannot ensure that the Activity will still be survived until it is done. If you tried to perform any other task in postExcute() which runs on UITread after finishing the background thread work. Your app might crash. That's why I don't recommend that.
The ASyncTask executes in background parallely to the main thread. In case if the Activity is closed, still the ASyncTask continues to execute till end. And it need not to return the control to the main process.
So, you can surely use this method of saving data before closing the application.
I am not sure whether after onPause of Activity (ie; when onPause() callback is called) you might be able to use the share-preference, feature. So, you should handle it on the backbutton or some event in onCreate()
Related
Let's imagine I have an Activity that performs an asynchronous operation as follows:
smtpClient.sendEmail(new EmailSendListener(){
#override
public void OnEmailSendSuccess(){
// Do stuff here
}
});
And before the callback method is called, for whatever reason the Android system decides to destroy my Activity. What happens to the callback?
Does it never run?
Does it run anyway because the instance of the Activity is technically still alive despite the Activity.onDestroy() method been called? If this is the case, should I in the callback code check if the Activity has not been destroyed to avoid accessing fields that are no longer available?
What happens if the entire process, not just the Activity, gets destroyed? That is, even the instance of my Activity is gone. Can someone give me the inside scoop of what's going on behind the scenes?
In my login Activity, just after the use log in, I sync data and create several tables in sql.
If the user open another app before this process finish, I want to cancel it and delete tables.
I thought I could put it in onPause or onStop, but then, when the process finishes and go to the dashboard activity, onPause or onStop should be called, and I don't want this.
I assume the solution is simple, but I can't figure out how to solve it.
Any idea?
The logic behind your solution is straightforward - create a flag (say for example shouldRollback) to true when you start your transactions. Once your transaction are successfully completed, you set this flag to false. In your onPause() method, simply check for this flag. If it is true, you know you need to roll back the changes.
I would only suggest this if this is your requirement. Personally, I would process all database operations in a background thread. The thread can run in the background even if other applications are opened. There will be too much overhead (if you have a large number of transactions) to keep rolling back inserts. You could use the transaction methods from the SQLiteDatabase object to automatically roll back changes if they were not completed successfully.
I feel like this should have been answered by the documentation in the Activity class, but I'm still not positive -- when is it actually safe to update the UI of an Activity (or a Fragment)? Only when the activity is resumed, or any point between being started and stopped?
For example, the Activity docs state:
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user. Between these two methods you can maintain resources that are needed to show the activity to the user. For example, you can register a BroadcastReceiver in onStart() to monitor for changes that impact your UI, and unregister it in onStop() when the user no longer sees what you are displaying. The onStart() and onStop() methods can be called multiple times, as the activity becomes visible and hidden to the user.
From that reading, I would assume that even if a foreground dialog is displaying, I can safely update UI elements behind it.
EDIT
To clarify: the reason I ask is having been bitten by errors when firing off an AsyncTask, and attempting to update the UI in the onPostExecute method. Even though it runs on the UI thread, the user has navigated away from that view and I would receive an exception. I'm starting a new project now and trying to establish some guidelines around better AsyncTask idioms.
I guess this comes down to what you mean by "safe" to update the UI. For elements on the Activity screen, they can be updated whenever, even if your Activity is not in the foreground (but make sure to update from the UI Thread).
However, the issue that will trip you up is saving state: onSaveInstanceState.
As you may know, the Activity that is in the background may be destroyed by the OS to free up memory. It will then be re-created when you come back to it. During this process, the onSaveInstanceState method will be called. If the OS does destroy the Activity, any changes you made to the UI State after the call to onSaveInstanceState will not be persisted.
For Fragments, you will actually get an IllegalStateException if you try to commit a FragmentTransaction after onSaveInstanceState. More info on that.
To summarize, you can update the UI of your activity at any point and try to gracefully handle the Fragment issues, but you may lose these updates when the Activity is restored.
So, you could say that it is only truly safe to update the Activity while it is in the foreground, or rather, before the call to onSaveInstanceState.
Edit in regards to Async Task onPostExectue
This is likely related to the issue I am referring to above with Fragment State Loss. From the blog post I linked to:
Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:
An activity executes an AsyncTask.
The user presses the "Home" key, causing the activity's onSaveInstanceState() and onStop() methods to be called.
The AsyncTask completes and onPostExecute() is called, unaware that the Activity has since been stopped.
A FragmentTransaction is committed inside the onPostExecute() method, causing an exception to be thrown.
My Problem: Is it possible to prevent an activity to call OnResume() when it is being created? As I saw after the OnCreate() and onStart() method runs, the next one is the onResume(), although I only want to have it when I resume the activity from the paused state.
Why do I need this: I launch my activity (FragmentActivity, so lets say OnPostResume() ) starting with a thread which takes about 2-3s to be ready getting data from an external database. After the thread is done, I call a method which needs these data and I want to call it everytime that activity gets visible. The thread runs only when the FragmentActivity is created (onCreate()), and I cannot put the method into the onResume() because onResume() would be running way before the thread would finish its task. So it would receive not-ready data.
Anyone has a better idea?
Not sure of the exact application of this but I'll make a suggestion.
If you use an AsyncTask, you can send it off to get the data you need and in the onPostExcecute() method you can call your method that requires the data or update the view as needed. (It runs on the UI thread)
If you happen to already have the data you need in certain scenarios you could also bypass the AsyncTask and directly update the view.
This AsyncTask can be triggered in the onResume() method.
If I'm missing something, please let me know and I can adjust my suggestion.
I didn't understand the purpose of this, but here's a possible solution:
If you only wish to get the even of onResume on states that didn't have the onCreate before, just use a flag.
In the onCreate, set it to true, in the onResume check the flag (and also set it to false). if it was true, it means the onCreate was called before.
I personally would prefer to check if the result available, rather than always executing the getter-code in onResume. If the user somehow resumes your activity before the background thread is finished, you'd have a call on onResume, but don't want to display a result.
Maybe it would be a good idea to calculate/fetch the values in the thread, and let the thread return immediately (and cause the values to get filled in) if the values are already cached somewhere. That way you'd only have one entry point (the thread) for updating your UI instead of two (the thread and the onResume method).
I have a Fragments which save user data in their onSaveInstanceState method.
I need to get this data when the user finishes the activity and display it in the next intent.
Currently the sequence of my implementation is:
user hits the close button
computation starts relying of data stored in prev calls of onSaveInstanceState, then start new intent
onSaveInstanceState of current fragments get called, information is missed in calculation
new Activity displays
old Activitys onDestroy is called
I thought about starting my calculation and the new activity in onDestroy. Then I need a mechanism to destroy the current Activity.
Or I could somehow call all remaining onSaveInstanceState method calls in the current thread before doing the calculation.
What would be better or is there a better way?
This sounds like a strange design to me for android. For this use case I would do:
user presses abitratry button (in your case close)
click listener gets the needed data (the same way you would get them in onSaveInstanceState)
start an asynctask if lengthly operation and show progress bar or make computation on UI thread if trivial
start intent with computation result for new activity
I have a Fragments which save user data in their onSaveInstanceState method. I need to get this data when the user finishes the activity and display it in the next intent.
Is there any reason you need the data specially from onSaveInstanceState() method?
thought about starting my calculation and the new activity in onDestroy. Then I need a mechanism to destroy the current Activity.
If you call something in onDestroy() there is no need to forcefully destroy the activity since its already in progress. (btw. finish() would do it). Appart from that, onDestroy should do clean up procedures e.g. free resources or close streams, not for calling new activities