Please, don’t use Singletons to persist state on Android
While studying our crash logs, I came across a crash in a 3rd party library we use. The crash was a NPE ( Null Pointer Exception ). Thankfully this 3rd party library was developed open source, and the code was available online.
I went to Github to examine the library’s code. Low and behold it came down to a singleton. A singleton responsible for holding the state or session data for the application. A specific task would pull down data from the network, store it, and display it. The problem is in how the network data was persisted. It was persisted in a static singleton instance.
I too have made this mistake before. Storing application state in a singleton. I too have been burned, and learned the lesson that singletons holding state can lead to unexpected NPEs. The first Android app I did, stored network data this way. I remember battling to keep the NPEs from happening before understanding the root problem.
You see the Android system can kill your application at any given time. When it does all your static variables go away too. The funky thing is though, the application can be killed, and you won’t restart from your MAIN LAUNCHER activity. If the user did not specifically kill the app, the app stays in the recently used apps and the user can restart it from the activity it was suspended on.
What was happening in this 3rd party code, was they were under the assumption that the singleton would be initialized and filled with data in the first activity. By the time the second activity was reached, which is only accessible via the first activity, it was thought the data would be there.
What can happen and is very valid is that the second activity can be started with out that first activity happening. Then when those fields on the singleton are accessed they will be null. Boom! NPE.
Thankfully, this is really easy to simulate. If you are getting NPEs that are unexpected in your crash logs, reproducing them with 100% accuracy is easy to do.
Open up the Android device monitor. Mac screenshot:
Run your app and navigate to the activity that is crashing. Find your app’s process, and click stop in the Android device monitor.
Goto to your recent apps list on your device, and restart the app. The app will then attempt to restart from the activity you were last at.
Notice that the app will crash and if you have a NPE caused by expected fields. Especially fields on a Singleton you initialize form another activity.
Another thing I recommend is running your app with don’t keep activities turned on. While this will not simulate your static variables being cleared, this will also simulate low memory situations that can occur. You see your app may not be killed completely but there is no guarantee that other activities in the background will disappear as your user leaves them.
Developing with that option on helps us stop NPEs as well. onActivityResult can cause some very peculiar bugs when you don’t expect it to get called on a new instance of the activity.
But back to singletons on Android that store state. Just don’t do it, and if you have to do it be aware of managing it to recover from all areas and activities in your app. Think atomically, because you never know in Android where your user will start from.
Some alternatives I suggest instead for persistence;
- an SQLite database. There are great libraries out there to make this easier. Room from Google is looking pretty delectable these days.
- Shared preferences, if it is something simple just use ‘em.
- Realm, 3rd party solutions are pretty awesome too!
Anything, but Singletons. Please!
Your own JSON file in your application directory. I won’t tell. Actually strike that from the record I did not just write that.
Got some suggestions on what people should use for persistence in an Android app please fire away in the comments. Let’s keep new Android developers informed, no more Singletons!