简体   繁体   English

在片段娱乐上还原适配器

[英]Restoring an Adapter on Fragment Recreation

I'm running into a crash where if the android OS kills a process, when the application attempts to reconstruct the activity and fragment from a saved instance, my recyclerview's adapter is always null. 我遇到了崩溃,如果Android OS杀死进程,则当应用程序尝试从已保存的实例重建活动和片段时,recyclerview的适配器始终为null。

Here is my hierarchy: 这是我的层次结构:

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

Each one of these recycler fragments is the exact same fragment except, when the parent activity creates a new instance of them, I call an exposed setter method that sets an adapter of type RecyclerView.Adapter (reason being that each one of these frags may be a paginating recycler but the content it displays and how the user interacts with it may vary) example code from the parent activity: 这些回收站片段中的每个片段都是完全相同的片段,除了,当父活动创建它们的新实例时,我调用了一个公开的setter方法,该方法设置类型为RecyclerView.Adapter的适配器(原因是这些碎片中的每一个可能是分页回收站,但其显示的内容以及用户与之交互的方式可能有所不同)来自父活动的示例代码:

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

Now, my end users are running into a crash where if they have this activity backgrounded, and the OS decides to kill the process that this activity lives on, it does not restore itself properly. 现在,我的最终用户遇到了崩溃,在该崩溃中,如果他们将此活动作为背景,并且操作系统决定终止该活动所持续的进程,它将无法正确恢复自身。 From what I can see, everything is there on restore except the adapter which is null. 从我看到的内容来看,除了适配器为null以外,所有内容都在还原中。 The code then tries to access the adapters interface and crashes with a NPE. 然后,代码尝试访问适配器接口,并因NPE而崩溃。

I can not figure out why this is happening. 我不知道为什么会这样。 I am also trying to wrap my head around restoring activities and fragments in events like these. 我还试图将精力放在恢复此类事件中的活动和片段上。 I've read a bunch of posts and documentation about state restoring and still can not find a solution. 我已经阅读了许多有关状态恢复的帖子和文档,但仍然找不到解决方案。 Here is an example of solutions I have tried and maybe someone can point out the flaw / gap in my logic. 这是我尝试过的解决方案示例,也许有人可以指出我的逻辑上的缺陷/差距。

Have the recycler frag Store itself in the fragmentManager and attempt to read it back in the onActivityCreated Method call. 让回收站碎片将其自身存储在fragmentManager中,并尝试在onActivityCreated方法调用中将其读回。

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

then: 然后:

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

To me, This feels like I am missing the mark. 对我来说,这感觉就像我错过了标记。 It would make no sense for a fragment to grab a reference to itself and then try and set itself from it. 片段获取对自身的引用然后尝试从中进行设置是没有意义的。 Even worse, the fragment it returns has a null adapter. 更糟糕的是,它返回的片段具有空适配器。 Live and learn. 活到老,学到老。

Next I tried saving the fragments I create in the parent activity for the viewPager implementation, then try and restore these fragments in the parents onCreate. 接下来,我尝试将在父活动中创建的片段保存为viewPager实现,然后尝试在父onCreate中还原这些片段。 The parent class would look like the following: 父类如下所示:

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

then: 然后:

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

What is interesting to note about this approach is when I save the fragment to the manager in the onSaveInstance method, my adpater is not null which makes sense. 关于这种方法,值得注意的是,当我使用onSaveInstance方法将片段保存到管理器时,我的adpater不为null,这很有意义。 When I grab it in the onCreate method, that fragments adapter is now null. 当我在onCreate方法中获取它时,该片段适配器现在为null。

This leaves me with my final thought that maybe restoring an adapter is not efficient and shouldn't be done this way. 这使我最后的想法是,也许恢复适配器效率不高,因此不应该这样做。 Yet again, it doesn't make sense for me to create the adapter inside the Recycler Fragment since I want this fragment to take any adapter implementation of type RecyclerView.Adapter . 再说一次,对我而言,在Recycler Fragment内创建适配器没有意义,因为我希望该片段采用RecyclerView.Adapter类型的任何适配器实现。 For further context, the viewPager implementation is of type FragmentStatePagerAdapter which supplies the bare minimum method overrides the get it to run. 对于进一步的上下文,viewPager实现的类型为FragmentStatePagerAdapter ,该类型提供了最小的方法重写,使其无法运行。 Which to my understanding, is exactly what you should do and the parent FragmentStatePagerAdapter will handle the rest of the restoring process for you. 据我了解,这正是您应该做的,父FragmentStatePagerAdapter将为您处理其余的恢复过程。 Am I wrong in this assumption? 我在这个假设上错了吗?

I'd greatly appreciate any input on this matter. 非常感谢您对此事的投入。 I tried to provide as much information as I possibly could however, if you feel that you might be able to assist me given more information, I would be happy to update my post if you just let me know. 但是,我试图提供尽可能多的信息,如果您觉得可以为我提供更多信息,可以帮助我,如果您让我知道,我将很乐意更新我的帖子。 Thanks! 谢谢!

Final Edit: I ended up going with a similar solution to the one marked as the answer to this post. 最终编辑:我最终找到了与标记为该帖子答案的解决方案类似的解决方案。 I will provide my actual solution below so if anyone in the future finds this post helpful to their situation. 我将在下面提供我的实际解决方案,以便将来任何人发现此帖子对他们的情况有所帮助。

  1. Remove the public setter method in the Recycler Fragments interface. 在“回收器碎片”界面中删除公共setter方法。 Given I was always calling a setter right after initializing the fragment, it made sense that something was wrong with that approach and is prone to error when future developers have to work in the code. 鉴于我总是在初始化片段后立即打电话给setter,这是有道理的,那就是这种方法有问题,并且当将来的开发人员必须在代码中工作时,很容易出错。
  2. Create an enumerated type as a data abstraction for the objects in the recycler. 创建一个枚举类型作为回收站中对象的数据抽象。 Given that certain implied types of data needed different adapters, this seems to be appropriate as well. 考虑到某些隐含的数据类型需要不同的适配器,这似乎也是合适的。 What I mean by this is I might have a car object but depending on where I am at in a user journey that car object might need a different adapter reflected with enums like CAR_PREVIEW_SEARCH or CAR_PREVIEW_SELECTABLE which comes into play during the onCreateView 我的意思是说我可能有一个汽车对象,但是根据我在用户旅途中所处的位置,汽车对象可能需要一个不同的适配器,该适配器反映了onCreateView起作用的枚举,例如CAR_PREVIEW_SEARCHCAR_PREVIEW_SELECTABLE

I can't confidently solve your actual issue without seeing more of the code base, however the below line was a little concerning; 在没有看到更多代码基础的情况下,我不能自信地解决您的实际问题,但是以下几行引起了您的注意:

Each one of these recycler fragments is the exact same fragment except, when the parent activity creates a new instance of them, I call an exposed setter method that sets an adapter of type RecyclerView.Adapter 这些回收站片段中的每个片段都是完全相同的片段,除了,当父活动创建它们的新实例时,我调用了一个公开的setter方法,该方法设置了RecyclerView.Adapter类型的适配器。

Can I suggest an alternative way of setting a type? 我可以建议另一种设置类型的方法吗?

In your FragmentStatePagerAdapter create an enum of your various fragment types; FragmentStatePagerAdapter创建各种片段类型的枚举。

public enum Page {
    PAGE_ONE,
    PAGE_TWO,
    PAGE_THREE
}

Then in your getItem(int position) implementation you would have; 然后,在您的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;
}

In the onCreateView implementation of your CustomFragment you can access this bundle like this; 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;
}

The above ensures that you're only manipulating the adapter when you definitely have the view. 上面的内容可以确保仅在您确实有视图时才操作适配器。 When the system recreates the fragments everything should work as normal. 当系统重新创建片段时,所有内容都应正常运行。

Hopefully this is at least somewhat helpful. 希望这至少有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM