lateinit - это зло и «костыль» Kotlin. Dagger 2 всему виной

  Рет қаралды 16,738

Android Broadcast. Все об Андроид разработке

Android Broadcast. Все об Андроид разработке

Күн бұрын

Я всегда плохо высказывался о lateinit. Он принёс много боли мне и разработчикам в моей команде. Пора показать почему и на что его заменить, а когда это не получится
🔗 Avito.Tech avito.tech/
💰 Поддержать проект на Boosty bit.ly/3sratqQ или Patreon / android_broadcast
🔗 Telegram канал "Android Broadcast" ttttt.me/android_broadcast
🔗 Kotlin lateinit kotlinlang.org/docs/propertie...
🔗 Kotlin lazy делегат kotlinlang.org/docs/delegated...
🔗 Detekt lateinit usage detekt.github.io/detekt/poten...
#AndroidBroadcast #kotlin #lateinit #detekt #lazy #кириллрозов #розовкирилл #android #fragment #dagger #dagger2
0:00 Вступление
0:41 Особенность свойств в Kotlin
1:42 Интеграция от АвитоТех
2:10 Как Dagger стал причиной появления lateinit
4:53 Утечка памяти в Fragment
7:22 Пример креша приложения
9:34 Замена lateinit на lazy делегат
11:19 Как Detekt помогает предотвратить ошибки
11:45 Заключение

Пікірлер: 93
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
💰 Поддержать проект на Boosty bit.ly/3sratqQ или Patreon patreon.com/android_broadcast 🔗 Telegram канал "Android Broadcast" ttttt.me/android_broadcast
@MishaAkopov
@MishaAkopov 2 жыл бұрын
Спасибо за качественный и полезный контент !
@codemachine19
@codemachine19 Жыл бұрын
Очень полезно, спасибо за информацию.
@romanpavliuk2301
@romanpavliuk2301 2 жыл бұрын
Больше такого контента на канале! Процветания каналу!
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Спасибо
@bortolex
@bortolex 2 жыл бұрын
Очень толковая и структурированная инфа, спасибо!
@glebku.45
@glebku.45 2 жыл бұрын
kzfaq.info/get/bejne/ZtSIi7irst-qaJc.html - я-бы сказал, что надо сделать `val data: Flow get() = _data.filterNotNull()`. Потому что Flow все-равно будет использован внутри корутины где-то во фрагменте. А брать непосредственное значение из StateFlow внутри View, как правило, не нужно. Таким образом текущее значение data будет доступно всегда во ViewModel с проверкой на null, а во View всегда будет значение для отображения.
@olegsivakov8700
@olegsivakov8700 2 жыл бұрын
Про lazy как с языка снял, очень удобная штука.
@antonkuranov
@antonkuranov 2 жыл бұрын
Разработчики Котлина попытались "скорректировать" Java, создав более жесткую типобезопасную модель, однако им сразу же пришлось придумывать различного рода костыли для успешной интергации с экосистемой джавы. Что касается nullability: основная ошибка состоит в непонимании того, что в большинстве случаев она не вычислима в статике и зависит от конкретного контекста или фазы выполнения. Если в джаве с этим не заморачивались -- контроль за nullability ложился полностью на разработчика, то в котлине тебя ставят к стенке и обязуют сразу статически заявить о возможном нуле для поля, даже если он присутствует чисто гипотетически. Поэтому в котлине nullability во многих случаях больше мешает, нежели реально помогает.
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Впервые слышу такую точку зрения и полностью с ней не согласен
@RamazanAbdulaev
@RamazanAbdulaev 2 жыл бұрын
На 5:12 разве будет утечка памяти? При уничтожении фрагмента все что в нем есть (не синглтоны) тоже уничтожится ж?
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Fragment может уничтожится не сразу вместе
@sergeykodzha2621
@sergeykodzha2621 2 жыл бұрын
lateinit течет в фрагментах только при уничтожении вьюхи? При уничтожении всего фрагмента он занулится?
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Да, всё верно. При условии, что на эти View больше нигде нет ссылок
@thunderdoge
@thunderdoge 2 жыл бұрын
А в чем делается такая анимация, чтоб изменения в коде красиво показывались?)
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Apple Keynote. Там есть переход между слайдами Magic Move, он так трансформирует код. В Power Point есть аналог, но работает иначе и только в платной версии.
@TvoyGospodin858
@TvoyGospodin858 2 жыл бұрын
видео хорошее, только у меня вопрос, при использовании ленивой инициализации appComponent как мне потом получить к нему доступ? он же не companion и без инстансы SampleApp я его не получу. companion get() метод конечно же тоже не получится написать так как нет доступа к нестатическим полям
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Я его специально храню в Application чтобы доступ к нему получался только через Contextи и не мог быть получен в произвольном месте. Можно написать простое расширение, пример есть в ролике kzfaq.info/get/bejne/Z8p_psiXl8DegWw.html
@TvoyGospodin858
@TvoyGospodin858 2 жыл бұрын
@@AndroidBroadcast спасибо
@newgate280
@newgate280 2 жыл бұрын
для набивки фрагмента рассмотрите следующий способ: class NoteFragment : Fragment(R.layout.fragment_note) { override fun onViewCreated(...){ val viewBinding = FragmentNoteBinding.bind(view) в нем нет необходимости переопределять onCreateView или выносить биндинг как поле
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Вы все равно храните жёсткие ссылки на все View, которые просто спрятаны в экземпляре ViewBinding
@newgate280
@newgate280 2 жыл бұрын
@@AndroidBroadcast да, они хранятся и должны собираться гк без необходимости прямого обнуления в onDestroyView
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
С чего такой вывод? Из ого куска кода все ссылки жёсткий, ViewBinding объявлен как val
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Ааа, вы binding только используете в onViewCreated, а как тогда работать с View за пределами этого метода?
@newgate280
@newgate280 2 жыл бұрын
@@AndroidBroadcast никак, вся логика работы фрагмента в onViewCreated, новые апи по типу ActivityResult способствуют этому
@timofeypletnev1387
@timofeypletnev1387 2 жыл бұрын
Кирилл, привет. Спасибо за твой контент) Вопрос не совсем по теме, но все же. Когда в Dagger 2 делать инжект во фрагмент, если компонента лежит в каком-то фрагменте выше или активити ((requireParentFragment() as SomeMainFragment).component.inject(this)). Сейчас делаю это перед super.onViewCreated(), но мне кажется, что это неправильно, и бывают кейсы, когда при повороте экрана вылетает ошибка, что какое-то поле не заинжектено (lateinit). Или может есть какая альтернатива и я не совсем понимаю как правильно работать с хранением компонент.
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Рекомендация - Fragment.onAttach()
@AndrewKupreychik
@AndrewKupreychik 2 жыл бұрын
5:40 фраза про зануление оборвана?
@Stazhkevich
@Stazhkevich 2 жыл бұрын
интересно, каким образом в первом примере будет утечка, фрагмент остается живой при уничтожении вью и при создании новой в проперти записывается новая вью, а на старую больше нет ссылок и ее съест гц
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Для Fragment можно сделать detach транзакцию, что приводит к уничтожению View, но экземпляр Fragment остаётся в памяти. Также есть retainInstanceFragment
@Stazhkevich
@Stazhkevich 2 жыл бұрын
@@AndroidBroadcast при аттаче все тот же флоу, новая вью байндится, старая собирается гц, а ретайн инстанс фрагменты вообще как юай фрагменты не юзаются, только как дата холдеры
@deadchannal
@deadchannal 2 жыл бұрын
Спасибо! Сам сталкивался с ошибками lateinit, использование его вызывает больше проблем
@AndreyDerkach8
@AndreyDerkach8 2 жыл бұрын
Годный контент, спасибо. А что по поводу инжекта во фрагмент? Мы ведь не можем избежать lateinit в этом случае. Помню ты как-то упоминал что можно инжектить в конструктор через фабрику в новой версии фрагмент либы
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Вот статья на эту тему proandroiddev.com/inject-into-android-component-constructor-4f5ddd27d06
@638electro
@638electro 2 жыл бұрын
Лично я не считаю lateinit костылем. Это инструмент, которым также нужно уметь пользоваться. Иначе мы попросту откатимся в Java-код и везде будем вставлять проверку на null. Как альтернатива - можно использовать связку private nullable переменной и notnull с геттером на nullable и элвис-оператором (для того же биндинга)
@638electro
@638electro 2 жыл бұрын
@@leonidbelousov9298 в Java это обязательно (т.к. нет null-safety), а в Kotlin - это возможность.
@sergeyo.1512
@sergeyo.1512 2 жыл бұрын
Все логично, но как то мне не сильно нравяться решения обратно в null. Как бы шаг назад. Для решения этой проблемы должна появиться какая то более элегантная конструкция.
@user-yd4si7yd3b
@user-yd4si7yd3b 2 жыл бұрын
Когда будет продолжение по дагеру?
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Совсем скоро. Я уже им занимают. Болел и не было возможности записывать
@rikimaru2015
@rikimaru2015 2 жыл бұрын
После этого видео решил порефакторить проект на lateinit - нашел еще 1 недостаток. У меня была 1 переменная с lateinit которая нигде не инициализировалась и не использовалась, при этом студия ее не подсвечивала серым цветом. Я даже сразу не понял почему мне не показывает использование
@Chernov1984
@Chernov1984 2 жыл бұрын
+
@alexkoty5877
@alexkoty5877 2 жыл бұрын
Посмотрел ваш видос и сложилось впечатление полной надуманности этой темы. :( Первый пример не приведет к утечкам памяти потому при детаче фрагмента все что было в нем заберёт гарбридж коллектор. А второй пример это просто тупо не правильное использование вьюмодели. там надо было лайвдату использовать или флоу. Посмотрим остальные ваши видосы
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Примеры взяты из реального кода, который я видел
@alexkoty5877
@alexkoty5877 2 жыл бұрын
@@AndroidBroadcast да, код взят может и из реальных ошибок но название то видео вводит всех в заблуждение. Зачем делать видео если вы не разобрались в теме:( Видео надо было назвать так: "я не правильно поправил того кто писал код не правильно, теперь у нас две ошибки а виноват lateinit" :)
@romanv1293
@romanv1293 2 жыл бұрын
1:18 false для булевого типа
@ildar2244
@ildar2244 2 жыл бұрын
А как в АвитоТех используют/не используют lateinit ? Есть best practic?
@BelokonRoman
@BelokonRoman 2 жыл бұрын
Странно называть lateinit абсолютным злом только потому что у кого-то кривые ручки. Это инструмент, призванный решать определенные задачи. И человек, который пользуется этим инструментом, должен знать как ним пользоваться. Конечно, если не знать, то можно натворить дел. Но это относится вообще к любым операторам\языкам\подходам. Я точно так же могу сказать что внутренние классы Java(которые не static) абсолютное зло, т.к. они неявно хранят ссылку на внешний класс и из-за этого было сделано миллионы утечек памяти. Но я так не скажу просто потому что проблема в незнающем разработчике, а не во вложенных классах. Касательно примера с ViewModel - есть еще одно решение: val data: Deferred = viewModelScope.async { getData() } Таким обрвзом мы и от nullability ушли, сразу стартанули получение данных и на стороне фрагмента мы всегда можем запустить корутину, дождаться данных(если они к тому времени уже небыли получены) и отобразить их
@user-zi8zw3yf2t
@user-zi8zw3yf2t 2 жыл бұрын
У тебя делегат падает иногда при восстановлении
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Ты про ViewBinding property delegate? Буду рад репо с пример где это воспроизводится чтобы исправить
@user-zi8zw3yf2t
@user-zi8zw3yf2t 2 жыл бұрын
@@AndroidBroadcast ошибочка вышла, не внимательно лог изучил, это эхоплеер ресурсы свои не находит у кого то
@user-not_defined
@user-not_defined 2 жыл бұрын
интересно а почему компилятор не предупреждает что lateinit поле может быть null? А так вполне согласен с этим, опасная штука и действует в разрез с Null Safety
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Компилятор считает что это всегда не null, там ведь тип указан. А вот то что там может не быть значения - это уже разработчик на себя берет ответственность за наличие там значение
@user-not_defined
@user-not_defined 2 жыл бұрын
Было бы неплохо если ide сразу бы предупреждала)
@user-not_defined
@user-not_defined 2 жыл бұрын
@@AndroidBroadcast тип указан но и указан что тип lateinit :)
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Можно поставить detekt плагин и включить эту проверку
@user-tx8lw5ti9m
@user-tx8lw5ti9m 3 ай бұрын
Для этого математика нужна программисту
@user-hn8jl8ym1e
@user-hn8jl8ym1e 2 жыл бұрын
Lateinit это полезный инструмент.
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Можно подробнее для чего?
@Nidvoraich
@Nidvoraich 2 жыл бұрын
Не знаешь Котлин. Все вокруг кричат, какой же он крутой и удобный, и супер классный. Начинаешь изучать Котлин - натыкаешься на подобные видео
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Сам Kotlin хорош но из-за необходимости обеспечения удобной работы с Java технологиями и библиотеками возникло вот такое решение ( Также одно из таких решений - kapt
@Nidvoraich
@Nidvoraich 2 жыл бұрын
@@AndroidBroadcast для меня пока что весь мир андроид разработки представляется так: откуда-то с другой вселенной к нам попало устройство на Андроиде. Учёные его потыкали разными палочками и получили список реакций на те или иные события. И всё программирование под Андроид сводится к соблюдению этих странных и необъяснимых паттернов. Например: в приложении несколько фрагментов. При переходе от фрагмента к фрагменту я должен записывать данные в бандл, при этом определятся с методом сериализации, потом в другом фрагменте эти данные оттуда доставать... Так если оба эти фрагмента - части одного и того же приложения - почему они не могут просто имел доступ к одним и тем же данным? Чтоб не копировать всё по тыще раз с использованием горы попутных инструментов? Пока что всё оооочень странно и нелогично. Видимо, чтоб это всё понять, нужно изучать всю историю развития Андроида :/
@MazurovAleksey
@MazurovAleksey 2 жыл бұрын
Спасибо. Но неужели это настолько не очевидно? На одном из стримов ты говорил, что ориентируешься на матёрых ондороидов.
@mcgede
@mcgede 2 жыл бұрын
Нет плохих инструментов, есть кривые руки. Все языковые особенности, которые надо учитывать. Поэтому не надо говорить, что lateinit зло. Это звучит так же прекрасно, как лозунг замечательного языка котлин "не так, как в java". И лозунг так себе, и аргументация против lateinit так себе.
@user-bl3gj8yg1p
@user-bl3gj8yg1p 2 жыл бұрын
Согласен!
@dimka11ggg
@dimka11ggg 2 жыл бұрын
Это для тех, кто из других языков пришел.
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Тех кто пишет в Java стиле на Kotlin очень много
@dimka11ggg
@dimka11ggg 2 жыл бұрын
@@AndroidBroadcast я их и имею ввиду
@danilstepanov4555
@danilstepanov4555 2 жыл бұрын
Видимо авторы так любят свой контент, что решили не пересматривать своё видео после монтажа и пропустили вставку двух фрагментов с 3:40
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
Косяк с рендерингом, сейчас уже подрезаю
@oxtokeeb326
@oxtokeeb326 2 жыл бұрын
Дублирование кода: видеоверсия))
@user-kc2ij9wh9o
@user-kc2ij9wh9o 2 жыл бұрын
lateinit это же всего лишь синтаксический сахар, чтобы не делать постоянно проверку на null. Не понял почему не стоит его использовать во фрагментах, почему он вообще может приводить к утечкам? Согласен постоянно им пользоваться плохо, особенно использовать в асинхронных операциях, но если говорить о фрагментах и активти, то lateinit это отличный вариант для определения вью элементов
@AntonP230
@AntonP230 2 жыл бұрын
В активити можно, во фрагментах нет - т.к. время жизни фрагмента может быть существенно больше времени жизни его view и почти всегда больше. А необнуленная ссылка на короткоживущий объект в долгоживущем = утечка памяти, приэтом lateinit Объекты нельзя занулять.
@user-kc2ij9wh9o
@user-kc2ij9wh9o 2 жыл бұрын
@@AntonP230 Какая то логика в этом есть, но почему тогда в документации по android рекомендуют использовать lateinit во фргаментах? Я довольно часто сталкивался с этим, даже в довольно серьезных проектах, а про утечки слышу первый раз. Может есть какая то более подробная инфа об этом, типо статьи с разбором?
@ihorkozar4715
@ihorkozar4715 2 жыл бұрын
@@user-kc2ij9wh9o, вот тоже возник вопрос по доке. На андроид деволоперс ведь тот же байндинг всегда приводится с lateinit.
@AntonP230
@AntonP230 2 жыл бұрын
@@user-kc2ij9wh9o если честно, не встречал. Можете поделиться ссылкой на такую документацию? Хотелось бы разобраться в чем там дело.
@user-kc2ij9wh9o
@user-kc2ij9wh9o 2 жыл бұрын
@@AntonP230 ютуб удаляет коммент со ссылкой, вбейте в гугле, там по первой ссылке дока будет. Use common Kotlin patterns with Android
@Citizen7777777
@Citizen7777777 2 жыл бұрын
Что мешает плохому танцору давно известно 🤣 Ужасное зло это когда без году неделя разработчик еще не отличая var от val уже пытается канал поднять и уроки записывать...
@AndroidBroadcast
@AndroidBroadcast 2 жыл бұрын
В чей адрес вброс?
Android Custom View. Диаграмма Ганта
40:41
Android Broadcast. Все об Андроид разработке
Рет қаралды 14 М.
Jetpack Compose - будущее Android UI и убийца Fragment
22:57
Android Broadcast. Все об Андроид разработке
Рет қаралды 37 М.
🌊Насколько Глубокий Океан ? #shorts
00:42
когда повзрослела // EVA mash
00:40
EVA mash
Рет қаралды 3,4 МЛН
3M❤️ #thankyou #shorts
00:16
ウエスP -Mr Uekusa- Wes-P
Рет қаралды 7 МЛН
#1 Что такое корутина. Важные особенности || Курс по корутинам
16:40
Android Broadcast. Все об Андроид разработке
Рет қаралды 87 М.
Основы DI и Dagger, как работает, настройка в проекте
17:31
Android Broadcast. Все об Андроид разработке
Рет қаралды 66 М.
Урок 2. Где работают замыкания JS? (+ задачи)
4:21
Алексей Волков
Рет қаралды 429
Как запускать задачи в фоне на Android. РАЗБОР
26:42
Android Broadcast. Все об Андроид разработке
Рет қаралды 3,4 М.
На что способен Jetpack Compose, Алексей Гладков @MobileDeveloper
15:45
Android Broadcast. Все об Андроид разработке
Рет қаралды 16 М.
Игорь Ахмаров - Kotlin delegates
9:31
Mobius
Рет қаралды 3,8 М.
Kotlin For Beginners - Inline, Noinline and Crossinline
9:53
Charfaoui Younes
Рет қаралды 4 М.
🌊Насколько Глубокий Океан ? #shorts
00:42