Всем привет! Меня зовут Максим Бредихин, я Android-разработчик в Тинькофф. А это — вторая статья серии об интересных моментах из Fragment API, о которых вы, возможно, не знали. Часть 1. Транзакции Часть 2. (Не) создаем инстанс (вы находитесь здесь) Часть 3. Навигация Часть 4. Анимации и меню Готовьте вкусности, сегодня я расскажу, как (не) создавать новые инстансы фрагментов. Для работы с фрагментами не обязательно использовать FragmentManager напрямую. Если у нас есть стартовый фрагмент, достаточно указать его в XML через атрибут name у контейнера. Дополнительно ничего делать не нужно, но под капотом без FragmentManager не обошлось. Вся магия происходит в четыре этапа: FragmentContainerView в конструкторе берет FragmentManager из родительского контекста. Если контейнер фрагмента находится в разметке Activity, будет использован supportFragmentManager, а если в разметке фрагмента — childFragmentManager. С помощью FragmentFactory создается инстанс фрагмента, указанного в поле android:name. Сразу после этого и до начала транзакции у фрагмента вызывается коллбэк Fragment.onInflate(Context, AttributeSet, Bundle?). Совершается транзакция. Стоит разобраться с onInflate(). Он вызывается до начала транзакции и, следовательно, дергается до всех коллбэков жизненного цикла. Первое и главное условие для вызова этого метода: фрагмент должен создаваться через XML. А дальше у нас две дороги: если мы — модные, молодежные и современные разработчики, которые слушают Google и используют в качестве контейнера FragmentContainerView, этот метод будет вызван только один раз при первом создании инстанса фрагмента; если мы предпочитаем старые подходы и используем <fragment> в разметке, метод onInflate() будет полноценным методом жизненного цикла, который вызывается перед onAttach(). Несмотря на это, я не пропагандирую такой способ. В остальных случаях метод не вызывается никогда. Его основное предназначение — достать аргументы из XML. Для этого нужно определить свои атрибуты для аргументов в ресурсах приложения.  Создаем файл attrs.xml, прописываем нужные аргументы и указываем их в разметке. На следующем шаге достаем аргументы из attrs в onInflate(). Отойдем от аргументов и вспомним, что в транзакции при желании можно присвоить фрагменту tag. То же самое можно сделать через XML. Для этого достаточно указать android:tag у контейнера. Важно! Создать фрагмент из XML мы можем, только указав ID у контейнера, иначе упадем с IllegalStateException. Это нужно для сохранения состояния при пересоздании View. На момент Fragments: 1.5.0 с таким фрагментом можно совершать любые транзакции, доступные для обычных фрагментов. Главное — выбрать нужный FragmentManager и достать фрагмент через ID контейнера или указанный в XML тег. В генах Android-разработчика прописано, что мы обязаны создавать фрагменты с пустым конструктором, чтобы система могла их самостоятельно пересоздать. Однако в версии Fragments 1.1.0 у нас появилась возможность контролировать создание инстансов фрагментов, в том числе добавлять любые параметры и зависимости в конструктор. Для этого достаточно подменить стандартную реализацию FragmentFactory на свою, где мы сами себе цари и боги. Главное — успеть заменить реализацию до того, как она понадобится FragmentManager’у, то есть до первой транзакции и восстановления состояния после пересоздания. Чем раньше мы заменим негодную реализацию, тем лучше. Для Activity лучшим сценарием будет замена: до super.onCreate(); в блоке init. У фрагментов доступ к своему FragmentManager’у появляется не сразу. Поэтому подмену мы можем совершить только между onAttach() и onCreate() включительно, иначе увидим страшный красный текст в логах после запуска. Но важно помнить, что parentFragmentManager — это FragmentManager, через который совершили коммит. Следовательно, если в нем ранее заменили FragmentFactory, делать это во второй раз не нужно. Теперь разберемся, как нам реализовать свою фабрику. Создаем класс, наследуемся от FragmentFactory и переопределяем метод instantiate(). На вход получаем classLoader, который можно использовать для создания Class<out Fragment>, и className — полное имя нужного фрагмента. Исходя из имени определяем, какой фрагмент нам нужно создать, и возвращаем его. Если мы не знаем такого фрагмента, передаем управление родительской реализации. Примерно так все выглядит super.instantiate() под капотом FragmentFactory:  Кто-то может сказать: «FragmentFactory — штука классная, но для транзакций нам все равно нужны конкретные инстансы, так что пойду-ка я добавлю в свой фрагмент companion object». И он будет прав, но только если сидит на фрагментах до версии 1.2.0. В этой версии нас избавили от необходимости создавать инстанс фрагмента в транзакции вручную, добавив дополнительные перегрузки методов FragmentTransaction.add(): Аналогичные методы добавили и для FragmentTransaction.replace(). Теперь мы можем сделать так: Или использовать fragment-ktx и расширение с reified-дженериком, которое я упоминал в первой части цикла. Что еще круче, теперь мы можем передать аргументы сразу во время транзакции: Или с использованием fragment-ktx: Во фрагменте нам останется только достать их как обычные аргументы: Вспомним, как мы учились работать с фрагментами. Создаем класс, создаем файл разметки и «надуваем» его в onCreateView(): Мы сотни раз набирали эти родные сердцу строки, но в версии Fragments 1.1.0 ребята из Google решили, что больше не будут это терпеть. Они добавили фрагментам второй конструктор, принимающий на вход @LayoutRes, благодаря которому больше не нужно переопределять onCreateView(). А под капотом работает тот же самый бойлерплейт: Чем меньше кода нам придется писать, тем лучше. Поэтому давайте не будем писать шаблонный код, если этого можно избежать. Если вдруг вы до этого инициализировали View в onCreateView(), правильнее использовать специальный коллбэк onViewCreated(), вызываемый сразу после onCreateView(). Подошла к концу вторая часть цикла «Неочевидного о фрагментах». В этой статье мы разобрались с созданием фрагментов в XML, добавили зависимости в конструктор через FragmentFactory, узнали, что создавать фрагменты в транзакциях не обязательно, и избавились от небольшого кусочка бойлерплейта в нашем коде. Теперь вы сможете использовать фрагменты без companion object для создания и сделать свой код немного чище. В следующей статье мы посмотрим на новые и не очень фишки навигации между фрагментами. До встречи!  Android разработчик