I have implemented simple drill-down pattern with several activities that pass data to display. But when I go back (either android back button or toolbar's back button) my app crashes in onCreate
because parameters are gone.
Ok, there are hundreds similar questions. Please read on. I did investigation and even senior developer reviewed my code and he did not understood.
This is a log how it was called:
TestItemsActivity.onCreate()
TestItemsActivity.onStart()
TestItemsActivity.onResume()
TestItemsActivity.onPause()
RunTestActivity.onCreate()
RunTestActivity.onStart()
RunTestActivity.onResume()
TestItemsActivity.onSaveInstanceState()
TestItemsActivity.onStop()
RunTestActivity.onPause()
TestItemsActivity.onCreate()
The last sentence is important - why is onCreate
called again? Activity lifecycle says that onResume
is expected.
Let's look at the source code. I use appcompat 7:25.1.
TestItemsActivity
public class TestItemsActivity extends AppCompatActivity {
TestScript script;
protected void onCreate(Bundle state) {
log.debug("onCreate()");
super.onCreate(state);
setContentView(R.layout.act_script_items);
if (state != null) {
script = (TestScript) state.getSerializable(ScriptListActivity.KEY_SCRIPT);
} else {
script = (TestScript) getIntent().getSerializableExtra(ScriptListActivity.KEY_SCRIPT);
}
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Intent intent = new Intent(TestItemsActivity.this, RunTestActivity.class);
intent.putExtra(RunTestActivity.KEY_POSITION, position);
intent.putExtra(ScriptListActivity.KEY_SCRIPT, script);
TestItemsActivity.this.startActivity(intent);
}
});
}
protected void onSaveInstanceState(Bundle state) {
state.putSerializable(ScriptListActivity.KEY_SCRIPT, script);
super.onSaveInstanceState(state);
}
AppCompatActivity
public class RunTestActivity extends AppCompatActivity {
protected void onCreate(Bundle state) {
log.debug("onCreate()");
super.onCreate(state);
setContentView(R.layout.act_script_items);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbarCalc);
if (toolbar != null) {
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
moveTaskToBack(true);
}
});
}
Intent intent = getIntent();
script = (TestScript) intent.getSerializableExtra(ScriptListActivity.KEY_SCRIPT);
}
And now the most interesting source code is the manifest:
<activity
android:name=".activities.TestItemsActivity"
android:label="@string/action_academy"
android:parentActivityName=".activities.ScriptListActivity">
</activity>
<activity
android:name=".activities.RunTestActivity"
android:label="@string/action_academy"
android:parentActivityName=".activities.TestItemsActivity">
</activity>
Accidentally I found that if I remove parentActivityName
than android's back button does not start new activity (onCreate) and my app does not crashes! Unfortunatelly toolbar's back button disappears.
Some SO questions I read:
- Saving Android Activity state using Save Instance State
- save the state when back button is pressed
- How to save activity state after pressing back button?
- Bundle savedInstanceState is always null
- SavedInstantState created during onSaveInstanceState not restored in onCreate
Can somebody explains what is going there?
Complete stacktrace including parts that I removed from source code above. It fails in adapter because scripts
instance is null. The activity failed to initialize it either from intent or bundle (which is null despite that onSaveInstanceState
was called)
java.lang.RuntimeException: Unable to start activity ComponentInfo{lelisoft.com.lelimath.debug/lelisoft.com.lelimath.activities.TestItemsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List lelisoft.com.lelimath.data.TestScript.getItems()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List lelisoft.com.lelimath.data.TestScript.getItems()' on a null object reference
at lelisoft.com.lelimath.adapter.TestItemAdapter.<init>(TestItemAdapter.java:97)
at lelisoft.com.lelimath.activities.TestItemsActivity.onCreate(TestItemsActivity.java:55)
at android.app.Activity.performCreate(Activity.java:6679)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
Update:
Workaround for this problem is to use a theme without a toolbar:
<activity
android:name=".activities.CampaignActivity"
android:label="@string/action_academy"
android:theme="@style/AppTheme.NoActionBar">
</activity>
Unfortunatelly I need the toolbar for one activity.