Todd Anderson
Todd Anderson

Reputation: 1168

Android: Activities destroyed unexpectedly, null savedInstanceState

I am not going to list every reference I've read through before posting this, but I have extensively read similar questions on stackoverflow and all of the android developer docs on activity/fragment lifecycles and maintaining state and have not found a solution.

Here is my scenario:

I have a main activity (ImpulseActivity) that uses a FragmentPagerAdapter. Each fragment displays a separate list of data that I am retrieving from the server. When pressing an action_item in ImpulseActivity's actionbar, you can filter data sent from the server. To accomplish this, ImpulseActivity launches a separate activity (FilterEventsActivity). FilterEventsActivity lists ImpulseActivity as it's parent activity. When pressing the back button on FilterEventsActivity, ImpulseActivity is recreated (OnCreate called) with a null (Bundle savedInstanceState). For testing purposes, I am overriding OnSaveInstanceState and placing fake data in outState. Note that this happens for every activity launched from ImpulseActivity.

My question is:

What is the proper design paradigm to prevent each fragment from needing to reload data in this particular situation? I would prefer not to use the Singleton pattern since my fragments are reused in other activities.

Relevant source code if needed:

public class ImpulseActivity extends FragmentActivity implements
    ActionBar.TabListener {

private MapSearchFragment mSearchFragment;
private BulletinFragment mBulletinFragment;

SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.impulse_activity);

    Log.v("ImpulseActivity", "onCreate " + savedInstanceState);

    final ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    CategoryManager manager = CategoryManager.getManager();
    manager.setListener(this);
    manager.loadCategories();

    mSectionsPagerAdapter = new SectionsPagerAdapter(
            getSupportFragmentManager());

    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.setAdapter(mSectionsPagerAdapter);
    mViewPager
            .setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                @Override
                public void onPageSelected(int position) {
                    actionBar.setSelectedNavigationItem(position);
                }
            });

    for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
        actionBar.addTab(actionBar.newTab()
                .setText(mSectionsPagerAdapter.getPageTitle(i))
                .setTabListener(this));
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putDouble("Hello", 1.02);
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.impulse_activity_actions, menu);
    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_create) {
        return true;
    }
    if (id == R.id.action_filter) {
        Intent intent = new Intent(this, FilterEventsActivity.class);
        startActivity(intent);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    int position = tab.getPosition();
    mViewPager.setCurrentItem(position, true);
}

@Override
public void onEventListClick(Event e) {
    Intent mIntent = new Intent(this, EventActivity.class);
    Bundle mBundle = new Bundle();
    mBundle.putSerializable(EventDetailsFragment.Event_Key, e);
    mIntent.putExtras(mBundle);
    startActivity(mIntent);
}

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    public SectionsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {

        Fragment fragment;
        switch(position) {
            case 0:
                if (mBulletinFragment != null) {
                    fragment = mBulletinFragment;
                } else {
                    fragment = new BulletinFragment();
                    mBulletinFragment = (BulletinFragment) fragment;
                }
                break;
            case 1:
                fragment = new MapSearchFragment();
                mSearchFragment = (MapSearchFragment) fragment;
                break;
            case 2:
                fragment = new MyEventsFragment();
                break;
            default:
                fragment = new BulletinFragment();
                break;
        }
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public int getCount() {
        // Show 3 total pages.
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        Locale l = Locale.getDefault();
        switch (position) {
            case 0:
                return "Bulletin";
            case 1:
                return "Map";
            case 2:
                return "My Events";
            default:
                return "Test";
        }
    }
}
}

AndroidManifest.xml

<activity
        android:name=".Home.ImpulseActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".Event.EventActivity"
        android:label="@string/title_activity_event"
        android:parentActivityName=".Home.ImpulseActivity" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".Home.ImpulseActivity" />
    </activity>

Upvotes: 3

Views: 891

Answers (2)

matiash
matiash

Reputation: 55350

Although the question mentions Fragments, the problem is actually with up navigation.

The default implementation for up navigation doesn't work exactly as one would expect for standard activties. In particular, when the parent activity has launchMode="standard" (the default), pressing the up button will create a new instance of it, not return to the previous one.

There are two alternatives for solving this problem:

  1. Changing the launchMode of ImpulseActivity to singleTop in the Manifest.
  2. Overriding the home button action to launch the intent with the FLAG_ACTIVITY_CLEAR_TOP flag. For example, in EventActivity.onOptionsItemSelected():

    if (id == android.R.id.home)
    {
        Intent intent = NavUtils.getParentActivityIntent(this);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        NavUtils.navigateUpTo(this, intent);
        return true;
    }
    

Either of these will bring your old activity to the top of the stack.

Upvotes: 6

er reflex
er reflex

Reputation: 82

if your fragments have layout just like activity example mainlayout.xml you can use intent for open each fragment with same details and dont use onstop(); pack create intent and intent flags in the main fragment activity

Upvotes: -1

Related Questions