arguments ведь это такая морока. Как часто можно столкнуться с тем что фрагмент воссоздан и данные потерялись. Кстати в каком виде будут данные которые передавались в воссозданном фрагменте, если они non-null. Будут краш сразу же при обращении, что объект null хотя не может им быть?
Категорически обязательно
а была ли такая же проблема с Dialog ? там ведь может ровна эта же проблема случиться. не DialogFragment а именно DIalog
в архитектуре подобной декомпоузу все сразу оказывается во вьюмодели и вообще забываешь про все bundle
Диалог не восстанавливается после смерти процесса и даже смены конфигурации
вьюмодели живут не вечно
Если вы хотите поддерживать восстановление состояния приложения после смерти процесса, вы никогда не можете забыть про сериализацию этого самого состояния
спасибо, понял, значит у диалогов нет необходимости в восстановлении, вот и проблемы не было
ну скорее вопрос о том, насколько часто случается такое , что происходит восстановление
ну до тех пор пока single activity жив , они есть или немного не так было организовано хранение стэка вьюмоделей
часто, как часто процесс будет завершаться зависит от того хватает ли памяти в процессе пользования телефоном. Короче часто 🙂
Сплошь и рядом, просто в хорошо сделанном приложении вы этого не замечаете. Типичный кейс: 1) вы регистрируетесь в приложении, заполнили форму из 15 полей 2) оно говорит, сделайте фото! 3) открывается стандартное приложение с камерой 4) система убивает ваше приложение, потому что оно свернулось в фон (это разумеется не всегда случится, но очень возможно) 5) вы делаете фотку, возвращаетесь в свое приложение, а вся информация о регистрации слететла, и вы открыли его как с чистого листа Ругань, удаление приложения, единица в маркете и гневный отзыв
ну все такое я бы хранил во вьюмодели, но но если и корневая активность будет уничтожена, то беда
Система убивает весь процесс приложения, всё что было в рантайме, в оперативной памяти, уничтожается.
просите и воздастся вам! Теперь аргументы передаются именно в контруктор, как вы и хотели! Но есть один момэнт... придется обмазаться кучей фабрик и написать еще больше кода, чем с бандлом :)) https://medium.com/capital-one-tech/android-fragmentfactory-75823af015fd
неет, спасибо)) через companion через простую фабрику понятно, в местах применения зато кода будет меньше, но это все равно много кода.
с бандлом на 3 строчки больше написать)
И самое главное, в Decompose все аргументы переданные в компонент (вью модель) автоматически сохраняются и восстанавливаются после смерти процесса.
Вот это я хотел услышать и понимал что это должно быть реализовано. Просто ещё до появления деклмпоуз у нас на проекте лид запилил подобную библиотеку . Точно такой же подход, но вот после смерти активности стэк бы не сохранился. Но и мы не парились по этому поводу, приложение было онлайн кинотеатром и ему там нечего было запоминать
А каким образом это реализовано? Как, например, сохраняется лямбда? Или там тоже ворох условий того, что можно, а что нельзя?
Каждый компонент представлен классом-конфигурацией, все конфигурации Parcelable. В роутер передаётся factory функция, которая создаёт компонент по его конфигурации. Т.е. в самих конфигурациях лежат только минимально необходимые данные для создания экземпляров компонентов. Например Config.DetailsScreen(selectedId: Long) : Parcelable. А лямбды и другие зависимости передаются в factory: when (config) { is Config.Details -> DetailsComponent(id = x, onFinishClick: () -> Unit) }
Видимо, слишком сложная информация для моего пьяного мозга, ладно. А как компоуз устроен с точки зрения восстановления стека известно? С фрагментами мне кажется, если пытаться сохранить колбеки, то можно выстрелить себе в хй, если передать лямбду, которая будет ссылаться на предыдущий фрагмент, а он выгрузится и убьётся. Тут нет? UPD. Т.е., есть допустим ListFragment и DetailsFragment. Мы из листа открываем детейлс, с передачей лямбды, которая внутри что-то делает с самим ListFragment. Вдруг всё убивается и при восстановлении у нас полноценно восстанавливается только DetailsFragment. Вызывается лямбда где-то там дальше и всё падает.
В композ для навигации отдельная библиотека, в которой аргументы в строке передаются. Она и сохраняет стек.
Добавил UPD с примером в сообщении выше. Компоуз такое переживёт? (Я не уверен, что даже фрагменты упадут)
Так лямбды не сохраняются в бандл, даже во фрагментах
Да. Но как я понял из обсуждения выше в декомпоузе у тебя как-раз можно передать что угодно как аргумент, в том числе лямбду, нет? И что у кого-то подобное решение было с фрагментами, с кастомным сохранением. И если так, то как поведёт себя компоузабл, если в неё передали лямбду, а процесс умер и восстановился?
Лямбды можно передавать как аргументы, но их не надо класть в бандл.
Так, а как заставить их пережить смерть и восстановление?
Они не должны переживать, новый экран создаётся с новыми коллбеками. А в бандл сохраняется только информация о том, какой именно фрагмент надо создать.
Может ли система пересоздать фрагмент при его живой активити?
Лямбда же создаётся на месте вызова. Допустим в предыдущем фрагменте. Как туда дойдут вызовы после пересоздания, если пересоздается активный фрагмент?
Как тогда происходят интересные глюки, когда после пересоздания фрагмента какие-то поля у фрагмента оказываются неинициализированными? 🤔 (распространенный антипаттерн, когда фрагменту после создания устанавливают извне листенеры, вызывают методы и т.п.)
Нет, каждый фрагмент надо создавать в его фрагменте-предке, дочерние фрагменты могут сообщать предку, что надо открыть другой фрагмент.
Не знаю как так получается у людей :-)
Ну вот и мне интересно. Получается же. И выглядит так, что фрагмент пересоздается при живой активити. Иначе, если пересоздается активити, то она создаст фрагмент и заново установит листенеры и т.п.
Я бы посмотрел на пример, когда так происходит. Уверен, что-то не так сделали.
Мы же используем FragmentFactory?
Например, типа такого кода в активити: val f = MyFragment() // или даже параметры в конструкторе фрагмента f.myListener = { ... } f.initSomeLateinitFields(1, "abc") fragmentManager.add(f)...
Если используется FragmentFactory, то именно она создаёт фрагмент второй и последующие разы. Там и передаются аргументы в конструктор всегда.
Не не не. С factory каждый может. Именно так, как у меня в примере.
Так и делается в фактори, можно в конструктор, можно сеттерами
Вот как-то так, например
Фабрику же назначают фрагмент менеджеру, а не напрямую вызывают.
Тогда не понимаю смысл сего действа: override fun onCreate(savedInstanceState: Bundle?) { childFragmentManager.fragmentFactory = fragmentFactory super.onCreate(savedInstanceState) if (savedInstanceState == null) { childFragmentManager .beginTransaction() .add(R.id.content, fragmentFactory.mainFragment(), TAG_MAIN) .commit() } } Фабрику присвоили, а фрагмент создаем все равно руками 🤷
После пересоздания активити, фрагмент будет создан автоматически фабрикой.
Тогда зачем в первый раз создавать фрагмент руками? По идее, достаточно передать класс, а фабрика сделает остальное.
Потому, что надо аргументы указать, которые Bundle
fragmentFactory.mainFragment() здесь же нет аргументов
Там есть setArguments (...)
Аа, этому фрагменту не нужны аргументы. Там есть details, вот ему нужны. Но API один общий
У details внутри есть аргументы, а при создании нет fun detailsFragment(): DetailsFragment
Посмотрите внимательнее пожалуйста, поиском по setArguments. Ещё можно запустить пример и убедится, что аргументы всегда на месте.
Мне, главным образом, интересна работа с фабрикой. Поскольку фабрика создает фрагмент по имени класса, то получается, что больше не следует руками создавать фрагменты так, как здесь: fragmentFactory.mainFragment()
Фрагменты приходится создавать руками в первый раз, потому что надо передать аргументы.
Почитайте статью, которую я выше отправлял здесь. Там я всё описал.
Обсуждают сегодня