95% Don't Understand THIS About Coroutines (Main-Safety)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video and in this video I just quickly want to talk about a very common mistake people do with coroutines this mistake can really lead to very bad performance in your app it's not only really a mistake but rather a big misunderstanding of how core routines work so let's take a look at what I mean so very often you have something like this a retrofit API just a normal get request for example for getting a user here and then you might have a user reposit atory where you do something like this you get a user by its ID returns a result and then you return with context dispatchers IO because that's obviously an IO operation and here you call that retrofit function if everything went well you return a successful result if there's some form of exception you return a failure so what is wrong here the big misunderstanding lies in this line here people think they always need to switch the cortin context when they use a c suspend function and while that itself here won't lead to a bug it won't lead to any performance issues it's really only an extra line of code which itself is not that bad but the root of this problem of doing this of having this with context block here at this place is a big misunderstanding of how cortines work because at this point this get user function which comes from retrofit will switch the context to the right dispatcher so you don't need to do this on your own but how do you really know that which function already does that behind the scenes because there is a core rule when it comes to implementing functionality that itself is blocking but not yet suspending and you want to bring that into a suspending function into suspending context that core rule is that you design that function for so-called main safety main safety means that it should be safe to call your suspend function from any threat but especially from the main threat so it shouldn't matter if your suspend function makes an API request if it makes a database cre if there is just a long running and very CPU heavy task it really shouldn't matter from which threat or from which co-routine dispatcher you call this function that is what main safety means but let's bring this into a context where we can really all understand what this really means and when you need to take a look at Main safety and for that I've prepared this file reader class this file reader is just a class which will read a file from our assets folder so just exposes the suspend function read file as bites we pass in the file name and then we get back a bite array after that file was read and to do that we can simply use our context refer to the assets open the file with the corresponding file name and use will just open the input stream so we can really read the bit from that file and finally return these and you now want with the suspend keyword that you can call this function in a normal Co routine and that co- routine will suspend until reading that file finished then in this case if you have your code like I do here you violated the principle of allowing main safety and and Studio even already gives you a little hint here that the suspend keyword is gray out because that effectively does not do anything here the suspend key would only makes sense if you also have suspending calls inside of the suspend function well right now we don't have any suspending calls which we would see here at these icons on the left like here in user repository if there is such an arrow then you know at this line there's a suspending function so with context is one and get user is also one but here we don't have that however just because something is not a suspend function does not mean that it's not a blocking function the best example for this would be threat. sleep um which you can use to make the current threat sleep for one second in this case and this will completely block the current threat and this will also block the current threat if you execute that from within a suspend function however the coroutine won't suspend while that happens and the same way in this function the cortine won't suspend when reading that file even though that is something that can take a little moment so what you would need to do here instead is at this point the withd context block would be totally needed so here we would want to have the with whoops with context this patches IO because reading a file is obviously an IO operation and then in here you could have this and suddenly the suspend keyword is not grayed out anymore because now on the one hand this function will suspend until reading this file is finished so we get the normal cor routine behavior and on the other hand we prevent that it will block the underlying threat so if we just do it like this and we don't have this with context block and we execute this from the main dispatcher inside of a cortine then that means this function will block the actual main threat so our UI thread which is definitely noticeable if we read in a large file here so to quickly show you that as well let's have a return statement do it wrong and for that I prepared a little 100 megabytes test file which I just want to read in and in ourain activity I also prepared a little infinite transition so it's really nothing else than an animation that allows us to infinitely rotate our red box here and then after a delay of 3 seconds we call this function read file as by so we read in our 100 megabytes file and even though as you can see this is a suspend function we have to execute this from within a CO routine you will see something interesting so let's run this take a look here you can see animation is fluently but then after 3 seconds there was a little lag in the animation and this will be even more significant if we just read in that file multiple times so just for a little demo for I in let's say one to five if we read five of such files and launch this again take a look here until it's relaunched there we go wait 3 seconds and then you will see okay it completely lacked here in our animation but let's Now understand why this happens and why this won't happen let's actually do it again why this won't happen if we surround this with a with context block so do it like this now if we switch the context to the io dispatcher and make this a real suspend function with a suspending function that is called in here and if we now relaunch this take a look here then even though we read read in five of these files directly on the main dispatcher as you can see here so launched effect the cin scope here will just be launched on the main dispatcher by default even though that is the case we won't experience any lag here because our read file as byes function switches the dispatcher to the right one and even if it would switch to the main dispatcher this would not be a blocking uh blocking function here since it would still be executed inside of an independent ctin so with context effectively avoids that the blocking but non suspending code here like reading such a file will not block the underlying threat so the whole threat instead with with context it will only block the corresponding Co routine which can be completely independent of the underlying threat so now just to summarize what main safety means and when you need with context and when you rather don't need with context like here so here you would be totally fine with just calling the try and catch blog like that so the responsibility of switching to the right dispatcher always lies at the function which executed the blocking code which is not suspending yet this function here does not execute blocking code which is not suspending it executes blocking code here with get user but that is already suspending so this function does not have any responsibility to switch to the right dispatcher it would actually be retrofit here where the responsibility would lie and retrofit might call some okay HP code under the hood which does not expose any suspend functions so retrofit would need this with context block under the hood so it's really safe to call this get user function from the main dispatcher from the io dispatcher it should really not matter on the other hand in our file reader here that is really needed because without this with context block there are zero suspend functions inside of this function even though we have a blocking function here which is this open function so again if you have blocking but non- suspending code this function is blocking non- suspending otherwise we would have an error here then you need to surround this with with context otherwise you will really risk your app's performance if you execute such a fake suspend function let's call it like that on the main Patcher and on the other hand if you have a suspend function which just executes other suspend functions and besides that not really blocking code then you totally fine not switching the context because each single suspend function should do that on its own because each single suspend function should do that on its own at the lowest level where it only executes blocking but non- suspending calls so I hope that really got clear it's a complicated topic but it's uh quite important and if you feel like there might be more of such mistakes you might be doing in your code which harm your apps performance you your apps architecture and your apps readability then it might be something that we work together very closely and we will take a look at your code because that is exactly what I offer in my 10we mentorship program if that sounds good to you you will find all further details Down Below in this video's description where you can also apply for this program other than that thanks so much for watching I will see you back in the next one have an amazing rest of your week [Music] bye-bye
Info
Channel: Philipp Lackner
Views: 26,226
Rating: undefined out of 5
Keywords:
Id: 4_fDefmI3yI
Channel Id: undefined
Length: 9min 37sec (577 seconds)
Published: Wed Oct 25 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.