如何从savedInstanceState

时间:2017-11-04 07:25:20

标签: java android performance android-fragments android-viewpager

所以我遇到了一个我似乎无法解决的问题。我在我的一个活动中有一个ViewPager,比如 MainActivity 。当活动在后台被杀死时,我正在实现保存和检索实例状态所需的所有方法。但是当活动尝试恢复其状态时,片段将被恢复,但它们不会附加到viewpager,所以我得到的只是一个白色屏幕。

以下是相关代码:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //..
    if (savedInstanceState==null) {
        setupViewPager();
    }
}

private void setupViewPager() {
    mAdapter = new ViewPagerAdapter(getSupportFragmentManager());
    mAdapter.addFrag(FirstFragment.newInstance(mFeedItem), "INFO");
    //Note: mFeedItem is obtained from the intent and I have verified
    //      that it is properly restored.
    mAdapter.addFrag(SecondFragment.newInstance(mFeedItem), "SERVICES");
    mViewPager.setAdapter(mAdapter);
    mTabLayout.setupWithViewPager(mViewPager);
}

ViewPagerAdapter.java

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFrag(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

FirstFragment.java

public static FirstFragment newInstance(Workshop item) {
    FirstFragment f = new FirstFragment();
    f.mItem = item;
    return f;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("feed_item",Workshop.packToGson(mItem));
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_item_detail_info, container, false);
    if (savedInstanceState!=null) {
        mItem = Workshop.extractGson(savedInstanceState.getString("feed_item"));
    }
    return v;
}

因此,如果我导航到另一个活动,然后MainActivity在后​​台被杀(我使用Don’t Keep Activities设置来测试),就会发生这种情况:

1)活动恢复正常,没有问题 2)调用onCreateView个片段,savedInstanceState完整无缺 3)但是,我的Activity中的ViewPager和Adapter是null。片段似乎没有附加到适配器/ viewpager。

那我在这里错过了什么?我应该做些什么来妥善恢复碎片吗?

PS:

我可以通过在MainActivity

中替换此问题来快速解决问题
if (savedInstanceState==null) {
    setupViewPager();
}

仅使用setupViewPager();,以便创建新的适配器和片段,无论它是第一次还原还是启动。但这带来了重置片段状态的缺点。此外,当我这样做时,我发现每个片段有两个实例,一个是恢复的,另一个是新实例,只实际显示了新实例。所以我不相信这是最好的方法。

2 个答案:

答案 0 :(得分:4)

通过销毁Activity,您还会销毁每个实例字段。因此,您必须重新实例化ViewPagerAdapter。您必须记住两种情况。

  1. FragmentStatePagerAdapter使用Fragments
  2. 保存/恢复FragmentManager
  3. 您必须恢复Fragments
  4. Fragment的状态

    第一点是每个Fragment有两个实例的原因。为了解决此问题,您可以通过 ID 获取存储的片段。 See this grepcode FragmentPagerAdapter(同样适用于StateAdapter

    private static String makeFragmentName(int viewId, int index) {
        return "android:switcher:" + viewId + ":" + index;
    }
    

    不是每次都在Fragment中实例化setupViewPager,而是先从FragmentManager获取,例如

    mAdapter = new ViewPagerAdapter(getSupportFragmentManager());
    int fragmentCount = 2; // save value if it's a arbitrary list of fragments
    for (int index = 0; i < fragmentCount; i++) {
        FirstFragment firstFragment = (FirstFragment) getSupportFragmentManager().findFragmentByTag(makeFragmentName(R.id.view_pager, index));
        if (firstFragment == null) {
            firstFragment = FirstFragment.newInstance(mFeedItem);
        }
        mAdapter.addFrag(firstFragment, "someTitle");
    }
    

    以上是有关如何从Fragments恢复以前保存的FragmentManager的示例。您可能需要调整具有不同类型BaseFragments的情况,依此类推。此外,您可能希望保存Fragment标题名称和总计数以恢复它们。

    第二点是您必须从Fragment本身恢复当前Fragment状态。为此,您的Fragment具有生命周期方法。请参阅此SO主题,了解Once for all, how to correctly save instance state of Fragments in back stack?

答案 1 :(得分:1)

FragmentStatePagerAdapter有saveState()和restoreState()方法来保存和恢复它的状态..

https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html#saveState()

  

saveState:保存与此适配器关联的任何实例状态及其在需要重建当前UI状态时应恢复的页面。

因此,似乎可以使用这些方法来存档您的目标。对不起,我现在没有编译器,所以我无法测试下面的代码。

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

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

private void setupViewPager(Bundle savedInstanceState) {
    mAdapter = new ViewPagerAdapter(getSupportFragmentManager());

    if (savedInstanceState != null) {
        Parcelable parcelable = savedInstanceState.getParcelable("Adapter");
        mAdapter.restoreState(parcelable, getClassLoader());
    } else {
        mAdapter.addFrag(FirstFragment.newInstance(mFeedItem), "INFO");
        //Note: mFeedItem is obtained from the intent and I have verified
        //      that it is properly restored.
        mAdapter.addFrag(SecondFragment.newInstance(mFeedItem), "SERVICES");
    }
    mViewPager.setAdapter(mAdapter);
    mTabLayout.setupWithViewPager(mViewPager);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Parcelable parcelable = mAdapter.saveState();
    outState.putParcelable("Adapter", parcelable);
}