Android - сбои в работе памяти с ViewPager › Фрагменты › Действия

Я использую библиотеку поддержки v4 в нашем приложении, и у меня есть пошаговый процесс в приложении, который нормально работает в обычных условиях. Однако у нас есть требование, чтобы все работало и не вылетало из-за нехватки памяти. Поэтому я использую библиотеку SetAlwaysFinish, чтобы помочь с этим (https://github.com/bricolsoftconsulting/SetAlwaysFinish). Это было чрезвычайно полезно для определения областей, в которых необходимо обнаруживать и обрабатывать подобные ситуации, но я столкнулся с одной, которая поставила меня в тупик. Имейте в виду, что это нормально работает при нормальных обстоятельствах, но я явно тестирую с «setAlwaysFinish» = ON.

Вот моя установка: у меня есть класс ProcessActivity, в котором размещается макет для ViewPager. У ViewPager есть набор адаптеров, который содержит мой список фрагментов, которые будут охватывать процесс. Это в методе onCreate():

ProcessActivity:

createSteps();  // this creates/populates the ArrayList "processSteps" with Fragments
theAdapter = new ProcessAdapter(this, processSteps); // the ProcessAdapter class extends FragmentStatePagerAdapter, with an additional list of Fragment steps
theViewPager.setAdapter(theAdapter);

Есть и другие части, но это ядро ​​того, как это настроено. Во время одного из шагов фрагмента мне действительно нужно «вырваться» из процесса шага и временно перейти к действию, чтобы выполнить некоторую логику (а затем вернуться к процессу шага). Я делаю это следующим образом с ForResult, так как мне нужно иметь возможность обрабатывать действие OK/Cancel в действии, когда оно завершается:

Фрагмент Step4:

Intent intent = new Intent(getActivity(), ThePushActivity.class);
intent.putExtra(blah..);
startActivityForResult(intent, THE_REQCODE);

После того, как он попал в это представление, иногда вызывается метод onDestroy() как ProcessActivity, так и Step4Fragment (из-за alwaysFinish). Иногда кажется, что он работает нормально и вызывает обратно процесс Step-Fragment. Но обычно он вызывает метод onDestroy() ProcessActivity, а затем повторно вызывает метод onCreate() с заполненным состоянием сохраненного экземпляра пакета. Это вызовет приведенный выше код создания шага и поместит приложение в странное состояние, где отображается последний шаг, на котором был пользователь, но за кулисами он на самом деле находится на первом шаге (фрагменты в этот момент находятся за пределами). удар и разъединение), и неизбежно происходит сбой. На данный момент кажется, что Step4Fragment полностью разъединен и где-то рухнет, если вы попытаетесь что-то сделать, даже если кажется, что он был правильно воссоздан.

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

Дайте мне знать, если мне нужно предоставить более подробную информацию. Заранее благодарю за любую помощь!

ОБНОВЛЕНИЕ № 1: Просто быстрое обновление, которое я заметил, что фрагменты повторно создаются и инициализируются, и они правильно попадают в метод onActivityResult() «текущего» фрагмента и делают то, что ему нужно. делать правильно. Кажется, что разъединение лежит в базовом классе ProcessActivity, следуя одному из этих сценариев. Макет ViewPager отображается правильно, но все, что находится за пределами ViewPager, неверно (т.е. он указывает, что он находится на 1-м этапе процесса, когда он должен указывать, что он находится на 4-м, и кнопки навигации отображаются для 1-го шага а не 4-й).

Поэтому я предполагаю, что мне нужно как-то правильно установить эти элементы вручную. Углубляясь в это, я могу упустить фрагменты кода, которые необходимы кому-то, чтобы активно помочь с этим. Если бы я мог каким-то образом получить доступ к состоянию поля ViewPager, которое содержит возможность получить «текущий показанный фрагмент», до того, как был вызван весь этот onDestroy()/onCreate(), возможно, из пакета saveInstanceState? Это, вероятно, решило бы мою проблему. Но при проверке этого пакета при отладке я вижу только сами фрагменты и их соответствующие состояния. Однако я продолжу копать.

В любом случае, пожалуйста, дайте мне знать, если у кого-нибудь есть какие-либо идеи о том, как правильно это сделать (на высоком уровне, конечно).

ОБНОВЛЕНИЕ № 2 Я вижу, что даже если "текущий" фрагмент кажется инициированным правильно, и представление отображается правильно, все отсоединено. Я не могу вызывать какие-либо методы для getResources() или getActivity() и т. д. На самом деле мне удалось заставить ProcessActivity «работать» правильно, основываясь на сохранении индекса шага (int) в пакете saveInstanceState, а затем перезагрузке пользовательского интерфейса. элементы вокруг него. Тем не менее, у меня все еще есть этот блокировщик с текущим фрагментом, отсоединенным от действия, хотя он, по-видимому, повторно создается правильно.

ОБНОВЛЕНИЕ №3 Когда я следую указаниям этого поста: ViewPager и фрагменты — как правильно хранить состояние фрагмента?, я получаю немедленное исключение, когда пытаюсь выполнить первый putFragment(). Исключением является: «IllegalStateException: Fragment Step4Fragment в настоящее время отсутствует в FragmentManager». Я думаю, это может быть связано с тем фактом, что я держу только один фрагмент слева и один фрагмент справа «активным» в любой момент времени (т.е. offScreenPageLimit).


person svguerin3    schedule 20.05.2013    source источник


Ответы (2)


Вы уверены, что ваш фрагмент не повторно создается?

Все подклассы Fragment должны включать общедоступный пустой конструктор. Платформа часто повторно создает экземпляр класса фрагмента, когда это необходимо, в частности, во время восстановления состояния, и должна иметь возможность найти этот конструктор для его создания. Если пустой конструктор недоступен, во время восстановления состояния в некоторых случаях возникнет исключение времени выполнения.

person Jim Baca    schedule 20.05.2013
comment
Да, все фрагменты абсолютно повторно создаются, и все они имеют общедоступные пустые конструкторы. Я помещаю журналы в каждый из них, и каждый из них вызывается (все они создаются в методе createSteps(), упомянутом в моем посте, в onCreate() основного класса процесса, который все это устанавливает... поэтому они обязательно перезвонят). Что странно, так это то, что из журналов кажется, что все подготовлено правильно и вызывается для повторной настройки представления с того места, где оно остановилось ... но на самом деле оно отображается неправильно и вызывает сбой, если вы что-то делаете. после того как покажет. - person svguerin3; 21.05.2013
comment
После реализации большего количества журналов я понимаю, что он действительно сначала вызывает пустой конструктор (который является фактическим, который следует использовать, с ненулевым прикрепленным действием). Но когда после этого вызывается onCreate() класса ProcessActivity, он воссоздает фрагменты, которые кажутся непересекающимися. Мне любопытно, как я получаю доступ к «настоящему» фрагменту, который был правильно воссоздан системой из класса ProcessActivity после его завершения. Я пробовал так много разных вещей, но мне не повезло. :( - person svguerin3; 22.05.2013
comment
@svguerin3 свяжитесь со мной через мой веб-сайт в моем профиле. Я попытаюсь помочь вам по скайпу, если хотите. - person Jim Baca; 22.05.2013
comment
Спасибо, Джеймс! Я пропустил этот пост: stackoverflow.com/questions/7951730/, что указывает на то, что не стоит делать то, что я делаю (создание фрагментов из onCreate ()). Поэтому я собираюсь попробовать упомянутое там решение. Если мне еще не повезет, я обязательно свяжусь с вами. Большое спасибо за готовность это сделать! Я дам вам знать, как это происходит - person svguerin3; 22.05.2013
comment
Без проблем. Я решил, что это проблема инициализации (хотя я думал, что это не пустой конструктор). Удачи. - person Jim Baca; 22.05.2013

Оказывается, это решение было похоже на решение, представленное в этом сообщении: ViewPager и фрагменты — как правильно хранить состояние фрагмента?

У меня было несколько вещей, которые нужно было изменить. Во-первых, мне нужно было переместить фрагменты в поля, а не во временные переменные. Во-вторых, мне нужно было избегать создания новых фрагментов каждый раз в onCreate(). Вместо этого он должен попытаться извлечь их из saveInstanceState как таковые:

fragment1 = (Fragment1Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment1Step.class.getName());
fragment2 = (Fragment2Step) getSupportFragmentManager().getFragment(savedInstanceState, Fragment2Step.class.getName());
etc...

и затем сразу после этого у меня есть последующие проверки для каждого из них; если они нулевые (т.е. приходят свежими, а не из сохраненного экземпляра), то они будут созданы новыми:

if (fragment1 == null) {
    fragment1 = new Fragment1Step();
} 
if (fragment2 == null) {
    fragment2 = new Fragment2Step();
} 

Конечно, для того, чтобы это работало, они также должны быть сохранены в методе onSaveInstanceState():

try {
    getSupportFragmentManager().putFragment(outState, Fragment1Step.class.getName(), fragment1);
} catch (Exception e) {
    // do nothing
}
try {
    getSupportFragmentManager().putFragment(outState, Fragment2Step.class.getName(), fragment2);
} catch (Exception e) {
    // do nothing
}
etc...

Причина, по которой я использую их в блоках try/catch, заключается в том, что наш ViewPager имеет только значение offScreenPageLimit, равное 1, поэтому некоторые из них будут вызывать исключения при вызове «putFragment()», если они в данный момент не находятся в «активном» стеке фрагментов. показывают.

Это не очень красиво, и может быть лучший способ справиться с этим ... но, похоже, теперь это работает нормально.

person svguerin3    schedule 22.05.2013