Jetpack Compose. ViewModel. Как выбрать архитектуру для Compose? [Ru, Kotlin\Android]

Поділитися
Вставка
  • Опубліковано 15 жов 2024

КОМЕНТАРІ • 72

  • @mypathhh
    @mypathhh 4 місяці тому

    Автору спасибо. Вестоским желаю терпения

  • @easyCodeRu
    @easyCodeRu 2 роки тому +4

    Начал изучать компоуз. Второй день. Пока не очень понятно че там как с фрагментами и навигацией и что под капотом у компоузов. Спасибо тебе за твой вклад, гляну код, попробую разобраться

  • @dashaxedit
    @dashaxedit 5 місяців тому

    Топовые видео, спасибо, разъяснил много сильных моментов на которые я искал ответы

  • @maximmaxim3262
    @maximmaxim3262 3 роки тому +3

    Спасибо за разборы! По твоем проектам и по примерам гугла (owl, с твоей наводки) начало получаться делать ui на compose c jetpack navigation. Выглядит как то, что будет в большинстве проектов через пару лет

  • @dreamer6228751
    @dreamer6228751 2 роки тому +1

    Огонь, так держать. Много полезного)

  • @deadchannal
    @deadchannal 2 роки тому

    Блин это очень круто! Спасибо!

  • @avdim88
    @avdim88 3 роки тому

    Видео просто ТОП! Очень полезно, Спасибо!

  • @yakiv1488
    @yakiv1488 2 роки тому

    Спасибо. Материал - топ!

  • @_Iovan_
    @_Iovan_ 3 роки тому +2

    Спасибо за очередное крутое видео Алексей и за крутой доклад на конференции в Омске, было очень много полезной информации! Рад был познакомится, спасибо тебе за твою открытость и простоту, ждем в следующем году!

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Спасибо большое) с удовольствием приеду ещё раз )

    • @СергейПанов-з3ц
      @СергейПанов-з3ц 3 роки тому

      Можно ли как-то посмотреть запись этого доклада?

    • @_Iovan_
      @_Iovan_ 3 роки тому +1

      @@СергейПанов-з3ц не думаю что в общем доступе он скоро появится(

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому +4

      Выложу на канале как только мне отдадут видео )

  • @zmeytee
    @zmeytee 3 роки тому

    Спасибо. Полезно.

  • @БыстрыйШенк
    @БыстрыйШенк 3 роки тому

    Круто! Спасибо

  • @KIRILLINGUSS21
    @KIRILLINGUSS21 3 роки тому

    очень очень круто. спасибо тебе за такое класное видео. ждем еще про compose и архитектуру

  • @alexsot2009
    @alexsot2009 2 роки тому

    Алексей, ты всегда говоришь про LiveData потому что есть observeAsState, но ведь еще StateFlow/SharedFlow и collectAsState. У нас на проекте, конечно под наши нужды, сделано как раз так. И MVI модель немного расширена. У нас есть Action, глобальное действие засылаемое во ViewModel, есть Event (тоже через Flow, для UI, обрабатывается в Composable через LaunchedEffect), есть Result (то что выдает ViewModel, и в данном случае Event это тоже Result), и собственно отдельный Reducer, который обрабатывает именно Result для изменения состояния. Event тоже сделан как Result, для того чтобы его можно было выдать из ViewModel, но при этом, при обработке Flow, такие сообщения перенаправляются в другой канал (eventFlow) и не попадают в reducer. Ну и конечно сама модель: _state как MutableStateFlow, event, action как SharedFlow. Было бы интересно услышать твое мнение на подобную архитектуру. Ее не я придумывал, и хотелось бы другое мнение, избыточна ли такая архитектура или нет

    • @alexeysokolov930
      @alexeysokolov930 11 місяців тому

      Ваш вариант более современный, недавно на медиуме похожее видел. Тут видео 2х годичной давности. По-хорошему вообще вью модельки надо убирать, как по мне

  • @СергейПанов-з3ц
    @СергейПанов-з3ц 3 роки тому

    Спасибо!

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Спасибо за поддержку канала!

  • @dm13kz
    @dm13kz 2 роки тому

    Алексей, я правильно понял, что вы модель в архитектуре mvi распили на части при помощи sealed-класса превратив таким образом состояние представления в набор событий по изменению ui? В моём понимании такая оптимизация превратила mvi в mvvm.
    Просто Дорфман в своё время акцентировал внимание на том, что модель по структуре должна быть полноценной и достаточной для построения ui в любой момент времени.
    Точнее, модель должна быть полной, неизменяемой и подписка на её изменение должна быть только в одном месте. Только такой набор качеств гарантирует согласованность между состоянием интерфейса пользователя и состоянием бизнес логики.
    Тот пример, который вы привели в качестве сложности копирования полноценной модели я решаю простой инкапсуляцией.
    Для приведенного вами примера я бы создал методы state.copyToLoading(), state.copyToData(values: List) и state.copyToError(th).
    Разовые же события, которые вы назвали Action-ами, в mvi называются side effects и их суть не том, чтобы разгрузить модель,
    а в том, чтобы при изменении конфигурации они повторно не появлялись на экране. Это легко решается котлиновскими Channel-ами.

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому +1

      Хороший комментарий, не согласен про MVVM, так как основная идея MVI в непротиворечивости стейта. Мой вариант от вашего не сильно отличается, просто у меня это силд классы. Можно было бы сделать точно такой же дата класс и внутри хранить одно из полей в силд классе, тогда было бы прям 1 в 1. Тогда как MVVM эт посто набор полей, которые очень даже могут быть противоречивыми. У меня же стейт всегда один
      Идея с extension функциями над стейтом интересная (я правильно понимаю, что это самописные extension функции?)
      Чтоб не путать с сайд эффектами в Компоузе я пытался объяснить их как одноразовые экшны. Через компоуз сайд эффекты это не всегда можно реализовать так как иногда они генерятся в ходе работы евента, который плюс ко всему ещё и может обрастать аналитикой и ещё много чем
      Надеюсь, понятно объяснил

    • @dm13kz
      @dm13kz 2 роки тому

      ​@@MobileDeveloper Спасибо за разьяснение, ваш подход стал более понятен, но некоторые вопросы всё таки остались.
      Если бы в примере использовался классический подход с xml-интерфейсом, то разделение модели на sealed классы сводилось бы к частичному обновлению ui и могло потенциально привести к несогласованности состояний интерфейса и бизнес логики. А учитывая, что используется compose и интерфейс фактически перестраивается каждый раз заново, то такой риск невилируется самой библиотекой. Получается цель mvi как архитектуры частично решается самой библиотекой - она сверяет новую dsl структуру с текущим состоянием ui и перестраивает разницу.
      Меня смущает, что разделение модели по sealed классам побочно определяет какому экрану какие данные нужны. Точнее даже какому состоянию экрана какие данные нужны. К примеру, если завтра при обновлении списка понадобится одновременно показывать уже загруженные данные и индикатор загрузки, придется переделывать и модель экрана т.е. sealed-класс и compose функцию. Если бы всё было в одном data классе, то достаточно было бы изменить только момент загрузки, где loading переключается с false на true. При условии, что compose функция изначально написана достаточно универсально.
      Получается, что модель берёт на себя часть ответственности за отрисовку, а не просто предосталяет данные для обновления интерфейса.
      Несогласованность данных внутри модели это палка о двух концах. С одной стороны это может быть ошибка разработчика по невнимательности, а с другой стороны стать со временем бизнес фичей. Я борюсь с человеческим фактором extension функциями модели, вы решаете это sealed-классами. Просто два разных подхода.
      Мне видится так, что ваш подход с sealed-классами более элегантен с точки зрения программирования, но имеет потенциальные риски в рефакторинге, при условии, конечно, что будет использоваться именно compose библиотека, которая берёт на себя задачу согласования состояний.

  • @ПавелГоловко-ц8ы
    @ПавелГоловко-ц8ы 2 роки тому +4

    Спасибо за видео. У меня вопрос, касаемо функции obtainEvent. В чем ценность подхода при котором у нас во ViewModel только один вход - obtainEvent, который внутри разводится через switch на несколько функций. Почему из View просто не вызывать нужную публичную функцию ViewModel-и, которая уже дальше создаст нужный state. Не кажется ли, что при данном подходе есть некоторая избыточность? Разве public-функций ViewModel-и не было бы достаточно?

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      Там не факт что не будет сразу же перехода в нужный стейт прям из obtainEvent. Плюс позволяет инкапсулировать какие-то функции. какие-то переиспользовать сразу же с нужным набором параметров, не накладывания на вью логику. Например, у вас есть функция fetchItems(count: Int, start: Int, filters: [Int]) и как вы ее будете из вью вызывать? Там нужно запоминать какое количество уже есть, с какого начинаем, какие фильтры. Все это должно храниться во вью модели, причем для евента скажем FetchInitial это будет параметры 0, 0, [], а для FetchMore 20, 1000212, [0, 1]. Вам либо логику придется частично во вью переносить (что плохо), либо через obtainEvent. В общем функция приватная не всегда 1 к 1 соотносится с евентом и в этом основная причина

    • @ПавелГоловко-ц8ы
      @ПавелГоловко-ц8ы 2 роки тому +4

      Верно, но что мешает сделать публичные функции без параметров fetchInitial и fetchMore, которые будут вызываться на стороне View, а эти функции в свою очередь будут вызывать приватную fetchItems с нужными параметрами? Тогда мы просто избавимся от набора sealed data классов.

  • @metallolomov
    @metallolomov 8 місяців тому

    А где начало всего этого? Если я только начинаю с чего начинать? как написать первое приложение и понять все эти вью и тд? Есть отдельный плейлист типа андроид студио начало? 😅

  • @maksym1266
    @maksym1266 3 роки тому +4

    Спасибо за видео, но для продакшена compose не кажется готовым. На что указывают experimental аннотации в том числе. Попробовал твой проект, при переключении всё моргает как новогодняя ёлка. Есть глюк, после повторного нажатия на Settings экран становится белый и табки перестают отвечать. Но в его будущее я, всё-же верю.

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Смотри, баг с навигацией это не компоуз, а мои руки кривые)) я поправлю это в ближайшем релизе.
      То, что он моргает это аналогично мои кривые руки и чуть-чуть компоуза. В частности это рипл эффект, который по умолчанию вешается на любой clickable. Сделать noRippleEffectClickable это пять минут. Тоже поправлю в ближайшее время )
      Но это право каждого же ) я считаю готов, но не могу и не имею желания кого-то принуждать ))

    • @maksym1266
      @maksym1266 2 роки тому

      Ребятки, хочу сделать небольшую ремарку к своему комменту. Не смотря на то что ещё используются аннотации experimental, пробовать и изучать compose однозначно стоит. Поскольку уже очень скоро experimental уберут и спрос на эту технологию стремительно начнёт рост.

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому +1

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

  • @eugene8863
    @eugene8863 2 роки тому

    Подскажите пожалуйста, умные люди в какую сторону покопать. Мне нужно сделать карту, типа дерева во все стороны. Её нужно будет скролить в любую сторону, с подгрузкой объектов. Какой то дефолтный компонент может взять за основу или ещё что то. Спасибо! Всем печенье!

  • @ЕвгенийЮрин-с5н
    @ЕвгенийЮрин-с5н 2 роки тому

    А как тестировать такие viewModel? Пока что возникают проблемы. Вроде указал все правила для корутин, на live data подписываюсь через расширение от гугла

  • @mistertwister1760
    @mistertwister1760 2 роки тому

    Что там за революция то с инапами?) что за либа?)
    требую :D

  • @homie2417
    @homie2417 3 роки тому +1

    Я где-то читал, что аннотация @Preview не создана для просмотра всего экрана, а лишь для @Composable функций, куда передаётся не ViewModel, а уже, просто, данные из State. Таким образом превью работает всегда.

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому +1

      Ну да, так оно и есть, поэтому я и выделяю вью в отдельную функцию с уже готовым стейтом

  • @deniskor1273
    @deniskor1273 2 роки тому

    Спасибо за видео!
    А где самый последний вариант кода посмотреть? в репе на гитхабе нет ничего того, что на видео реализуется, а имеено SupportScreen и иже с ним

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      Я ее случайно стёр :( ну в смысле ветку эту, но вью модели есть и в других экранах в проекте

  • @НикитаШумский-е3р
    @НикитаШумский-е3р 3 роки тому +3

    Спасибо большое за видео. Буду пытаться затащить к себе твой подход. Разве что я считаю, что Intent все же больше подходит, чем Event. Для меня Event это больше про то, что мы должны из VM что-то прокинуть в UI для пользователя, а не наоборот. Хотелось бы узнать твое мнение насчет таких штуковин как SnackBar и Toast. Как они укладываются в MVI? Это же single event. Показал и забыл. Нужен небольшой аддон к твоему видео :)

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому +1

      Забыл уже, если честно, показывал я там это или нет, но в проде у нас есть отдельный sharedflow для работы с экшнами ) здесь можно завести SinglealiveAction для этого

    • @НикитаШумский-е3р
      @НикитаШумский-е3р 3 роки тому

      @@MobileDeveloper а как вы обходите засылку двух одинаковых ивентов в sharedFlow? Оборачиваете, например, bool в класс обертку Event? Мне пришлось для этого сделать Channel

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому +2

      Для такого сценария лучше использовать StateFlow) у нас засылка двух одинаковых экшнов валидная ситуация. Мы делаем replayCache в одно событие просто, чтоб оно не проигрывало дважды одно и тоже, но мы можем заслать одинаковые экшны (например toasty тот же) а StateFlow такое фильтрует автоматом )

  • @ivanchepelkin6347
    @ivanchepelkin6347 3 роки тому +1

    У меня довольно похожий подход с mvi и разбивкой на стейты. Есть 3 стандартных стэйта - прогресс, ошибка, данные. И этот Стейт хранится в лайвдате: вроде круто, всегда вытягивается последнее состояние. Но!!! Если есть кейс в котором последнее состояние перед укладкой фрагмента в бэкстек - прогресс (например что то отправить и уйти на другой экран), то после возврата обратно на фрагмент приходится заново данные перезагружать ибо иначе будет висеть старый Стейт - прогресс. А вроде как не хочется. Не понимаю пока как правильно обработать такой Кейс.

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      Если данные из локалки, то в целом не вижу ничего страшного, но конкретно на этот случай, можно не использовать sealed class и хранить все в одном data class, а там соответственно будет поле с данными и поле isProgress, тогда достаточно будет только флаг сбросить

  • @immortal_lnight
    @immortal_lnight Рік тому

    12:10 почему не sealed interface? Они же легче вроде, я их обычно использую

    • @MobileDeveloper
      @MobileDeveloper  Рік тому

      Их тогда не существовало, а еще у них есть проблема с интеропом в иос (но это если про кмм говорить)

    • @immortal_lnight
      @immortal_lnight Рік тому

      @@MobileDeveloper не знал что они так недавно появились

  • @IlayMMC
    @IlayMMC 2 роки тому

    Почему бы не использовать State из композа вместо LiveData? Есть какие-то существенные причины так не делать или это просто для примера?

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      Например то, что для использования State тебе нужно чтоб все твои функции были Composable

  • @МитяПомидоркин
    @МитяПомидоркин 3 роки тому

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

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Это не белый экран, это риппл эффект ) уберу его в ближайшем релизе)

  • @inquisitor4894
    @inquisitor4894 Рік тому

    Эх, жаль конечно нельзя посмотреть исходники из видео. Те что на гите отличаются и частично нет материала

  • @ivanchepelkin6347
    @ivanchepelkin6347 3 роки тому

    Спасибо за видео! А можно ли подружить compose , navigation и dagger 2 вместо hilt? Есть реальные причины, почему я не могу юзать hilt ((

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Да, вполне ) там придётся чуть кода ещё дописать, но можно

  • @Ivan-s8y3g
    @Ivan-s8y3g 3 роки тому

    Не понимаю для чего в VM писать логику. На мой взгляд если её и использовать то только проксируя все вызовы до feature. А вообще пора выкидывать её)

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Тут сразу по нескольким причинам. 1 - к этому привыкли люди и у многих проекты выстроены на вьюмодели и моя задача была показать, что можно оставаться на ней, используя компоуз. 2 - В целом, если проект небольшой, то какая разница как ты это назовешь VM или Interactor ) смысл от этого не сильно поменяется ) А где-то это все объединять нужно, потому что так проще работать командой

  • @ТатьянаТ-е2л
    @ТатьянаТ-е2л 2 роки тому +1

    у вас есть обучение?

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      Да есть, очное и онлайн)

    • @ТатьянаТ-е2л
      @ТатьянаТ-е2л 2 роки тому

      @@MobileDeveloper а как найти? как ваш сайт называется?

    • @MobileDeveloper
      @MobileDeveloper  2 роки тому

      @@ТатьянаТ-е2л напишите мне в телеграм пожалуйста @neuradev

  • @blusterhash
    @blusterhash 3 роки тому +7

    "Рожание" нового стейта убивает весь смысл компоуза (который состоит в том, чтобы рекомпозировать только те части, на которые повлияли конкретные переменные вью-модели). Тут же получается, что при любом чихе будет рекомпозироваться ВСЁ

    • @МитяПомидоркин
      @МитяПомидоркин 3 роки тому

      А что мешает логически разбить экран на несколько функций, которые будут принимать в качестве аргумента несколько полей из стейта?

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому +1

      Нет, не будет) можете проверить. Рекомпозируется только то, что изменилось реально. Только что логами прошелся

  • @ДенисСаранин-м1и
    @ДенисСаранин-м1и 2 роки тому

    Так и не отрефакторил? А то в репе ничего нового

  • @TeMHbluTurpa
    @TeMHbluTurpa 3 роки тому

    Спасибо!

    • @MobileDeveloper
      @MobileDeveloper  3 роки тому

      Спасибо, что поддерживаете канал! ))