I've read many articles about how bad global variables in the application class or in Singletons are. The biggest issue for me was that it causes a NullPointerException when the app gets killed by the system and the user restarts it. The app restarts with the Activity where the user was before and not with the first Activity of the app. The global variables don't stay in memory forever so starting the last Activity causes a NPE (if you don't check for null manually).
All those examples are using apps with multiple activities though. Does this problem still exist with single activity applications?
I've tried to replicate the NullPointerException in my app but on all my devices the app restarts with the first fragment and therefore the app does not crash.
Yes, they do. The reason why it didn't crash my app was that I had no fragment where I accessed the singleton's data without creating it first. I fixed it by replacing the singleton with a room database.
Related
I am very familiar with the Android activity lifecycle, but I feel like I am missing something that should be pretty obvious here.
I have a multi-activity Android app; it uses static variables (in the application class) to keep a handle on the various objects that are used across the different views. The problem occurs when the app is paused (exited) and Android (presumably) cleans up memory to maintain foreground processes. When the user resumes the app, it occasionally (not always) seems to come back and resume in the Activity that the user left, but with the static variables nulled. Even worse, the activity sometimes seems to resume with the static variables still present, but with internal variables in objects nulled.
I eventually implemented a simple sanity check, which checks that the statics are not nulled (and also the most vital inner variables of relevant objects) and returns the app to start if it fails. This did cut down on a lot on the problems, but I still see the occasional issues with this, as it is simply not practical to check everything for every resume().
What I really need is for the app to restart from scratch if the Android OS decides it needs to clean anything non-GC from memory while the app is inactive. I feel there should be a graceful way to do this, but haven't noticed anything in the documentation.
Any thoughts? How do others avoid this problem?
Using the Application class for preserving state can result in unexpected behaviour if Android decides to kill your process completely. Check out this answer to a similar question
i.e. you should probably use some sort of persistence (SharedPreferences or some DB) in your Activity lifecycle callbacks.
I have a public variable set in my Main Activity that declares the App as the free version or paid (boolean).
I am receiving a lot of crash errors (null pointer exceptions) when it is accessed from a separate activity. I can't reproduce this error on test devices so I can only assume that Android loses the Main Activity Variable when it is low on memory?
Typically this happens when my users navigate to the browser to download a file and then navigate back to my App.
Should I be setting a variable local to the Activity so it no longer references a separate Activity? Or is there a way to keep that variable in memory?
You should use some sort of persistent storage, SharedPreferences seem like a good option for the task at hand.
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.
I noticed that when my application encounters an error, the value of my application context variables are also reinitialized to its original value (not the updated value). Based from my understanding, this happened because the application was recreated.
How can I save and restore the values of my application context variables when an application error occurs? I'll also be glad if you could give a more detailed explanation on how things are working on the background of my application when it encounters an error.
Note: I read that one of the solution for this is by using SharedPreferences. However, SharedPreferences saves the data even when the application is dead. I don't want to save the data when the application is dead. I only want to save the data when the application is alive or on background.
How can I save and restore the values of my application context variables when an application error occurs?
First, don't have an unhandled exception.
Second, don't rely on static data members or custom Application subclass instances. There are many scenarios in which your process will be terminated and those values go away. They should be used for an in-memory cache of persistent content, and little else.
Sometimes, unhandled exceptions are truly unexpected, but these should be infrequent and usually tied to specific devices (e.g., ran out of storage space). Everything else represents a bug in your app, and you must fix the bugs.
In my application, I use a subclass of the Application object to store some references to complex objects I need to access from all of my activities. When my app starts, the startup activity checks one of these references, in this case a Location, and if it is null, it starts the LocationListeners which populate the reference for further use.
If I back out of the app to the launcher screen, and re-launch it, the Application object still has the reference from the previous use a few moments prior. This is fine, and is what I'd expect, but I'm curious how long the Application object is kept around once I've back'ed out of my application? (onDestroy() has been called on all activities, nothing in the stack.)
When is it finally killed? I know it does finally get killed as when I've not used the app in a while, it will search for location on startup (indicating the aforementioned null reference.)
(Also, is storing refs there like that a good idea?)
Thanks in advance gang! :-)
It stays around as long as the application is in memory. It only finally goes away when either someone forcible terminates your application (either using a task killer or from the application settings) or when Android decides it wants to reclaim the memory that your app is using (and this typically only happens when your app has been closed for a while and the phone is running low on memory).