Are One-Time Events an Anti-Pattern? - Why Almost Every Android Dev Does It Wrong!

Поделиться
HTML-код
  • Опубликовано: 26 ноя 2024

Комментарии • 120

  • @Marco-dr2on
    @Marco-dr2on Год назад +48

    It's crazy how often your videos come out when I most need them. Thanks!

  • @abdelrahmankhaled7575
    @abdelrahmankhaled7575 Год назад +36

    I like these type of videos which does not directly teach a new concept but rather opens a discussion on a special topic this made me understand the topic better in deep, thanks for this, and hope for more videos of this type !

  • @ПавелКуликов-ж9н
    @ПавелКуликов-ж9н Год назад +15

    Thank you so much for your videos. I’m writing a diploma work in my university using Android development, I have never worked with mobile applications and I’m really love clean architecture, so your videos about MVVM, about Jetpack compose, comparison sharedFlow, channels, QR-codes, cameraX, AI and so on are GREAT. So maybe it’s just simple word “thanks” for you, Philipp, but you should know, that this “thanks” for me is COLOSSAL THANKS! I’m from Russia and our developers on RUclips don’t talk about all of this as good as you! So don’t stop making this very useful and meaningful videos, you are great)

    • @PhilippLackner
      @PhilippLackner  Год назад +1

      Thank you! All the best for your diploma 💪🏻

  • @AlexGnok
    @AlexGnok Год назад +13

    We actually have used another workaround for this - the old school Event class with isConsumed property. So the ViewModel sets the state (StateFlow), then the Fragment during consumption first checks if the Event has been consumed already (in which case just ignores it), then if not - does what it needs to do and sets the property isConsumed to true. This combined with some extension functions (like consumeIfHasntBeenAlready and peekContent for testing) makes a flawless job

  • @etasdemir
    @etasdemir Год назад +3

    Great video, thanks!
    one thing to add for 7:00 channel's default capacity 0 which used in the video and it creates a rendezvous channel. Publisher suspends itself until there is subscriber or subscriber suspends until there is a publisher.

  • @DenisFisenko
    @DenisFisenko Год назад +24

    Awesome video, thanks!
    We use state way for one-time events. To unify the handling of such events we've created a common interface like:
    interface UiEvent {
    val data: T
    val onConsumed: () -> Unit
    }.
    Where the `onConsumed` callback is set to the view model's function to set event property in the state to null.
    With some nice extensions, handling of such events on the UI layer is uniformed and clean.

    • @PhilippLackner
      @PhilippLackner  Год назад +1

      That's awesome, thanks for adding 🙌

    • @Narazgul
      @Narazgul Год назад +1

      I like this solution as well, in case I'd have to stick with state

    • @riyupapa39
      @riyupapa39 Год назад

      That is also good idea. If the event is not happen continotly like snackbar text or some of notify , MutableState with reset consume also good.

    • @LokendraBohara-s3u
      @LokendraBohara-s3u 3 месяца назад +1

      how to use this . any example

  • @gustavobarbosab
    @gustavobarbosab Год назад +1

    I agree with you Philipp... When we are using "Channels", the code is simpler to understand, and thinking about junior developers, working with states in this situation can be hard and generate a lot of bugs. This kind of thought about right and wrong depends on the context.
    Nice video, thank you for sharing your knowledge with us.

  • @nicoloagnoletti3275
    @nicoloagnoletti3275 Год назад +16

    Crazy to me how hard it is to make junior colleagues understand the difference between state and one-time events
    Great content as always 👍

  • @satnamsingh-qh2si
    @satnamsingh-qh2si Месяц назад

    I used state to trigger & Observe event in my Jetpack compose project and I can say it was a pain in as*** as most of the time I forgot to reset the state back after it's single use.. So thanks Philipp for having this video on youtube to guide people like us. Thanks a lot...

  • @amzpakistani3186
    @amzpakistani3186 Год назад +8

    Hey Philipp, excellent work! Can you please make an updated tutorial about ktor and Rest API? I would really appreciate that 🙏

  • @kotasjn
    @kotasjn Год назад

    I dealt with this dilemma for several weeks last year and I'm glad I ultimately stuck with the same solution that you did. If you have a solid understanding of channels and can handle them correctly, there's no need to use state for single events. 👍

  • @dahlola
    @dahlola Год назад +1

    Very nice video! I have been battling with how to implement one-time events and ended up following Manuel's advice because I wanted to stay on the safe side.
    But I agree that it introduces a lot of complexity and one junior developer in my team doesn't really understand it. I myself can forget about resetting the state also.
    But now I feel more comfortable using channels and think I will switch to that :)

  • @verbranntenetzhaut
    @verbranntenetzhaut Год назад +1

    Hey Phillip I subscribed to your channel a while ago because I got really interested in Android content but I stopped watching your videos because they just weren't that great presented compared to reading a blog article etc.
    Now I just want to give some positive feedback because this video is very good, referring to the blog article, preparing an example that demonstrates the different cases and keeping it all readable was very helpful.
    Your insights and POV are very valuable, thank you for the effort and I'll definitely tune in more often 👍

  • @mohammad_droid
    @mohammad_droid 6 месяцев назад

    Perfect!
    I was a fan of the sharedFlow school, but you convinced me as usual😂

  • @pelealexandru
    @pelealexandru Год назад

    thanks Philipp for the deep dive on the topic! i'm working on a greenfield project with compose and i was really not sure how to best handle this kind of viewmodel driven navigation scenario. i fully understand the options now!

  • @manticomar1146
    @manticomar1146 10 месяцев назад

    I don't care what the other dude from Google says for me. philip is always right. We respect u philip

  • @ernestguevara5968
    @ernestguevara5968 Год назад

    I saw the use of channels first in your form validation video and it blown my mind, because first I was using events then unknowingly has the same implementation as manuel, but I didn't like it for the same reason that I need to reset it everything, for me it looked dirty. So when I saw the channels in your form validation video, I replaced it and works and look what I want it to be!

  • @samadMahmoodi
    @samadMahmoodi Год назад +22

    there is no RIGHT way to do ANY thing in android

    • @sina2862
      @sina2862 3 месяца назад +2

      yes people just care about funectionality and performance of the app , they dont care for example you used jetpack compose or xml or native drawing

    • @__J____ff
      @__J____ff 2 месяца назад +1

      oh shut up junior ! stop encouraging antipatterns. There is a proper way to do something, thats why we have different things like stateflow or sharedflow.

  • @ssSFrankSss
    @ssSFrankSss Год назад

    This is a thumbs up video from me. After creating some complex apps I am entirely aligned with your position on this topic! Keep up the good work ;-)

  • @SerhiiSolodilov
    @SerhiiSolodilov Год назад +4

    The only issue is that rotation isn't the only reason to lose event. If you consider that ViewModel could be recreated too state will be on top of the solutions list.

    • @PhilippLackner
      @PhilippLackner  Год назад

      In which scenario would the ViewModel be recreated, but not the UI?

    • @SerhiiSolodilov
      @SerhiiSolodilov Год назад +5

      ​@@PhilippLackner UI also wont exist. Example is when the user start operation, switches to another app and Android decides to kill your app. When user get back, ViewModel needs to be restored and show correct UI and having state is more suitable for that.

    • @PhilippLackner
      @PhilippLackner  Год назад +2

      @@SerhiiSolodilov ah now I see what you mean, yes that is a good point

  • @nem1st
    @nem1st Год назад

    I was waiting for this video for so long. Thank you very much!

  • @bitwisedevs469
    @bitwisedevs469 Год назад

    First! Finally this topic! Indeed, resetting state which is being suggested by many can cause more bug when someone forgets it instead of sticking to use channel. Thanks a lot

    • @aldaricJohnes
      @aldaricJohnes Год назад +1

      channel are really bad for that, the second your app will be killed by the system you will have an inconsistent state between your state and channel, and that's way worse than "someone forgetting"
      Design errors are worse than dev errors

    • @bitwisedevs469
      @bitwisedevs469 Год назад

      Fair enough, actually that is what we are doing currently. However if this is the case then all we need here is to use StateFlow directly so no need to convert Channel to state. Unfortunately manually resetting each state is making it hard to maintain and it adds more complexity to read the system flow. So this approach is where we are heading.

  • @DayZilya
    @DayZilya Год назад

    I loved the video. Would love more of such content. Explaining differences of approaches

  • @leonardomiranda1349
    @leonardomiranda1349 7 месяцев назад

    Excellent video, you explained every scenario flawlessly 👌

  • @Otabek001uz
    @Otabek001uz Год назад

    Very nice, it's amazing, good luck Phillip!

  • @deepakkanyan1072
    @deepakkanyan1072 Год назад

    Today only I was working on same functionality, really thanks its really helped

  • @riyupapa39
    @riyupapa39 Год назад

    This is very very very important and powerfull tip!!!!!! Thanks Philipp!!!!

  • @ChrisAthanas
    @ChrisAthanas Год назад

    Super useful breakdown of a controversial and misunderstood factor of Andriod and pure compose apps

  • @walkmn
    @walkmn Год назад +1

    Sounds like we are still in process of finding best mechanism of doing one shot event from viewModel to UI

  • @AlejandraStamato
    @AlejandraStamato 2 месяца назад

    Brilliant video on a topic full of nuances, nice one :)

  • @Nixomia
    @Nixomia Год назад +2

    Your videos have been incredibly helpful, and I really appreciate you sharing your knowledge. I recently tried to put your Jetpack Compose tutorial into practice while building an app, but I'm having trouble with the login/register navigation and home nested navigation. Specifically, I'm unsure how to manage the scaffold for the login and register screens, and I'm also struggling to customize the FAB for each screen in the home nested navigation. Any guidance you could provide would be greatly appreciated.

  • @Guilo583
    @Guilo583 Год назад

    It is amazing. I always learn new thing when watching your video. Thanks

  • @theophilus494
    @theophilus494 Год назад

    wisdom!!! thanks always Philipp

  • @omkarpawar1741
    @omkarpawar1741 Год назад +6

    why does android not provide a simple and optimal way to develop apps, instead of focusing on core development of what features our might need, developers have to worry about what to use for development, there are so many permissions to handle that it has become crazy, then with compose state and viewmodel, in viewmodel satetfllow, sharedflow, and the list goes on, and the pace at which these changes come makes all the code developed earlier redundant

    • @PhilippLackner
      @PhilippLackner  Год назад +12

      I agree, but I think it's also incredibly easy to say that and hard to do. Making everything safe for users, easy for developers, easy to use for users, run as fast as possible is very complex and a lot of interests clash together.
      If developers would have a much more limited toolkit which is simpler to use, they'd again complain why they don't have fine grained control. It's just hard to please everyone to the fullest degree.

    • @Naxomiun
      @Naxomiun Год назад +2

      You dont need to use all these tools. In fact they are pretty useless if you know what are you doing. Most of these things can be replaced by common and 'old' patterns, which didnt have these problems btw

    • @omkarpawar1741
      @omkarpawar1741 Год назад +1

      @@Naxomiun using old tools has the advantage of their reliability but there is always a risk of google deprecating them and bringing out half baked or totally different tools, the way SAF(Storage Access Framework) was handled then the epic moment when deprecations were deprecated and then brought back, now in compose handling a simple variable or an UI state is just a joke, compose state, view model, they could have just brought one viewmodel api and managed the one time events more effectively, some thing which has to baked in the OS itself is left to the developers, like handling denial of permissions with no Propper out of the box solution where developers have to provide their own logic, instead on working on a new feature for their app , imagine developing an app requiring camera from a service using notification where images and video permission are to be handled for api 32 and lower, api 33 and then api 34 and higher. wonder if the various teams developing different apis within google even communicate with each other where different "features" end up being a problem for developers

    • @Naxomiun
      @Naxomiun Год назад +1

      You will always be able to use bare threads instead of coroutines, service scoped classes instead of viewmodels, callbacks instead of flows or hand dependency injection instead of Hilt. Theres no way no one could ever deprecate those as they are part of the language. In fact, the possibilities of viewmodels/livedata/etc being deprecated are really high if you know about google historic changes in its APIs.

  • @unknownBoy85lover
    @unknownBoy85lover Год назад

    Thank you it will helpful absolutely great value to your videos and sharing your experiences with us ❤❤❤❤

  • @evgeniy2012year
    @evgeniy2012year Год назад

    great video! Events look like the best option. thanks

  • @valentinvolodarskij
    @valentinvolodarskij Год назад +1

    Thanks for a video on a really interesting topic but I have one question about LaunchedEffect. I have seen in many examples that we have to pass the lifecycle as a key to close the LaunchedEffect if the owner of the lifecycle has changed. But do we really need to pass it as a key? Because according to the documentation the LaunchedEffect will be closed and restarted if one of the keys changes OR the LaunchedEffect leaves the composition. So in general we will only close and restart this effect when the activity has recreated itself (which means that the LaunchedEffect will leave composition even without lifecycle as a key) or I don't understand other cases where the lifecycleOwner is changed
    Thanks in advance to everyone for any clarification on this question! :)

  • @mobiledevpro
    @mobiledevpro Год назад

    Thanks you shared you thoughts about that . Well-explained👍

  • @dawidhyzy1425
    @dawidhyzy1425 Год назад

    Would be nice to hear Manuel Vivo response too 🙂

  • @stanchostanchev5991
    @stanchostanchev5991 Год назад

    All this sounds very nice, but how these constructions will behave when we set "Don't keep activities" to ON in Developer options, which will make activity to be recreated every time when we bring test app from bg to fg. This will illustrate how the app will behave after the device goes to "doze" mode.

  • @jeyhey5320
    @jeyhey5320 Год назад

    For somebody who has always used a state-solution, anything else seems odd. Of course you need to handle state reset because it is a state. You can’t get around it no matter what you do. You also need to think about it in the other solutions.

  •  Год назад

    Very well explained Phillip

  • @1stCyborgOnMars
    @1stCyborgOnMars Год назад

    Great video. Thanks for helping again

  • @alexeysimchenko7494
    @alexeysimchenko7494 Год назад

    I definitely prefer a State to use

  • @yeminnaing7197
    @yeminnaing7197 Месяц назад

    Thanks! Now I do understand very well .

  • @PedroBarbosaRoman
    @PedroBarbosaRoman 2 месяца назад

    Regarding the article solution, if we are sending events (by updating the compose state object) with a high frequency (milliseconds) doesn't it drop some of them? Since the compose rendering system drops the intermediate states and only considers the last one for the recomposition.

  • @mariuskohmann8476
    @mariuskohmann8476 Год назад

    Great video! How does SingleLiveData from LiveData lib compare to these solutions using Flow/State?

  • @ahmetozcan2278
    @ahmetozcan2278 Год назад +1

    What about keeping a list of ui events in the state object and update the list when the events are processed ?

  • @sriduttan7692
    @sriduttan7692 Год назад

    Hey @PhilippLackner , Awesome video, thanks! I have a Qn, WhileSubscribed function takes stopTimeoutMillis. can't we just set stopTimeoutMillis to 5000ms for ex. this will address all the issues when lifecycle/orientation changes happen. it will wait for the collectors to come back, therefore emissions won't get dropped. (StateFlow)

  • @TuanBuianonymous
    @TuanBuianonymous Год назад

    Wow, i have been wait too long for video like this. Thankssssss

  • @nowfarhan
    @nowfarhan Год назад +1

    Can you skip events when you use sharedflow with repeat=1? If you are using it for events, I think this is the easiest solution.

    • @vitalijuskolinko9011
      @vitalijuskolinko9011 Год назад

      replay = 1 is equivalent of StateFlow :) You will be able to go back then.

  • @TheRcfrias
    @TheRcfrias 9 месяцев назад

    For some reason, the channel option will just be "collectable" once, as well as emitted once. I was hoping I could have 2 different isolated views react to this channel through the viewModel, but only the first view that collects the event will get the "message". I am switching back to state, since this is proven solution for precisely this scenario.

    • @PhilippLackner
      @PhilippLackner  9 месяцев назад +1

      What you're looking for is a sharedflow as the name says :)

    • @TheRcfrias
      @TheRcfrias 9 месяцев назад

      Memory-wise, is there any difference, improvement, best practice to pick sharedflow instead of state in a view controller? Are you saying sharedflow will also emit once, but can be heard by several views and not just one?

    • @PhilippLackner
      @PhilippLackner  9 месяцев назад

      @@TheRcfrias correct, it's like a channel that allows multiple subscribers

  • @sina2862
    @sina2862 3 месяца назад

    its be great if you create a app like music player or chat app and do your best approaches for each senario

  • @rakam.a8070
    @rakam.a8070 Год назад +1

    What's the difference between collecting state with MutableStateFlow and MutableSharedFlow? I usually use MutableStateFlow but I see other people use MutableSharedFlow as well, which one is recommended?

    • @PhilippLackner
      @PhilippLackner  Год назад +2

      SharedFlow is NOT state, it doesn't cache anything by default. As the name says, only use StateFlow for state

    • @adrian110288
      @adrian110288 Год назад +1

      The difference between the two is mainly in a way they behave upon subscribing to them. I think official docs explain it well, you can then decide which behaviour suits your case better 👍

  • @LEEJIHUN
    @LEEJIHUN Год назад

    필립 센세 항상 감사합니다

  • @codingCouncil
    @codingCouncil Год назад +1

    What about collectAsStateWithLifecycle ? . We can't provide the dispatcher.Main.immediate there can we ?

    • @codingCouncil
      @codingCouncil Год назад

      anyone wondering how to provide Dispatchers.Main.immediate with collectAsStateWithLifecycle; this is how
      val uiState by viewModel.uiState.collectAsStateWithLifecycle(context = Dispatchers.Main.immediate)

  • @eddieng7136
    @eddieng7136 Год назад

    Would you talk about the new compose Multiplatform wizard ?

  • @Slodin
    @Slodin 3 месяца назад

    I really disliked the ui state thing with cleanup work. I often have to test a screen many times and oops forgot to clear a state and now it's debugging time. I have been using that pattern, but I run into bugs so often that I's annoying. Which is why I got here I guess.
    Is there really no downside to this approach tho? it seems like everything we do will sacrifice something in order to achieve something else. If it's just some performance then I would happily give it away as most applications I make are not intensive. Having a less buggy approach beats any performance hit IMO (as long as it doesn't renders the app unusable).

  • @redaelmadini
    @redaelmadini Год назад

    What if we have to send multiple events at one (in a list)? I think the third solution with "state" is most suitable. Plus, in some scenarios we have to later consume this event after an action, i.e undo delete action within a snack bar that we have to keep after rotation.

  • @mesutemrecelenk5447
    @mesutemrecelenk5447 Год назад

    Hi Phlipp. Thanks for this nice tutorial. How to use observeAsEvent function for our all composables?

  • @breensrobert
    @breensrobert Год назад

    this was insightful

  • @nikolozlatsabidze2196
    @nikolozlatsabidze2196 Год назад

    what can you suggest for state inside viewModels. data class or sealed class. consider the case when ui is very complex, data classes become very big and complex with lots of parameters. is it the sign to use sealed class or even decompose it using another viewModel. so may be multiple viewModels for that particular screen.

  • @jaysh1175
    @jaysh1175 Год назад +3

    Nice video, but to be honest I kinda fell over the argument against state being: "You always need to think about resetting the state [...] when I think of a larger team where the might be junior developers as well [...]" and then proceed to add the Dispatcher.Main.immediate which is equally 'not obvious' (or even worse) in my opinion

    • @PhilippLackner
      @PhilippLackner  Год назад +9

      Yes, I agree that the immediate dispatcher isn't obvious either, but it's a single rule that has to be established in the team ("when observing events, ALWAYS use this ObserveAsEvents function").
      Resetting state is not so trivial, since it sometimes need to be reset and sometimes it's fine to leave it (such as when the previous backstack is cleared after navigation). Even though, I knew the mechanism behind resetting state, it happened to me more often than not that I forgot it. Especially, because one-time events on Android are almost always handled with channels/SharedFlows in Android projects, using state is a rather uncommon approach which is why I think this concept would be new to 95%+ of junior devs and would require an additional intro

  • @mikeshilovski1512
    @mikeshilovski1512 Год назад

    Great videoo, thanks 👍

  • @MajesticNut
    @MajesticNut 3 месяца назад

    But the Channel's buffer by default is 0...

  • @GulshanRahimova
    @GulshanRahimova 11 месяцев назад

    Great video

  • @龔詩測試機1號
    @龔詩測試機1號 Год назад

    Nice topic bro

  • @danieldawson8018
    @danieldawson8018 Год назад

    cmd + f, my friend. ;)

  • @hnim2292
    @hnim2292 Год назад

    how about use StateFlow with Event class?

  • @meetb26
    @meetb26 Год назад

    if Mutable StateFlow and live data use so problem

  • @MonichGPT
    @MonichGPT Год назад

    You just need helper container with nullable field

  • @TheFlexath
    @TheFlexath 2 месяца назад

    thank youuuuuuuuuuuuuuu

  • @aamirhoda7363
    @aamirhoda7363 2 месяца назад

    Phillip, I'm just blown away by the amount of knowledge you have & your ability to deliver that knowledge in such a precise & to-the-point manner that probably a 10 year old can also understand perfectly fine.
    Superb, thanks a lot, massive respect, unlimited love! 💙💙💙💙💙💙💙💙💙💙💙💙💙💙💙
    BTW, just a funny note, at the end of your video, you could have done that counter check on the best-of-3 condition because previously you had to do that two times to validate your point 😅😅😅😅

  • @yuuzuX
    @yuuzuX Год назад

    If using presenter to update the ui?

  • @androidpc9656
    @androidpc9656 2 месяца назад

    COMpose

  • @noider545
    @noider545 Год назад

    Incredible.Wonderful.Exceptional.

  • @frajola_br
    @frajola_br Год назад

    Good morning, do you have a video teaching how to mask input? I didn't find any tutorial teaching how to mask phone number, or even currency

  • @ka61er
    @ka61er 11 месяцев назад

    :v i always use the third way

  • @HelloWorld-xt8wp
    @HelloWorld-xt8wp Год назад +1

    Excellent video. But isn't a coroutine context in viewmodels and in views already Dispatchers.Main.Immediate? I do not understand how this hack works.

  • @stoyan_vuchev
    @stoyan_vuchev Год назад +1

    I've got a notification about the video exactly when I was about to implement a navigation channel!
    Coincidence? I think NOT! 😂Thank you Philipp for the amazing content, keep up the Great work! 🙌🏼
    Here is a quick snippet of the function to observe such events in a lifecycle manner:
    @Composable
    fun CollectFlowWithLifecycle(
    flow: Flow,
    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
    onCollect: suspend (T) -> Unit
    ) = LaunchedEffect(flow, lifecycleOwner.lifecycle) {
    lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
    withContext(Dispatchers.Main.immediate) {
    flow.collect(onCollect)
    }
    }
    }