Fragments being inflated with old data, when going back to an activity that was stopped

老子叫甜甜 提交于 2019-12-23 12:32:49

问题


Activity A has fragments. When it starts an intent to activity B, then when B.finish(), A executes onCreate() again.

But this time, even though A.onCreate() has a new PacksPagerAdapter and a new ViewPager, the fragments are shown with old data.

I can see that that onCreateView() is executed for each fragment, but it still has the old arguments since the static newInstance() wasn't called. Arguments are created when FragmentPagerAdapter's getItem(position) is called.

Here's how it's implemented -

public class PackActivity extends Activity {
    ...
    PacksPagerAdapter mPacksPagerAdapter;
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPacksPagerAdapter = new MyPagerAdapter(getFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mPacksPagerAdapter);
        mViewPager.setOffscreenPageLimit(PACK_PAGES - 1);

        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(int position) {
                        actionBar.setTitle(...);
                    }
                });

    }
    ...
}

public class MyPagerAdapter extends FragmentPagerAdapter {
...
    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class
        // below).
        return PackFragment.newInstance(myData);
    }
...
}

public class PackFragment extends Fragment {
...
    public static PackFragment newInstance(PackInfo pack) {
        PackFragment fragment = new PackFragment();

        Bundle bdl = new Bundle(2);
        bdl.putSerializable(EXTRA_PACK, pack);
        bdl.putInt(EXTRA_PACK_POSITION, pack.packNumber);
        fragment.setArguments(bdl);

        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View packView = inflater.inflate(R.layout.pack_fragment, container, false);

        // this has the old argument, since newInstance(...) wasn't called      
        PackInfo pack = (PackInfo) getArguments().getSerializable(EXTRA_PACK);
        ...

        return packView;
    }

...
}

Any idea why new fragments aren't being instantiated on the 2nd creation of activity A?

Update - Activity-Fragments lifecycle

The answer is, as Tal said, is that activity is being restored, so old fragments are being reattached instead of creating new ones.

After spending a lot of time studying this, I've found that there are typically 3 scenarios for activity-fragments lifecycle. Here are the scenarios, the lifecycle and how to reload fragment's old data:

New activity

Lifecycle: onCreate() ==> onResume() ==> Fragments are created and attached

No need to reload.

Restored activity

Lifecycle: onCreate() ==> Fragments inflated with old data and reattached ==> onResume()

In onResume(), remove all fragments and create new ones, either manually or automatically using an adapter -

        // Remove all fragments
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        for (int i = BuildConfig.PACK_PAGES - 1; i >= 0; i--) {
            Fragment f = findFragmentByPosition(i);
            if (f != null)
                fragmentTransaction.remove(f);
        }
        fragmentTransaction.commit();

        // This will force recreation of all fragments
        viewPager.setAdapter(packsPagerAdapter);

Resumed activity

Lifecycle: onResume()

Same as activity restore, remove old fragments and recreate.

Note: Some OS versions always restores activity, even when opening a SettingsActivity for a few seconds, and other (older) versions will always resume.


回答1:


my answer will be better if you'll post the fragment transaction you commit in activity A.

not sure that you know it or not - if Activity A is re-created when it pop back from back stack - it means that it restored from previous instance state.

in that case, you should not perform the transaction again, because - it already happens automatically via the super.onCreate() Activity method. in fact, if you'll perform the fragment trasaction in that case - you'll cause the same fragment to be added twice (2 instances)

you can know if currently onCreate() been called from restoring instance state by checking if savedInstanceState parameter is null or not.

my assumption is that you are not checking savedInstanceState, and performing the transaction anyway.

if I'm right, then the following change should fix the fragment duplication problem:

instead of:

public static class ActivityA extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        performFragmentTransaction();

}

write:

public static class ActivityA extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
           performFragmentTransaction();
        }
}

more info at - http://developer.android.com/guide/components/fragments.html




回答2:


When the user moves away from activity A to B, the FragmentPagerAdapter detaches the fragments in A from user interface and does not destroy the instances so that they can be reused when user returns back to A latter.

I'm not sure why onCreate of A is called after B finishes. If you don't want the old fragments to be attached to the user interface, instead you want them to be recreated, then you need to explicitly destroy them.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mPacksPagerAdapter = new MyPagerAdapter(fragmentManager);

    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.pager);

    // Remove the fragments if they already exist.
    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction currentTransaction = fragmentManager.beginTransaction();

    int count = mPacksPagerAdapter.getCount();
    for(int position = 0; position < count; position++) {
        long itemId = mPacksPagerAdapter.getItemId(position);
        // fragment name format maintained by adpater: "android:switcher:" + viewId + ":" + id
        String name = "android:switcher:" + mViewPager.getId() + ":" + itemId;
        Fragment fragment = fragmentManager.findFragmentByTag(name);
        if(fragment != null) {
            // if fragment instance exists, destroy it.
            currentTransaction.remove(fragment);
        }
    }
    // commit all the operations with state loss.
    currentTransaction.commitAllowingStateLoss();
    // immediately execute the operations
    fragmentManager.executePendingTransactions();

    // Rest of your code follows here.
    ....
}



回答3:


i think you should add your fragment to your view and commit.

write something like this in your "onCreate".

fragmentTransaction.add(R.id.container, YOUR_FRAGMENT);
fragmentTransaction.commit();

or replace the old one.




回答4:


This is the advertised behavior: Your fragments are being restored.

Actually, it should also be noted that it is merely chance that is causing Activity A to be recreated. It could be possible that Activity A still exists after B finishes which will not lead to execution of onCreate of A again.


Why newInstance is not being called?

This happens because the fragments are actually in the FragmentManager of the killed activity. Therefore, they are restored when the activity is recreated. The tags of the fragments are usually: "android:switcher:" + your_pager_id + ":" + your_fragment_position.



来源:https://stackoverflow.com/questions/25747996/fragments-being-inflated-with-old-data-when-going-back-to-an-activity-that-was

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!