In our application, we rely almost solely on data from our web service. On first launch, we start a LoginActivity where we handle logging in and retrieving this data before continuing onto the MainActivity where all of the UI that consumes the data begins.
We store the all of this data in a static data model, and it works great for what we need, however there are some instances when the application is killed off (due to memory constraints) while the Activity retains its state. So if I go and launch several other apps (to cause this to happen) then relaunch the app, it attempts to resume the MainActivity (which relies on the data from the service) and crashes due to fact that the application is no longer active and the data model no longer contains any data.
I've discovered that the Application.onCreate() method will be called in this case, so it seems like I'd want to handle returning to the login screen at that time, but it doesn't seem to be recommended practice to launch an activity from the application's context, not to mention that we'd already be in the process of resuming the activity.
What I would really like is for the application to not retain the activity state after the Application is no longer active (and all of the static data has been lost) and to just launch from the LoginActivity as expected.
This seems like a very common scenario; any suggestions on something simple that I may be overlooking, or any more info I can give?
I have the same problem.
Now I first check for static data in onResume() of MainActivity, while the static data is null I use startActivityForResult to open LoginActivity to prepare the static data, then continue when it returns Activity.OK in onActivityResult.
If you really want to have an app restarted to LoginActivity, first thing comes to mind is checking in every other Activity.onCreate:
if (yourStaticData == null) {
finish();
return;
}
I'd suggest not doing that and making app behave correctly when the process is killed.
clearTaskOnLaunch may be what You are looking for.
Related
My app needs to initially download data from two different web services (JSON) and import them into it's local database (Realm). I have two activities that need to display data from these web services. The first one (HomeActivity) is the initial activity that the app loads. The second one (LineupActivity) is created when navigating to it from the HomeActivity.
Currently, I've created an Application class (extending Application) in order to handle the web service downloading and importing. In it's onCreate(), it calls two methods, which are AsyncTasks that download and import each web service.
The reason I've added this download/import process into the Application class is for 2 reasons:
I want all the app data to be downloaded as soon as possible, so
when navigating to the second activity it doesn't need to initiate
another download.
Both these activities have swipe to refresh. They call each respective method in the Application class to re-download/import the
web service data.
Have I approached this incorrectly? Should I move the web service download/import logic out of the Application class? Also, does the onCreate() of the Application class get called more than once? Meaning, I know that it only gets called only once in the application's lifecycle, but does the Android OS eventually kill an app and have it call the onCreate() in the Application class when starting it again? I want the app to download fresh data upon startup, but not every time the user brings the app into focus.
Have I approached this incorrectly?
"Incorrectly" is a very relative term in this context.
Metaphorically, its like the context is never null but that doesn't determine boolean incorrect is true or false.
Should I move the web service download/import logic out of the Application class?
I would say Yes, as the download logic would not be related to the O.S. and the app being alive in its memory. Your requirement does not seem complex. Service would be necessary if the downloads are huge chunks of data, and if its not necessary, don't do it.
Also, does the onCreate() of the Application class get called more than once? Meaning, I know that it only gets called only once in the application's lifecycle...Application class when starting it again?
No, it won't be called more than once without app being killed and restarted. And what your are saying is correct, but for your requirement, there are probably more efficient and lighter ways to do it rather than combining it with an Application class.
For the rest of the logic, you could implement Asynctasks as a separate class and implement interfaces which are the callbacks of the result of your task. This would help in Swipe-to-refresh functionality.
In terms of documentation reference, Application class
There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way.
Your case seems exactly that.
One thing you need to decide is When, how often do you need to refresh/download the data. For only start-up of the app or once daily, you can store date/day in SharedPreferences and check the value in onResume() of your Activity.
You can also implement inheritance with a Base Activity with the necessary download check logic in it and extend your classes. A Splash Screen would always help to initiate the downloads.
I suppose you could approach this anyway you want, though I like to implement one of the three options mentioned here: https://dl.google.com/googleio/2010/android-developing-RESTful-android-apps.pdf.
The presentation itself can be found here: https://www.youtube.com/watch?v=xHXn3Kg2IQE.
This does however not mention how you should sync on startup. But you could just set a SharedPreference in the application class and then use one of the patterns to sync in the background.
I'd recommend you instead of using Application to download your stuff to use a Service. If by any reason your app gets killed by the OS your dl's will never complete. Using a Service it will.
Also, you can use a Broadcast to your activity to signalize that the service has completed downloading and taking the necessary following steps.
first: I think it's bad to place the download at appliaction onCreate
as stated in documentation
Called when the application is starting, before any activity, service,
or receiver objects (excluding content providers) have been created.
Implementations should be as quick as possible (for example using lazy
initialization of state) since the time spent in this function
directly impacts the performance of starting the first activity,
service, or receiver in a process. If you override this method, be
sure to call super.onCreate().
pay attention to this part
Implementations should be as quick as possible (for example using lazy initialization of state) since the time spent in this function directly impacts the performance of starting the first activity
so any delay in download or import may cause the first activity to be delayed.
and the behavior is not clear, a black screen maybe?
Second suggestions for download/import:
1- use an AsyncTask in the first activity, where you display a small progress bar indicating the download/import process or even block the whole UI until completed (based on your business)
2- add a splash screen while downloading the data
regarding fresh data, you can store a timestamp, last_updated
and before starting the download/import process, check that value, if less than your accepted value (say 1 hour) don't start the download/import.
finally, regarding onCreate() call, i think it's not called everytime, only when app is re-created, like 1st run after reboot, or after being killed or forced close.
I found and read a lot of articles talking about global variables in Android, some of them suggests using an subclass of Application + declare it in the manifest file as a glbal variable container.
But some articles mentioned that This class could also be killed when system memory gets low, is this correct?
So, is it 100% reliable to use an Application subclass as a global variable container? And could somebody give me a link to some documents explaining the life cycle of an application in Android (not activity)?
EDIT:
Thanks for the answers, I think I need to explain a bit more of my question.
The situation is, I just want to share a global String variable, Activity A modifies it, and activity B reads it.
When B is currently visible and user receives a call,
If A and B are killed but Application keep untouched (is this what Google calls an empty process?), I'm OK with it.
If A, B, and Application class are all killed and when user come back my app gets a clean start, I'm OK with it.
Which I'm not OK with it is, everything was killed including the Application class, when user come back my app doesn't start fresh, I mean, it starts from Activity B, will this happen? then should I start A manually or let Application class to do the initiation? none of these ideas looks good to me...
The answer is both "YES" and "NO" - the Application class can be used as a "global variable container" in that all activities and classes can rely on it.
However, you cannot rely on the Application class (or any other class) to persist indefinitely. In other words, if the user answers their phone, your application could be destroyed and then re-created when the user completes the call and returns to your app. So, you definitely cannot rely on it to persist data, but you can rely on it to provide global access to data when you app is active.
This is the Android documentation:
http://developer.android.com/training/basics/activity-lifecycle/index.html
This is a really good post on it - read the SECOND highest voted response, in addition to the "accepted" response:
Using the Android Application class to persist data
It explains pretty clearly how your app can be killed/destroyed whether you expect it or not.
EDIT:
To clarify, if you have a variable (call it "myVar") in the Application class and a user sets it in Activity A, then proceeds to Activity B, Activity B will be able to read the change.
If Android "destroys" the application class, which can occur anytime the user is not in your app (and in rare instances even if they are...), the app will be reconstructed so that the Activity Stack is still valid but "myVar" is not set, unless you persist the data.
In other words, Android will recreate the Application class and Activity B, but there is no guarantee that it will recreate Activity A until the user does something to destroy Activity B. Also, it will certainly not "replay" the user actions in Activity A in order to recreate the app state, and in your case that means "myVar" is not reliable.
If you read the references provided, you will see that this is the case (and also I have tested it).
EDIT 2:
To persist data, consider SQLite (which is pretty complicated and there are many references) or SharedPreferences:
How to use SharedPreferences in Android to store, fetch and edit values
This class could also be killed when system memory gets low, is this correct?
Yes.
So, is it 100% reliable to use an Application subclass as a global
variable container?
If you want to use it to pass values it is not reliable.
why?
EDIT:
Which I'm not OK with it is, everything was killed including the
Application class, when user come back my app doesn't start fresh, I
mean, it starts from Activity B, will this happen?
yes. It is possible that you set a value in your application from activity A and then when you are in Activity B user leaves your app and after a while android kills your process. then user comes back wants to look at your app. android again recreates application and activity B but not Activity A and instead of fill the application variable with what has passed Activity A to it, it recreates it from default initialization. So simply you missed what has passed or set by Activity A.
Don’t Store Data in the Application Object
I am developing an Android application consisting of over 10 activities. I have some state objects that I access in almost every activity and for this purpose these were implemented as global static variables in the MyApplication class.
I noticed that this approach is OK as long as the user is "in" the application. However, when he presses the home button and opens another apps and then goes back to my app through the "Recent activities" button, I see that the Android system resets the statics from the MyApplication so I have to deal with NullPointerExceptions. I know that this behaviour is caused by Android killing and recreating the application process.
I know that the best way to persist this kind of data is using SharedPreferences or SQLite, and I have no problem checking if MyState==null in the onCreate for and restoring it, but the problem is that I don't know when to properly store my state object (in prefs or database). I tried to override MyApplication's finalize() - no good, I saw that onLowMemory may not be called, I don't see how can I use onPause, OnStop and so on because I have so many activities that the serialization de-serialization would considerably slow down the app.
Any thoughts?
Thanks in advance!
It is better to not depend on the Application class unless you need to load some data, before anything else is started. Android can kill your process at any time to free resources, so your app should be able to handle this. Save all of your data in a snigleton class, and load it lazily -- check for null, and if so load on first access. It the state needs to be persistent, consider staving it file/shared prefs. If not, your app can probably live without it, so just make sure you check for null, etc.
Generally, you should persist state when activities become inactive -- onStop(), onPause(), but you can save as soon as it makes sense (e.g., the user has entered all required data). Spin off an AsyncTask to save data in the background and let the user continue their work.
What is the best way to maintain session information across multiple activities?
The question What is the best practices on Android to keep data between activities deathes/restarts for the whole application session? has some ideas, but does not really help me.
*My application has multiple activities with the manifest tag singleTop. They essentially work as different tabs - they each maintain their own set of fragments and back stack and so putting it all into one activity would break navigation for the user.
I am currently saving the session data as a static singleton created by my Application subclass. This works fine most of the time, except when the entire application is killed by the OS to save memory (as mentioned in the above link), say, when the user gets a call on a device with low RAM.
The only notification the app has that it is going to be killed (as far as I know) is
Activity.onSaveInstanceState(Bundle outState)
So the problem is this: onSaveInstanceState will eventually be called on every activity, not just the top-most one (the one that will appear when the user eventually returns to my app). When each non-top activity resumes, I could use Activity.onRestoreInstanceState(Bundle savedInstanceState) to restore my singleton session, but non-top activities would have old copies of the data (they may have been navigated-away from long before the user got the call).
One solution would be to only restore data to the singleton session Only if it is currently empty, but this relies on the first activity to receive Activity.onRestoreInstanceState being the top activity. This is not always the case - if the user gets a call and then returns to the app via the launcher icon, then the Main activity will be resumed first and brought forward, not the activity the user was on when they got the call.
A simple notification in the Application class that the application is being killed by the OS (not the user) is really what I need - I would then save the session to a file, and read it back on the first call to Activity.onRestoreInstanceState, but AFAIK this doesn't exist.
If you have some data that you want to store when app closes and reload them when app starts, you can have a mechanism to store and retrieve them on application's shutdown and startup. To do this override onStop() and onStart() methods on Activity's lifecycle.
And I think the best way to store and retrieve data in these two methods is SharedPreferences.
I would simply insist to use Application class to save the session. Using Application class you will be able to access the session everywhere were you are having Context so probably you can access in your whole Application. Application class also maintains the value after you Application is closed so its better to clear previous session when your Application is re-launched.
As the main use of Application class is to maintain global state of variables so that they can be used throughout the Application with updated values.
On the first time my app is running, on the root activity, the user is required to select a certain options that determines which data would be downloaded for him from a database.
Once he had picked that option, the data is downloaded and kept on a singleton class that should hold that data as long as the application is running. (Even in the background)
My problem is that sometimes after the user exits my application for a while (using the home button), Android apparently kills some of the activities in my app, and somehow with them, it resets the important singleton class. Causing my app to receive all kinds of null reference exception once I try to access the data that is supposed to be kept on the singleton.
I understand that the Android OS can sometimes choose to kill an application, but allow the user to return to it from his last visited activity. Sometimes it just kills the whole application and forces the user to begin from the start.
I was looking for a solution, and I found out about "android:alwaysRetainTaskState" attribute that I can apply on my root activity. Apparently with it, whenever android decides to kill my app, it would at least force the user to begin from the first activity where I can re-download the data, instead of allowing the user to begin with a more advanced activity causing him to get null exceptions.
However when I applied it on my application it did not work, and when I returned to my application I was beginning from an advanced activity, with an empty singleton instance.
I also tried "android:clearTaskOnLaunch" and it worked, but it's an overkill, since I don't want that every time the user would return to the app, he would have to start over. I want it to happen only if android decided to kill the app.
Is there something I'm doing wrong? Is there any chance maybe there's a better solution for keeping the singleton alive?
Thanks!
Nope. There is not better solution. This is how it works. Android can kill your application's process whenever it wants to (as long as it is in the background). You can keep data in a singleton, but it is always possible that Android kills your process anyway. When the user returns to your application Android will create a new process and then create a new instance of the activity that the user is resuming. You need to put code in onCreate() of all your activities that recognizes that your singleton is gone and then does an appropriate thing (either redirects to the root activity of your application or reinitializes your singleton.
It is pretty easy to tell if your process has been killed and recreated. Just add a static boolean variable to your singleton that you set to true when it has initialized. In onCreate() of all your activities, check the state of that variable and do something intelligent if the variable is false.