I have extended the Application object as MyApplication and the design is as follows
public class MyApplication extends Application {
private MyObject myObject;
public MyApplication () {
mInstance = this;
}
public static MyApplication getInstance() {
return mInstance;
}
public MyObject getMyObject(){
return myObject;
}
public setMyObject(MyObject object){
myObject = object;
}
}
I am accessing the myObject value in Activities as well as Services as MyApplication.getInstance().getMyObject(). I don't see any issue with this normally, but in production for few users sometimes suddenly getMyObject() is returning null i.e getting reset. First I was under the impression that the OS might be killing and re-creating the app due to which I am observing null. I am not avoiding the memory low scenario, however if that is the case it should be killing the Activities and the Services too before destroying the Application object.
Inside an Activity I am keeping a reference of MyApplication as
private MyApplication myApp = MyApplication.getInstance();
So this is how my observation from logs regarding the getMyObject() value.
myApp.getMyObject() prints != null
then a Service MyService gets called and inside that
MyApplication.getInstance().getMyObject() prints == null
again back in the activity myApp.getMyObject() or MyApplication.getInstance().getMyObject() prints != null
myObject has been initialized before printing these logs and in between these logs there is no myObject reset code getting called.
Can it happen that the Application object got re-created but the Acitivty is still active?
or
Can Service get a different instance of Application than that of the Activity thread?
As per my understanding in a app life-cycle myApp should always be equal to MyApplication.getInstance() as OS should maintain single instance of Application.
Note: Also would like to add that I am getting this un-usual behavior in Samsung Tab 4.
Don't store Data in the Application Object
The applicataion object will not stay in memory forever. When a user sends your application to the background by e.g. pressing the home button the application object might be killed if the system requires the memory. This might happen after minutes but could take hours. When the user then resumes the application the application object might not be the same as before and mMyObject will be null if it has no initial value.
Solutions
Make null checks and act accordingly
Use some form of data-storage
Initialize your object in the application constructor
public MyApplication () {
mInstance = this;
mMyObject = new MyObject()
}
You don't wanna have public constructor, but a onCreate() method.
You also want a static instance of the class.
public class MyApplication extends Application {
private MyObject myObject;
private static MyApplication mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static MyApplication getInstance() {
return mInstance;
}
public MyObject getMyObject(){
return myObject;
}
public setMyObject(MyObject object){
myObject = object;
}
}
And of course change your manifest to tell that's this is your class that will handle the application (if not done yet)
<application
android:name="com.XXX.MyApplication"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
Related
I have some Apps where I use a static reference of the Application instance.
That's usefull when I need to use context in some singleton Class that has application lifetime.
Here an example:
public class MyApp extends Application
{
private static MyApp smApplication;
public MyApp()
{
MyApp.smApplication = this;
}
public void onCreate()
{
super.onCreate();
MyApp.smApplication = this;
}
public static MyApp getApp() {
return MyApp.smApplication;
}
}
class Settings // Example of Singleton class
{
...
public boolean loadSettings()
{
Context context = MyApp.getApp(); // NullPointerException on the next line
SharedPreferences pref = context.getSharedPreferences(SHAREDPREF_FILENAME, Context.MODE_PRIVATE);
...
}
}
This works like a charm in the majority of the cases.
But... I have a number of NullPointerException crashes reported in the Play Console about that Context. Usually called in the MainActivity.onCreate or MyService.onCreate.
Application should Always be created before any other Activiy or Service. So I can't understand what happens. I've never had a similar crash on any of my devices.
smApplication is initialized in the constructor and never set to null.
It seems that in that cases Application is not created. It's also weird that all these crashes are reported in the PlayConsole but I cannot find them in Firebase Crashlitics as if neither Crashlitics has yet initialized in that cases (and FC shoul be initialized by the Application)
This has been happening for years in a small part of my users.
Any suggestion?
PS. I Do not need suggestion on code refactoring, just to understand what happens and how to avoid it.
Is it possible to make an activity singleton?
I have found many resources that just tell to use android:launchMode="singleInstance" or singleTask, but I would constructor to be called only once.
Ideally, I would like to be able to specify custom constructor/builder method e.g. getInstance()
You could store your references in Application instead of an Activity. The application class is de facto a singleton. You only need to define your access methods.
public class BaseApplication extends Application {
private static BaseApplication sInstance = null;
public synchronized static BaseApplication getInstance() {
return sInstance;
}
public synchronized static void setInstance(BaseApplication app) {
sInstance = app;
}
public BaseApplication() {
}
#Override
public void onCreate() {
super.onCreate();
setInstance(this);
}
Now you can access it by calling BaseApplication.getInstance(). As a bonus the Application extends Context so now you have an application context reference anywhere you want (safe to use pretty much everywhere except inflating layouts).
Don't forget to define this class as the base application class in your manifest:
<application
android:name="com.yourapp.BaseApplication">
Usually they do as follows:
1) define what comprise the Activity state
2) Save the state in onSaveInstanceState
3) Restore the state in onCreate or in onRestoreInstanceState
I got a singleton class in my application, which is defined just somewhat like:
public class SingletonTest {
private SingletonTest() {}
private static SingletonTest instance = new SingletonTest();
public static SingletonTest getInstance() {
return instance;
}
}
When I exit my application and open again, the instance has not been initialized again because the former one is not destroyed and still in JVM. But what I want is to initialize the static field every time I enter my application. So, what should I do in the onDestroy() method? Thanks a lot!
Your static variable will remain in memory as long as your application stays in memory.
This means that, a static variable will be automatically destroyed together with your app.
If you want a new instance of your singleton, you will need to create a static method that reinitializes your singleton and call it in the onStart of your application object or the first activity you launch(or whenever you need it)
private Singleton() {}
private static Singleton mInstance;
//use this method when you want the reference
public static Singleton getInstance() {
//initializing your singleton if it is null
//is a good thing to do in getInstance because
//now you can see if your singleton is actually being reinitialized.
//e.g. after the application startup. Makes debugging it a bit easier.
if(mInstance == null) mInstance = new Singleton();
return mInstance;
}
//and this one if you want a new instance
public static Singleton init() {
mInstance = new Singleton();
return mInstance;
}
something like that should do.
From what you are saying, it seems that Singleton is not suited for what you want to do. You should declare an instance variable that would be initialized/cleared by the methods onCreate()/onStart() and onStop()/onDestroy().
See this graph for the Activity lifecycle.
Source : http://developer.android.com/reference/android/app/Activity.html
I am having trouble saving the state/singleton of my application.
When the application starts a loading screen (activity) is shown and a singleton is initialized with values from a webservice call (note that network access cannot run on the main thread).
After the singleton is created I open my main activity. Note that values from the singleton are required to build the layout.
Now assume the app goes in the background and is killed there (e.g. because of low memory). My singleton instance is deleted as the app is killed. When I switch back to my app it tries to recreate the main activity. As I mentioned earlier the values from the singleton are required to build the layout, so this leads to a NullPointerException (when I try to access members of the singleton, as it is not there anymore).
Can I somehow tell android to start the first loading activity after the app was killed? It would be great if I could refresh the singleton before the layout is recreated, but this seems to be a problem as network calls can not be on the main thread and therefore not block until the refresh is finished.
I assume that I could save the singleton in all activities onStop and recreate it in the onCreate methods, but this seems a bit too unpredictable and would probably lead to a inconsistent state...
Another way could be to just always finish my activity onStop, but this would lead to losing on which tab the user last and so on, even if the app is not killed, so this is not a good option.
Any ideas on how to solve this?
Why not just use a SharedPreferences instead of a singleton?
Anytime you want to save some global state, commit it to preferences. Anytime you want to read the global state, read it back from preferences.
Then you don't have to concern yourself with application lifecycle at all, as your data will always be preserved regardless of what the phone is doing.
For something like that I used a pseudo singelton object as a Application class. This object will be created on the beginning and will be in the memory. But note that the system will terminate the application if the memory is needed by other applications. However this object is persitent even if all activities are temporally terminated.
To use that you need to declare that in your android manifest like here:
<application android:label="#string/app_name"
android:icon="#drawable/icon"
android:description="#string/desc"
android:name=".MySingeltonClass"
...
Here is a code example:
public abstract class MySingeltonClass extends Application {
// ...
public void informClientOnline() {
clientOnline=true;
Log.v(LOG_TAG, "Client is online!");
}
public void informClientShutdown() {
clientOnline=false;
Log.v(LOG_TAG, "Client is going offline. Waiting for restart...");
Timer t=new Timer("shutdowntimer", false);
t.schedule(new TimerTask() {
#Override
public void run() {
if(!clientOnline) {
Log.v(LOG_TAG, "Client has not restartet! Shutting down framework.");
shutdown();
System.exit(0);
}
}
}, 5000);
}
}
this two functions are called like this:
((MySingeltonClass)getApplicationContext()).informClientOnline();
You could save your Singleton when onSaveInstanceState() in the Activity gets called. All you need to do is to make it implement Parcelable (it's Androids own form of serialization), then you can put it in the outState Bundle in onSaveInstanceState() which will allow you to retrieve it laver in onCreate() or onRestoreInstanceState() in the Activity, whichever you like.
I've included an example for you:
public class TestActivity extends Activity {
private MySingleton singleton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState.containsKey("singleton")) {
singleton = savedInstanceState.getParcelable("singleton");
} else {
singleton = MySingleton.getInstance(5);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("singleton", singleton);
}
public static class MySingleton implements Parcelable {
private static MySingleton instance;
private int myData;
private MySingleton(int data) {
myData = data;
}
public static MySingleton getInstance(int initdata) {
if(instance == null) {
instance = new MySingleton(initdata);
}
return instance;
}
public static final Parcelable.Creator<MySingleton> CREATOR = new Creator<TestActivity.MySingleton>() {
#Override
public MySingleton[] newArray(int size) {
return new MySingleton[size];
}
#Override
public MySingleton createFromParcel(Parcel source) {
return new MySingleton(source.readInt());
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(myData);
}
}
}
When my application goes to background , my (static and singleton) objects are cleared.
So I tried to store these objects in Applicaton Context . I am using the following code.
Accounts.create(getApplicationContext()) will be called once to store the accounts instance.
Is that possible(reliable) to store objects in Application Context ? I am not sure the following way is correct or not . please guide ..
public class Init extends Application {
private Hashtable<Object, Object> globalStore = new Hashtable<Object, Object>();
public void putToGlobalStore(Object key, Object value) {
globalStore.put(key, value);
}
public Object takeFromGlobalStore(Object key) {
return this.globalStore.get(key);
}
public void removeFromGlobalStore(Object key) {
this.globalStore.remove(key);
}
public boolean containsInGlobalStore(Object key) {
return this.globalStore.containsKey(key);
}
}
public class Accounts {
protected Accounts(String name, Context context) {
Init init = (Init) applicationContext;
init.putToGlobalStore(name, this);
}
private static Init applicationContext;
public static void create(Context context) {
if (context instanceof Application)
applicationContext = (Init) context;
else
applicationContext = (Init) context.getApplicationContext();
if (applicationContext.containsInGlobalStore(GLOBAL_NAME))
Logger.log("Warning " + GLOBAL_NAME
+ " is already created. This will remove all old datas");
new Accounts(GLOBAL_NAME, applicationContext);
}
private static final String GLOBAL_NAME = "accounts";
public static Accounts getInstance() {
try {
return (Accounts) applicationContext
.takeFromGlobalStore(GLOBAL_NAME);
} catch (Exception e) {
Logger.log("GLOBAL_NAME Lost");
return null;
}
}
Please help.
You should know that the application context itself gets destroyed if left unused for a long time in the background. So there is no guarantee that your static and singleton objects will not be cleared when the app is in background. Instead what you can do is persist your objects from time to time (either in a flat-file or shared preference or database) and restore them in the onCreate method of the Application class
I have been using this method in my application and i didn't see any problem unless my process gets killed by the OS or if there is a crash in my application and my app gets restarted.
If you think whatever data you are storing is valid for only life time of a program why don't you override OnCreate of Application object and create all your singletons there. This way you can always make sure your application has all singletons before your app starts functioning.
Application class is not permanent.
If App process killed, Application class private member variable data loss.
Using Shared Preferences.
I know this question was asked a long time ago, but here's a good article that suggests using the Application object to store data is generally not a sound design methodology.