The rule Philipp refers to is a convention only. It states that a suspend function should never block the calling thread. If we write the suspend function, then the responsibility is ours to ensure that it is main-safe. If we use a 3rd-party suspend function, then it's the responsibility of that developer to abide by the convention. However, it is still our responsibility to ensure (e.g., read the documentation or study the code) that the 3rd-party suspend function follows the convention. In the case of both Retrofit and Room, their documentation states that any suspend functions they generate will be main-safe.
If your try-catch block can catch a cancellation exception, then the code is already written incorrectly. The block must catch code exceptions inside the corroutine builder or the block must wrap the topmost corroutine builder - the parent.
withContext just means that everything inside its lambda will be executed in the specified dispatcher, it does no mean that it will magically make the functions called inside of it non-blocking, but it is useful because we can offload the main thread from heavy operations (like an http call or a filesystem operation) and just block a thread on the IO dispatcher instead and then get the result on the calling thread (Main in this case), so it is basically a callback but without the ugliness of it
I agree with all the points provided, but what if a repository method does large mapping stuff / CPU work / multiple calls to Room's DAO? Should i wrap a method with withContext(...) or put withContext(...) inside mappers and DAOs?
There is no such thing as light computation on UI thread. Any computation on it will result in a frame drop, it just a question when and how many frames will be dropped.
Thanks phlipp for this useful video. I want to ask a question about this topic. I have a countdown timer in whole app that in activity onCreate. This timer is resetting at user interaction. This timer must be in io thread? Is it necessary?
it wasn't that clear.. so the coroutine scope should only be written inside a suspend function? not from the repo's function and not from the ViewModel where you call the repo function?
I'm a little confused sorry, when you say that that the non suspending blocking part of a corutine won't block the underlying thread you mean it won't block the main ui thread right? The IO thread on which the corutine runs on will be blocked by the synchronous code in the coroutine until the coroutine reaches a suspending point?
When designing a public api, what would be the best approach to let the user know that he don't need to use with Context? Or Public api should be always mainsafe?
Is there any downside if we wrap a suspending function with a with context(someDispatcher) anyway? For example: I've a room DAO and in the repository implementation, I wrap my dao call with a with context() block despite knowing room calls are done in background thread.
But is it a good idea to give this control to retrofit ? I mean, it's retrofit and yes we can trust it, but what we are using other libs which are fetching or doing something isn't it better for us to do this change ourselves?
So, this information is really useful, however, if every suspend function is supposed to be Main Safe, then why should we not switch off of the main thread ASAP and onto the default thread? After all, if the Main thread is for doing UI work, then why do non-UI work on it?
Because it would be a nightmare from maintenance of that code point of view. Technically you could switch every list iteration to non-main thread but in reality nobody does that.
For the people out there: This does not mean that you should not take care of your threads just because there is a call to network through retrofit at the end of your logic. You would end up executing your entire code at the scope default dispatcher; probably Main.Inmediate.
this is absurd situation you imagined people do not normally put all blocking logic inside one function terminated with retrofit call. you should have your blocking code in separate main-safety functions
@@trollberserker1515what? Its really common to map data models into domain/ui models, merge lists from two endpoints, or just formatting some info before sending it to an endpoint. If you dont handle your threads you'll execute all this logic in your main thread. Its simple to understand. Retrofit switches your dispatcher right before making a net call.
Thank you a lot It will really help me But i have one doubt because before that I always switch context to IO in repository. It means we dont need to switch context to IO when making http request using retrofit ? Yes or No and explain please
I have a doubt when making a splashscreen when producing on my cell phone, it installs 2 apps, one with the splashscreen and the other with the normal one. And if I uninstall one, both are gone
The rule Philipp refers to is a convention only. It states that a suspend function should never block the calling thread. If we write the suspend function, then the responsibility is ours to ensure that it is main-safe. If we use a 3rd-party suspend function, then it's the responsibility of that developer to abide by the convention. However, it is still our responsibility to ensure (e.g., read the documentation or study the code) that the 3rd-party suspend function follows the convention. In the case of both Retrofit and Room, their documentation states that any suspend functions they generate will be main-safe.
Always setting the Dispatcher even though we shouldn't is a code smell. Thanks for awesome content
Thanks Philipp it was very useful. to me you're the most trustworthy person I've ever seen. I can always rely on your content🤘🏻
Thanks a lot, that's a big compliment 🙏
Thank you for your videos Philipp! I always learn something new!
Happy to help!
++
++
Philipp doing what Philipp does, Thanks
You should not catch generic exception in the suspend method, such as coroutine may throw `CancellationException` to interrupt invocation
Yes I covered that in a separate video, but I thought not to further overcomplicate this one with an additional non-trivial concept :)
If your try-catch block can catch a cancellation exception, then the code is already written incorrectly. The block must catch code exceptions inside the corroutine builder or the block must wrap the topmost corroutine builder - the parent.
@@PhilippLackner What is the video?
Really useful. Thank you Philipp. Please more videos like this.
Mate I can't explain how much I love your content
withContext just means that everything inside its lambda will be executed in the specified dispatcher, it does no mean that it will magically make the functions called inside of it non-blocking, but it is useful because we can offload the main thread from heavy operations (like an http call or a filesystem operation) and just block a thread on the IO dispatcher instead and then get the result on the calling thread (Main in this case), so it is basically a callback but without the ugliness of it
interesting contents! It was useful tips. Especially 'suspend function call' icon in Android Studio. Thanks philipp
You can use repeat(5) {} instead of that for
I agree with all the points provided, but what if a repository method does large mapping stuff / CPU work / multiple calls to Room's DAO? Should i wrap a method with withContext(...) or put withContext(...) inside mappers and DAOs?
If mapping is CPU heavy, the mapping function should switch the context to the default dispatcher
@@PhilippLackner why not computation dispatcher?
@@Coden55 sorry, typo I correct it. Of course the default one
There is no such thing as light computation on UI thread. Any computation on it will result in a frame drop, it just a question when and how many frames will be dropped.
Thanks for the info!
Hey, how does retrofit switch to the background thread? Is there any official source that declares that?
Thank you Philipp, your videos are very valuable to us.🎉
Great video again :3
Philip woke up and decided to show the facts 😅
6:43 "even if it would switch to main dispatcher this would not be a problem" how come?
excellent video!
Very useful and trusted
One of the thing to keep in mind is that you should be injecting the dispatcher. That way you can inject test dispatcher while writing unit tests
yep
Amazing content as usual, Consider making a video on how to retrieve data from a suspend function via flow or callback functions.
i've been researching coroutine context for the past 5 days 😮
Thanks phlipp for this useful video. I want to ask a question about this topic. I have a countdown timer in whole app that in activity onCreate. This timer is resetting at user interaction. This timer must be in io thread? Is it necessary?
Hey Phil, do some magic on compose UI!
I faced this problem. Thanks for the detailed video 😍
Can you change that yellow lamp to green one 💚
it wasn't that clear..
so the coroutine scope should only be written inside a suspend function? not from the repo's function and not from the ViewModel where you call the repo function?
I'm a little confused sorry, when you say that that the non suspending blocking part of a corutine won't block the underlying thread you mean it won't block the main ui thread right? The IO thread on which the corutine runs on will be blocked by the synchronous code in the coroutine until the coroutine reaches a suspending point?
Not the whole thread will be blocked, just the single coroutine. You can launch lots of Coroutines on the same thread which will all run independently
@@PhilippLackner Thanks a lot
@@PhilippLacknerindependently? You mean concurrently or parallel?
@@marianpazdzioch5437 Concurrently, not in parallel. Parallel run implies using multiple CPU cores, not threads.
Why try-catch in the Repository? Adding this error handling logic should be located in the Use Case
It definitely depends
When designing a public api, what would be the best approach to let the user know that he don't need to use with Context? Or Public api should be always mainsafe?
That's the concept, the user should be able to rely on your API properly switching the dispatcher
@philip please tell me why jetpack compose fell laggy please one vadeos on this
Coroutines and supend functions are the most difficult thing to me to understand since Android 1.x
Same here. RxJava is easy comparing to coroutines.
Is there any downside if we wrap a suspending function with a with context(someDispatcher) anyway? For example: I've a room DAO and in the repository implementation, I wrap my dao call with a with context() block despite knowing room calls are done in background thread.
Not a performance downside, but overdoing this makes the code less readable since every withContext adds another indentation and line of code
But is it a good idea to give this control to retrofit ? I mean, it's retrofit and yes we can trust it, but what we are using other libs which are fetching or doing something isn't it better for us to do this change ourselves?
Then check THAT library.
So, this information is really useful, however, if every suspend function is supposed to be Main Safe, then why should we not switch off of the main thread ASAP and onto the default thread? After all, if the Main thread is for doing UI work, then why do non-UI work on it?
Because it would be a nightmare from maintenance of that code point of view.
Technically you could switch every list iteration to non-main thread but in reality nobody does that.
Tightening yourself to other library implementations isn't a good recommendation.
A small use case that could lead to blocking code inside the library would mess the app up. I agree with you, even though his video is on point.
Nice One
Can you please record your videos in 4k?
seeing `for(i in 1 .. 5){}` instead of `repeat(5) {}` hurts my eyes :
i did not get the point.. please
Please make a tutorial how to build android tv with jetpack compose.. thaanks ❤❤❤
For the people out there: This does not mean that you should not take care of your threads just because there is a call to network through retrofit at the end of your logic. You would end up executing your entire code at the scope default dispatcher; probably Main.Inmediate.
this is absurd situation you imagined
people do not normally put all blocking logic inside one function terminated with retrofit call.
you should have your blocking code in separate main-safety functions
@@trollberserker1515what? Its really common to map data models into domain/ui models, merge lists from two endpoints, or just formatting some info before sending it to an endpoint. If you dont handle your threads you'll execute all this logic in your main thread. Its simple to understand. Retrofit switches your dispatcher right before making a net call.
Still, if those lists are not long (arbitrary, YMMV) then mapping/iterating over them on main thread is acceptable.
Thank you a lot It will really help me
But i have one doubt because before that I always switch context to IO in repository.
It means we dont need to switch context to IO when making http request using retrofit ? Yes or No and explain please
Correvt, retrofit does that
Holy cow, I am one of those who used it incorrectly.
I have a doubt when making a splashscreen when producing on my cell phone, it installs 2 apps, one with the splashscreen and the other with the normal one. And if I uninstall one, both are gone
Brother teach us backend as well apart from firebase
Now i know why my code is lagging
😃