This morning this post showed up in my stream by +Philippe Breault. About the caveats when treating the Application object as global state.
Although the advice that he gives, don’t store stuff in the Application object, is sane the example doesn’t really illustrate the problem. His argument is that when the activity is recreated, the application is recreated and therefore the state is lost. While this is true, the real issue is that the GreetLoudlyActivity from his example isn’t saving any state, which it should.
Luckily, this is easy to fix! A little while ago I wrote bundles, a set of two annotation processors for Android. The @Frozen annotation makes it easier to save and restore state in Activities and Fragments. So let’s fix the GreetLoudlyActivity:
class GreetLoudlyActivity extends Activity { | |
TextView textview; | |
@Frozen | |
String mName; | |
void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.reading); | |
textview = (TextView) findViewById(R.id.message); | |
// because we are still referencing the global application singleton, that may not be initialized, | |
// we need this null check to see if the activity is restored | |
if (savedInstanceState == null) { | |
MyApplication app = (MyApplication) getApplication(); | |
mName = app.getName().toUpperCase(); | |
} else { | |
GreetLoudlyActivityState.restoreInstanceState(this, savedInstanceState); | |
} | |
} | |
void onResume() { | |
super.onResume(); | |
// removed the reference to getApplication from onResume | |
textview.setText("HELLO " + mName); | |
} | |
void onSaveInstanceState(Bundle outState) { | |
// this class is generated by the @Frozen annotation | |
GreetLoudlyActivityState.saveInstanceState(this, outState); | |
} | |
} |
If you don’t want to use custom code for it, this is how you would do it without @Frozen:
class GreetLoudlyActivity extends Activity { | |
TextView textview; | |
String mName; | |
void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.reading); | |
textview = (TextView) findViewById(R.id.message); | |
// because we are still referencing the global application singleton, that may not be initialized, | |
// we need this null check to see if the activity is restored | |
if (savedInstanceState == null) { | |
MyApplication app = (MyApplication) getApplication(); | |
mName = app.getName().toUpperCase(); | |
} else { | |
mName = savedInstanceState.getString("name"); | |
} | |
} | |
void onResume() { | |
super.onResume(); | |
// removed the reference to getApplication from onResume | |
textview.setText("HELLO " + mName); | |
} | |
void onSaveInstanceState(Bundle outState) { | |
super.onSaveInstanceState(outState); | |
outState.putString("name", mName); | |
} | |
} |
Remember, there’s a setting in the development settings section of your phone (ICS or up) to test for these kinds of bugs. It’s the “don’t keep activities” option.