Public Variable accessed from separate Activity Crash Android - android

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.

Related

Do global variables still cause NPE if I only have one Activity?

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.

How does android do GC?

Now I'm facing some problem in Android when memory is low or the application is killed by system.
Scenario 1:
I set some static members in a class, I found in some situation , it will be deleted by system when the application is still running.
My problem to this is : when does this kind of GC run?
Scenario 2:
If I switch to another large application and then switch back to my application ( named App_A). App_A sometimes will be recycled by system and restart the last activity when it be switched back.
But there are some application-wide data (like login info) I saved in a singleton.
My problem to this is : Dose the application-wide data saved in singleton will be deleted?
If so, is there a appropriate way to restore the data?
My effort is:
To Scenario 1, I will avoid to use static member directly.
To Scenario 2, I will save those data into file , after it be deleted, I pass Context to each public function to let each of them have the ability to restore the data. But I think it will be unfriendly when the function is used in some situation which need run quickly.
I can only answer about Scenario 2.
Android will try to keep recently used apps in memory, but if the user switches to another app and memory starts running low, the OS has the option to kill the recently used app to make more memory available to running applications.
I had the same problem, where I had some user-context data like username in a static singleton. This data would disappear when returning to the app after using a number of other apps.
The way I solved this problem was to use the activity's intent. Since the user data was retrieved at the beginning of the app, I would simply pass this data to subsequent activities in their intents. Because the OS stores the intent and uses it to recreate an activity not in memory, my data was no longer vulnerable to being garbage-collected.
Also consider other means of persisting data: Shared Preferences, file system, SQLite database. But never count on static data from previous execution being available at the start of an activity.
It is generally bad idea to use singleton to save some data.
Best practice is using any persistent storage - SQLite, Realm,JSON, or any file.
Easiest way is saving your login data for ex. in JSON - then in Application class parse it in onCreate method into POJO - then you can get it from any place of your app. And store to file when app is closing or on any change.
Anyway I suggest you to read Android guides about persistence, memory management and performance tips.

Can I use an Application subclass as a global variable container?

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

alwaysRetainTaskState doesn't work as expected. Singleton class is losing its state

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.

Value of Application Context Variables Lost on Application Error

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.

Categories

Resources