Exception recreating Android Fragment after App dies in the background
On a recent Android development project, we encountered a tricky, and at first difficult to reproduce issue. We would open the App, either from the recents list or the launcher icon, and the App would immediately crash. Start it again, and everything was fine. Digging into the logs, we found that there was a consistent exception:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.artermobilize.aea/com.artermobilize.aea.MainActivity}: java.lang.NullPointerException ... Caused by: java.lang.NullPointerException at java.util.ArrayList.(ArrayList.java:93) at com.artermobilize.myApp.ItemListFragment.onCreateView(ItemListFragment.java:73) ...
The offending line of code was in the first line in the onCreateView method in a fragment class, which looked like this:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { AlertAdapter adapter = new ItemAdapter(inflater.getContext(), R.layout.alert_row, new ArrayList<Alert>(items)); setListAdapter(adapter); return super.onCreateView(inflater, container, savedInstanceState); }
It was pretty clear from the stack trace that the exception was happening because the items variable we were initializing the ArrayList with was null when onCreateView was executing. When stepping through the code in the debugger, it always had a value, so what gives?
I finally came across a StackOverflow post that pointed us in the right direction. The key comment there for us was:
… the application is restored to its previous state because it was killed by the OS due to low memory while it’s in the background. Upon task switching back it’s restored to that previous state …
When an Android App is killed in the background, when you then task switch back to it the OS will try to restore the Activity/Fragment that was last in the foreground. This does not follow the same sequence of execution as when the App is first launched. In our case, the items variable was be populated from data contained in a static variable in another class. That static variable would be trashed by the OS along with the rest of the App, and the code to populate it was not being executed because it resided in a different Activity. Understanding how the Activity/Fragment was being recreated now, we were able to readily reproduce the condition by sending the App to the background, and running an ADB command to kill the process, which made the static variable null every time:
adb shell am kill com.artermobilize.myApp
With that realization, our solution was to reload the static variable if it’s null when the Fragment is created. We wanted “fresh” data anyway, so in our scenario this made sense. Another possible approach would be to hook into the default Bundle instance state, which the system already uses to store the state of each View object in order to recreate the Activity/Fragment. Google provides guidance on how to extend this to support recreating an Activity.
I had also same issue that app was crashing after I restarted it the next day. I turned out that I forgot to implement Serializable interface for one of the objects passed to the Fragment. Using your “adb shell …” – command I could reproduce it quite easily – thanks a lot, Sir!
You’re very welcome! Glad that command helped you out.