Android Studio: Debugging Tips n' Tricks (Android Dev Summit '19)

[Music] hey everyone I'm David Herman and I'm Justin Nieto and we are developers on Android studio now earlier this year at i/o other members of our team gave me tips and tricks' talk about editing your project in Android studio it is excellent however as developers we all know that there are some days you just spend way more time in your debugger than you do in your code editor so to that end Justin and I we went around our team we asked everybody for their favorite debugging features we don't have a lot of time up here so we narrow them down to what were the essential tips that we think will be easy to incorporate into your debugging flow now in order to showcase these features we put together a simple game inspired by the new Android 10 version rules simply are you grabbed Tim's you get points you run into too many deserts which represent old versions it's game over the core of this game is a loop that tries to run at 60 frames a second where it updates all of the objects in the game and then it renders them I'm going to assume that your own app is not quite the same as this one but don't worry all of the features that we're going to share here will apply for any project now this is all gonna be a demo from this point forward and we have a lot to get into so I'm gonna get out of the way and kick it over to Justin right so as David mentioned we're gonna talk about a lot of debugging tips but if you're anything like me you're gonna sit through this whole presentation and then go home and still use printf statements everywhere to see what's going on that's okay so we have tips for you too we're actually going to start here so if we open up logcat which is where all of our our android logs surface in the IDE you'll see that when we start the app so we have two things going on here we're logging the FPS like the frames per second rate every second and then when Android dies at the end of the game we also log the score that you got through this session so you might notice right away that the there's kind of some bloat here things like the date and the threads that we don't really care about one thing you might not know is that you can go to this settings bar over here and remove some of those information so that we can cut it down you just the tag and the message that we care about right yeah so even with this you might notice that we have all of these spammy logs and they're kind of getting in the way of us seeing these high score messages so what we can do there is actually search here to filter things down and this is nice but if this is a super useful search that you do all the time or you have multiple searches you might want actually save this so you can come back to it later so what we can do to make that happen is to go here and add a custom filter so if we go over here and edit filter configurations I did this before but so we can type in so in this case I'm looking at high scores I can type in the substring that I'm looking for and now I have the same filtering effect but I can jump back and forth between different ones so for now I'll go back to the whole the whole log set so that's great but maybe we really want to look at every log in the game we just don't want these spammy FPS logs so what we can do here is highlight the substring that's relevant between all these different spammy things that we don't care about right click on it and go to fold lines like this and this creates a filter for us that will collapse all of these messages together and leave just the things we care about so if I do this you'll see that now we're just looking at the scores and some other information from the emulator and if we need to look at this information later we can always expand it or collapse it so now that we've got all the the logging bits out of the way let's go ahead and jump into some code in sorry in particular we're gonna start at this loop that happens every every frame of the game loop where we go through all the items and reason over them so I should since it happens every frame I should be able to set a breakpoint here and when I start debugging this should hit pretty immediately so let's go ahead and do that Oh No okay so I didn't do that just kidding so the I did this on purpose I hit the Run button when I started the app instead of hitting the debug button and while I did it on purpose to demonstrate the next feature I want to show this actually happens to me quite a bit in real life and it can be super frustrating to stop the app start at any debug mode again and then work your way all the way back to this state that you're trying to deep from so what you can do instead is click this icon over here and attach the debugger to the already running process and when I do that you'll see that we hit the breakpoint pretty immediately so that being said we have our breakpoint but say the bug that we're tracing down is related to when Android actually hits one of these desserts it's not good to have a breakpoint right here because it gets hit every frame and we never get up to the dessert that we're trying to hit to repro so instead what we might want to do is go down to the part of the code where we've actually determined that we've hit a dessert one thing that's nice because I don't need this breakpoint anymore I can just drag it to the line that I care about and it gets rid of it from the old line and then I can run and when I collide now I get it on collisions that's great but maybe the bug that I'm actually chasing down is only related to the last dessert that I hit you know when Android dies at the end so for that we have the same problem I have to stop every time I hit a dessert that gets tedious so what we can do is what's called setting a conditional breakpoint so I can right click on this break one in the debugger add my condition I can put any boolean expression in Kotlin here and if it evaluates to true when the code hits it then we'll we'll catch at the breakpoint so in this case I'm going to type you know player dot health is equal to one because we're about to decrement it for the last time in the case that we care about and when I go through that we won't catch until so we hit twice and we didn't catch now i hit the last one and i've caught the breakpoint and i only get the case that I care about so to take this example even further if we're talking about say the boat that we're tracing is actually last dessert that you hit but when we render the hearts so if I jump down into our render loop where we make that determination in between broken and fall hearts I could set a breakpoint here and because I've already hit the other conditional breakpoint when I run it'll go through to the rendering step and that's fine but if I want to check this condition you know multiple times I want to cycle through this in case the the bug doesn't repro every time this isn't a great thing to do to leave this unconditional breakpoint here because they have the same problem every frame I'm going back through what I can do to get around this is right-click on the breakpoint again open up the more menu and go to this section here where we can disable the breakpoint until the first one that we had was hit so now we end up with that the game run through now we end up with I'm not stopping every time because the breakpoint is disabled and then when I hit the last desert our first breakpoint catches and because we've hit the first breakpoint it's activated the other dependent breakpoint and when I run through we land there in the render loop so what you might have noticed during all these things is when we right-click on the let's keep waiting when we right-click on these breakpoints and start working with them you'll notice this suspend tab here and so far we've had all selected for all of this and that means that when the currently executing thread encounters the breakpoint we will just stop every thread in the application and that that pauses the whole game State and that can be useful for the bug like we were talking before with rendering the hearts but if you're working in a multi-threaded application and you're chasing down some really tricky synchronization issue you might also want to try suspending just the thread that encountered the breakpoint so to highlight this functionality we have a section of the game that's responsible for when Android dies simulating sending off the score to some server somewhere in our case we just delay and log some stuff but if I set a breakpoint here in the background thread what we're doing our network call right-click it and tell it to de spent to suspend on just the thread then what I can do is play through my game which is over again play through my game and you'll notice that because I died before I'm already in the background thread but the UI thread is still active which means I can cycle through the whole game again and get myself in a state where say I have you know multiple Network calls queued and I can try to reproduce some kind of tricky synchronization issue there so for the next the next tip I want to show you the rest of the breakpoints that we have set aren't particularly useful so I'm going to go ahead and try to disable them here so what I can do to do that right is right click and then deselect enabled and that's fine but what might be handier is to use alt-click which will just toggle these guys on and off or option-click if you're on Mac so all that being said I'll go to my last tip which isn't that line it's this one so this is the part of the code where we determine the score score multiplier which is this this guy up here that determines you know how much your points are multiplied by when you hit a 10 let's say I need to track this value over you know all sorts of times in the app like like very very many times I can set a break point here and I can run through my app we have the same problem I hit this guy all the time when the game's running I hit this guy all the time and then I have to chase down the information I'm looking for through this whole menu which can be tedious what I really want to do is is log the the multiplier value here but I don't want to drop logs all over like log statements all over my code it's kind of a pain um so what we can do instead is leave our break point and disable suspension altogether so now whenever we hit this break point we're not gonna stop the app then I can go over here to this evaluate and log section and I can type in any Kotlin expression so in this case my multiplier is you know scored multiplier and now when when the thread encounters this breakpoint it doesn't stop it just evaluates this expression logged it to the console and moves on if you don't need that much information you just need to check to see if we go through a certain code path you can deselect this and instead choose to have a breakpoint hit message and I'll just tell you that you you hit this breakpoint at this time and in fact that's so handy that there's a shortcut for it if you shift click in the gutter it'll create one of these for you so those are all the tips that I wanted to share I'm gonna invite David back over to tell you about some of the ones that he knows all right have you ever found yourself working on a bug where you're not making progress so you tried to put it aside for a bit and then work on a second bug but while you're working on that bug you keep hitting the break points from the first bug you're not ready to delete those break points yet so you just keep disabling one at a time well it turns out there's a better way using break point groups so I'm gonna go here let's imagine that this was a break point we just hit right-click on the break point go to more and this now you see all the break points up here you can multi-select them right-click create a new group for them you can name this after the bug that you're working on I'm just gonna call this just in now that they're grouped now that they're grouped you can enable them toggle them on and off for the single click and when you're done with the buggy could also just bleed all of them as well all right I'm gonna jump back into the code now for some context this is the function responsible for initializing the current item so I'm gonna let this run a couple times you can see it so when the item is off the top of the screen we don't yet know if it's going to be a random dessert or a number 10 now let's say here I want to step into this generate random type method but I accidentally step over it now I may have stepped over on purpose also but the maybe the result was a little bit confusing to me I didn't expect it so what I wish I could do in retrospect was step into it well if you're targeting a device that's running at least Android 10 you can click on this drop frame button and what that's gonna do is it's gonna pull you out of the current method and put you right back to before it started so when you step into it again you get your second chance yeah now this feature is not a time machine so if I was in the middle of a long function and it had done a lot of intermediate work for example modified the state of this current class that doesn't get undone when you drop frame this is not a deal breaker it's just something to be aware of and despite that this feature has saved me a ton of times all right now I'd like to bring your focus down here to the variables window so this is the item class and this particular instance of an item is ID one zero two three one so as you can see on the screen there's already a couple of items there but let's say I just want to follow the life cycle of just this particular item now what I might have done in the past is write down one zero two three one on a piece of paper but that's a little error-prone instead just right-click on it mark the object it asks you for a label I'm going to creatively call it my item and immediately you'll notice that anywhere that this appears in any of these debugging windows it's labeled now my favorite part of this is if I come over here to a random method oh by the way did you know you can click on the line gutter and you'll run to that directly so now I'm in the render method and my item instance of course is not in scope what I could do is come over to the watches window and I can type my label my item followed by underscore debug label we don't have to worry about remembering it because it autocompletes it for you and anywhere in my app I can actually follow my single instance to see what it's state is doing this is actually very powerful when combined with conditional breakpoints which we talked about earlier so this line here is responsible for rendering all of these items so what I'm going to do is I'm going to set a breakpoint on the line I'm gonna right-click on it and I'm gonna set my condition to item equals my item debug label and when I run there sure enough you can see that I am actually highlighting my particular item and I didn't have to step over the breakpoints of the other instances now as long as we're here and we're at a breakpoint what you can what you can do is you can click on this evaluate expression button to bring up the evaluate expression dialog it sometimes opens up here in single line mode so I like to expand it to multiple lines like the Watchers window you can type in any expression here so let's just do item dot image and then hit the evaluate button and you can browse the object in this object browser below but you can do richer expressions in here as well like you can create variables you can use if statements and let me just go ahead and verify that this is working just by putting in a random expression and there you go so sometimes the variable and watch windows are great for when you're trying to watch values and value change over time as you step through the code but the evaluate expression dialog is really great if you just want a live inspection shell that you can see the current state of your app and maybe you're not really sure what you want to debug but you can you dive in by doing these evaluations all right I'm gonna jump back to some code that to a break point that Justin said earlier remember this is the code responsible for colliding with a desert and there's a condition on it as you can see it's player dat health equals one I'm gonna enable it now this is gonna be a little bit hard to see in this case but I'm gonna try running into some deserts and see if you can notice there's a very slight stutter even when the breakpoint doesn't hit let me go ahead and it's a little bit hard to see but now here on the third one so even in this case if you couldn't quite see it whenever you have a conditional breakpoint even if you don't stop the debugger still has to do the work to evaluate that expression and if this was in the middle of a really tight loop this might become really noticeable unfortunately there's not really much you do in that case if this is actually really affecting your debugging experience so what you just have to do is you know type the expression out explicitly I'm just gonna put an O up here so I could hook my breakpoint on it and at this point I've ever done the code version of what the conditional breakpoint was here which I sometimes have to do for performance this is a point where you might decide to restart your app and read debug it or we hit the debug button but instead let's go ahead and use apply changes which is available and all Android devices at least running 8.

0 now what this is going to do is this is going to apply my code patch over and I want to draw your attention here to the frames window because it now says obsolete so what's happened is I used to in this old version of the update method and we've pushed over this new version with my break with my new code in it but that being said the debugger is still stuck in the old one so what I'm gonna do is I'm gonna use the drop frame feature we talked before I'm gonna go out of the old and step back into the new so here I verified that in fact the code has been patched because I hit the breakpoint I also want to call everyone's attention to this other button I'm here so both of these buttons are apply changes this first one is responsible for patching just code this second one does the same thing but it also restarts the activity afterwards this could be really useful if you modified some layout resources or if the code that you're trying to debug is for example in a non creative method all right and finally despite all of these tips and tricks you are going to still write some bugs you're still going to publish them and you're still gonna get bug reports so if you ever get a bug report that has a call stack in it here it just looks like a bunch of text just copy it come back over here to Android studio click on analyze it's really tiny but analyze analyze stack trace and you'll see that it already found the value that was in the clipboard all I have to do is hit ok it's gonna drop my call stack into the console below fully annotated so very quickly I can see that this is my codebase versus this is code that I might not have to page in Jinju and of course you can click on the links to quickly jump through your codebase alright that concludes the demo right so we talked about a lot of different tips today and there were as we mentioned before there was still more that we couldn't fit into this talk so if you're watching recording on this video please check the description and we'll try to include some helpful links there if you are here in person come talk to us in the sandbox and we'd love to expound on some of the expand on some of these more if we forgot your favorite debugging tip please tell us leave it in a comment tell us in person because we'd love to know what it is to incorporate it in our own workflow with that thank you all so much [Applause] [Music] you [Music].

Hãy bình luận đầu tiên

Để lại một phản hồi

Thư điện tử của bạn sẽ không được hiện thị công khai.