在片段恢复上恢复适配器

时间:2018-12-12 15:34:50

标签: android android-fragments android-recyclerview android-savedstate

我遇到了崩溃,如果android操作系统杀死了一个进程,当应用程序尝试从保存的实例重建活动和片段时,我的recyclerview的适配器始终为空。

这是我的等级:

Parent Activity    // destroyed when process is killed
  Nested View Pager Fragment // view pager containing four recycler view fragments
    Recycler Frag 1
    Recycler Frag 2
    Recycler Frag 3
    Recycler Frag 4

这些回收站片段中的每个片段都是完全相同的片段,除了,当父活动创建它们的新实例时,我调用了一个公开的setter方法,该方法设置了一个RecyclerView.Adapter类型的适配器(原因是每个碎片可能是回收站,但其显示的内容以及用户与之交互的方式可能有所不同)来自父活动的示例代码:

RecyclerFrag feedFrag = RecyclerFrag.newInstance();
feedFrag.setAdapter(new myCustomAdapter());

现在,我的最终用户遇到了崩溃,在该崩溃中,如果他们将此活动作为背景,并且操作系统决定终止该活动所持续的进程,它将无法正确恢复自身。从我看到的内容来看,除了适配器为null以外,所有内容都在还原中。然后,代码尝试访问适配器接口,并因NPE而崩溃。

我不知道为什么会这样。我还试图将精力放在恢复此类事件中的活动和片段上。我已经阅读了许多有关状态恢复的帖子和文档,但仍然找不到解决方案。这是我尝试过的解决方案示例,也许有人可以指出我的逻辑上的缺陷/差距。

让回收站碎片将其自身存储在fragmentManager中,并尝试在onActivityCreated方法调用中将其读回。

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    getFragmentManager().putFragment(outState, "myFrag", this);
    ...
}

然后:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if(savedInstanceState != null) {
        getFragmentManager().getFragment(savedInstanceState, "myFrag");
        ...
    }
}

对我来说,这感觉像是我错过了标记。片段获取对自身的引用然后尝试从中进行设置是没有意义的。更糟糕的是,它返回的片段具有空适配器。生活和学习。

接下来,我尝试将在父活动中创建的片段保存为viewPager实现,然后尝试在父onCreate中还原这些片段。父类如下所示:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    getSupportFragmentManager().putFragment(outState, "frag1", frag1);
    ...
}

然后:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState != null) { 
        getSupportFragmentManager.getFragment(savedInstanceState, "myFrag);
        ...
    }
}

关于这种方法,值得注意的是,当我使用onSaveInstance方法将片段保存到管理器时,我的adpater不为null,这很有意义。当我在onCreate方法中抓住它时,该片段适配器现在为空。

这使我最后的想法是,也许恢复适配器效率不高,因此不应该这样做。再说一次,对我而言,在Recycler片段内创建适配器没有意义,因为我希望该片段采用RecyclerView.Adapter类型的任何适配器实现。对于进一步的上下文,viewPager实现的类型为FragmentStatePagerAdapter,它提供了最小的方法来覆盖使其运行。据我了解,这正是您应该做的,父级FragmentStatePagerAdapter将为您处理其余的恢复过程。我在这个假设上错了吗?

非常感谢您对此事的任何投入。但是,我试图提供尽可能多的信息,如果您觉得可以为我提供更多信息,可以帮助我,如果您让我知道,我将很乐意更新我的帖子。谢谢!

最终编辑: 我最终找到了与该帖子标记为答案的解决方案相似的解决方案。我将在下面提供我的实际解决方案,以便将来任何人发现此帖子对他们的情况有所帮助。

  1. 在Recycler Fragments界面中删除公共setter方法。鉴于我总是在初始化片段后立即调用setter,所以这种方法有问题,当将来的开发人员必须使用代码时,很容易出错。
  2. 为枚举器中的对象创建一个枚举类型作为数据抽象。考虑到某些隐含的数据类型需要不同的适配器,这似乎也是合适的。我的意思是说我可能有一个汽车对象,但是根据我在用户旅途中所处的位置,汽车对象可能需要一个不同的适配器,其中包含诸如CAR_PREVIEW_SEARCHCAR_PREVIEW_SELECTABLE之类的枚举在onCreateView

1 个答案:

答案 0 :(得分:2)

在没有看到更多代码基础的情况下,我不能自信地解决您的实际问题,但是以下几行令人担忧;

  

这些回收站碎片中的每个碎片都是完全相同的碎片   除了,当父活动创建它们的新实例时,我   调用设置类型适配器的公开的setter方法   RecyclerView.Adapter

我可以建议另一种设置类型的方法吗?

在您的FragmentStatePagerAdapter中创建各种片段类型的枚举;

public enum Page {
    PAGE_ONE,
    PAGE_TWO,
    PAGE_THREE
}

然后在您的getItem(int position)实现中;

@Override
public Fragment getItem(int position) {
    CustomFragment fragment = CustomFragment();

    Bundle bundle = new Bundle();
    bundle.putInt(CustomFragment.INDEX, position); // You could swap this to whatever serialised information you need

    fragment.setArguments(bundle);

    return fragment;
}

在CustomFragment的onCreateView实现中,您可以像这样访问此捆绑包;

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

    RecyclerView.Adapter adapter;

    if(getArguments() != null) {
        switch (FragmentStatePagerAdapter.Page.values()[getArguments().getInt(CustomFragment.INDEX)]) {
            // Assign whatever adapter/values ect in here
        }
    } else {
        // setup various default states here
        adapter = SomeDefaultCustomFragmentAdapter()
    }

    // set adapters etc

    return view;
}

以上内容确保仅在确实有视图时才操作适配器。当系统重新创建片段时,所有内容都应正常运行。

希望这至少有所帮助。