Восстановление адаптера при воссоздании фрагмента - PullRequest
0 голосов
/ 12 декабря 2018

Я сталкиваюсь с сбоем, когда, если операционная система Android убивает процесс, когда приложение пытается восстановить действие и фрагмент из сохраненного экземпляра, адаптер моего recyclerview всегда имеет значение null.

Вот мойиерархия:

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

Каждый из этих фрагментов утилизатора является точно таким же фрагментом, за исключением того, что когда родительское действие создает их новый экземпляр, я вызываю открытый метод сеттера, который устанавливает адаптер типа RecyclerView.Adapter.(причина в том, что каждый из этих фрагов может быть переработчиком страниц, но его содержимое и способы взаимодействия с ним могут отличаться) Пример кода из родительского действия:

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

Теперь, мои конечные пользователиприводят к аварийному завершению работы, когда, если они имеют фоновое действие этого действия, и ОС решает прекратить процесс, на котором оно действует, оно не восстанавливается должным образом.Из того, что я вижу, все на восстановлении кроме адаптера, который является нулевым.Затем код пытается получить доступ к интерфейсу адаптеров и падает с помощью NPE.

Я не могу понять, почему это происходит.Я также пытаюсь обернуть голову вокруг восстановления действий и фрагментов в подобных событиях.Я прочитал кучу постов и документации о восстановлении состояния и до сих пор не могу найти решение.Вот пример решений, которые я попробовал, и, возможно, кто-то может указать на изъян / пробел в моей логике.

Пусть утилита frag хранит себя в fragManager и пытается прочитать его обратно при вызове метода 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 не является нулевым, что имеет смысл.Когда я получаю его в методе onCreate, этот адаптер фрагментов теперь имеет значение null.

В связи с этим у меня остается заключительная мысль, что, возможно, восстановление адаптера неэффективно и не должно выполняться таким образом.Еще раз, для меня нет смысла создавать адаптер внутри фрагмента Recycler, так как я хочу, чтобы этот фрагмент принимал любую реализацию адаптера типа RecyclerView.Adapter.Для дальнейшего контекста реализация viewPager имеет тип FragmentStatePagerAdapter, который предоставляет метод минимума, переопределяющий его запуск.Насколько я понимаю, это именно то, что вы должны сделать, и родитель FragmentStatePagerAdapter будет выполнять остальную часть процесса восстановления для вас.Я ошибаюсь в этом предположении?

Буду очень признателен за любой вклад в этот вопрос.Однако я постарался предоставить как можно больше информации, если вы считаете, что можете помочь мне, предоставив больше информации, я был бы рад обновить мой пост, если вы просто дадите мне знать.Спасибо!

Окончательное редактирование: В итоге я выбрал решение, аналогичное тому, которое помечено как ответ на этот пост.Ниже я приведу свое реальное решение, поэтому, если кто-то в будущем сочтет этот пост полезным для своей ситуации.

  1. Удалите метод открытого сеттера в интерфейсе фрагментов рециркуляции.Учитывая, что я всегда вызывал сеттер сразу после инициализации фрагмента, было понятно, что с этим подходом что-то не так, и он подвержен ошибкам, когда будущим разработчикам приходится работать с кодом.
  2. Создайте перечислимый тип в качестве абстракции данных для объектов в утилизаторе.Учитывая, что для определенных подразумеваемых типов данных требовались разные адаптеры, это также представляется целесообразным.Под этим я подразумеваю, что у меня может быть автомобильный объект, но в зависимости от того, где я нахожусь в путешествии пользователя, этому автомобильному объекту может потребоваться другой адаптер, отображаемый с помощью перечислений, таких как CAR_PREVIEW_SEARCH или CAR_PREVIEW_SELECTABLE, который входит в игру во время * 1046.*

1 Ответ

0 голосов
/ 12 декабря 2018

Я не могу с уверенностью решить вашу настоящую проблему, не видя больше базы кода, однако нижеследующая строка была немного относительно;

Каждый из этих фрагментов утилизатора является точно таким же фрагментом, кромеКогда родительское действие создает новый экземпляр из них, я вызываю открытый метод установки, который устанавливает адаптер типа 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;
}

В onCreateView При реализации своего CustomFragment вы можете получить доступ к этому пакету следующим образом:

@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;
}

Вышеуказанное гарантирует, что вы манипулируете адаптером только тогда, когда у вас определенно есть вид.Когда система воссоздает фрагменты, все должно работать в обычном режиме.

Надеюсь, это по крайней мере несколько полезно.

...