As developers, we are sometimes trapped by problems that make us spend more time in the debugger than we spend writing code. That’s why we recently took the opportunity to learn about some of the techniques used by the Android studio team to speed up debugging. Next, we’ll show you some tips that we think are the best, save your time, and easy to integrate with your debugging process.
Although your application may be quite different from the hypothetical sample application in this article, the tips in this article can be used to develop any application.
Filtering and folding of log
Let’s start with the classic debugging method, a little trick of printf statement. If there is a game, it will print its frame number and user’s final score in the log, then the game will give the following content in the logcat window:
The output here may contain many things that you don’t care about, such as dates and thread IDs. In fact, you can easily adjust the display of the content. Just click the settings Icon in the logcat toolbar to pop up aConfigure Logcat HeaderWindow, where you can cancel messages you don’t want to see:
In this way, you can get more concise and relevant log output, as follows:
However, there is still a lot of confusion that prevents us from focusing on the high score message. You can use the search function to solve this problem as long as thesearchInput a part of debugging information in theLogcatWindow to filter:
You will often use the search criteria, you can use theEdit Filter ConfigurationAdd to custom filter:
Next, add the details of the filter:
Another way to reduce chaotic logs is to use the collapse function, which can collapse similar logs into the same group. You just need to select part of the text in a log, right-click, and selectFold Lines Like This :
WhenConsoleWhen the dialog box appears, clickOKTo organize the approximate logs containing the selected text together:
If you need to see the collapsed information later, you can click on a line to expand the content. There are also buttons that allow you to expand or collapse collapsed rows.
Attach debugger to current process
Although we can passDebugButton or menu option to start a debugging session, but when you want to debug an application that has already started, you can attach a debugger to it so that you don’t have to restart the application. You can clickAttach Debugger to Android ProcessButton to do this:
InChoose ProcessIn the pop-up window, select the process you want to attach a debugger to and clickOK。 Next, as in a normal debugging session, the debugger starts to trigger your breakpoints.
If you find that the location of the breakpoint is not appropriate, in addition to clearing and resetting the breakpoint, you can also drag the current breakpoint to where you want it. This is useful because the move operation retains the breakpoint setting, which includes many of the features that will be covered in the rest of this article.
You may need to find bugs in your app or game that are related to specific types of events. For example, in a game under development, you may want to stop running when a player’s character runs out of health when it collides with an object. You add breakpoints to collision events, but each collision causes the run to stop. To avoid this, you can use conditional breakpoints.
To set a conditional breakpoint, you need to right-click a breakpoint and add a condition to it. The condition here can be any code expression that results in a Boolean. When the code runs to this line, if the expression execution result is true, the breakpoint is activated.
Here, based on the logic of the player colliding with an object, set a player.health ==1, so that you can capture the last object collision event before the player’s health value drops to 0.
A piece of code will be triggered by different paths, which is not uncommon in application development. If you find a bug that can only be triggered on a specific path, arbitrarily making a breakpoint for it will cause many meaningless running interrupts. To cope with this situation, you can use dependency breakpoints. Dependent breakpoints are activated only after a specific breakpoint has been triggered. For example, you can create a breakpoint that will only be triggered in the path you are interested in, and other breakpoints can rely on this breakpoint, so that the breakpoints will only be triggered in the path you are interested in.
To set a dependent breakpoint, you need to right-click the second breakpoint in the path and open itMoreMenu. InDisable until breakpoint is hitIn the check box, select the breakpoint you want to rely on:
You will notice that the breakpoint icon has changed:
Now, your application will only stop running at the previous breakpoint after it has been triggered.
This function can also be used in other places where conditional breakpoints are used, so as to avoid copying and pasting conditional breakpoints to new locations.
If you are debugging a multithreaded application, you will notice that breakpoints suspend all threads by default, but sometimes you may not want it to. For example, you may want to verify that other functions of the application work properly when a background thread is blocked, or you want to know whether the UI can continuously render while performing a background task.
To suspend only the current thread, you need to turn on the breakpoint option and selectSuspendIn settingsThreadOptions:
Evaluate and log
Sometimes, you may prefer to see some information about the state of the application than to stop at a breakpoint. Maybe you can do this by adding println statements to your code, but this method requires recompiling the application, and you can actually use the breakpoint itself to evaluate and record.
To do this, you need to disable it in the breakpoint optionSuspendAnd enabledEvaluate and log :
Now you can add any code expression to the input box, and the content will be evaluated and recorded to the console.
If you just want to quickly verify that breakpoints are triggered and don’t care about the details, you can use the“Breakpoint hit” informationTo record the trigger event of a breakpoint. You can even use shift + to add breakpoints to make this faster.
To disable breakpoints (not delete breakpoints), you can right-click breakpoints and deselect the enabled box from the pop-up box. You can also disable breakpoints more quickly by holding down ALT (option on MAC) and clicking breakpoints.
You must have come across this scenario: you are solving a bug and adding several breakpoints to it, but you find that you have no clue for a while, so you go to solve other bugs. Soon, however, you begin to trigger the breakpoints added to resolve the first bug. Triggering irrelevant breakpoints is not only annoying, but also takes you away from your debugging process.
You can use breakpoint grouping to make the development process more comfortable.
When your program runs to the first breakpoint independent of the current debugging process, right-click and open.MoreMenu, you will see a list of all breakpoints, where you can check all breakpoints related to the first bug:
Right click the selected breakpoint and selectMove to groupNextCreate newAnd name the new group, such as the bug you are working on. Now you can easily enable and disable all breakpoints with just one click.
Of course, you can also use the grouping function to remove all relevant breakpoints after the bug has been resolved.
Drop frame (discards the current frame)
Sometimes, when you browse pending code, you may accidentally skip a method that should have been entered. If your device is running Android 10 or later, you can clickDrop FrameButton to trace back:
This feature takes you back from the current method to the node before it started execution, giving you a chance to reenter the method.
This feature is not a “time machine.”. If you’re in the middle of a long function that has done a lot of work before (for example, modifying the state of the current class). When you discard the current frame, changes made by such operations are not reversed.
Sometimes you want to track the life cycle of certain types of instances. In this case, the object to be tracked has a hash value: @ 10140:
In order to recognize the object when it reappears, you may have taken out a pen and paper to write down the number. However, you can choose another way: right-click the object, clickMark ObjectLabel it.
This way, no matter where the marked object appears in the debug window, it will carry the tag you added for easy identification. Here we add an“myItem“Label:
Even better, even if you are in a completely different context and can’t touch the object, you canWatchesWindow to view it. No matter where you are, as long as you trigger the breakpoint, you canWatchesAdd the suffix to the window as“_DebugLabel“Tag (don’t worry that you won’t remember the content of the suffix, there is automatic completion here):”
Now, you can use it anywhereWatchesWindow to observe the state of the object of that type.
You can also combine this feature with conditional breakpoints. For example, you can make a breakpoint, right-click and set a condition for it to check for tagged objects:
In this way, instead of skipping a bunch of breakpoints before entering the scope that contains a specific instance, the code will run to the appropriate place and then stop:
AlthoughVariablesAndWatchesWindows are great for tracking an explicit value, but sometimes you want to explore your code more freely, and it’s time to evaluate the expression function. When you are at a breakpoint, you can use theEvaluate expressionButton to access this function.
You can use theExpressionEnter any expression in the input box and clickEvaluateButton to evaluate it. Of course, if you evaluate an object, after the evaluation is complete, you canResultSection browse the details of the object:
The evaluation expression pop-up window may open in a single line mode, which you can clickExpandTo extend it to multiline mode:
Now, you can enter complex multiline expressions that can contain variables, if statements, and so on
As mentioned earlier, when you use conditional breakpoints, you need to evaluate an expression; even if the code does not stop at the breakpoint, the debugger still needs to perform the evaluation. If you run evaluation operations in a very tight loop, such as animation processing in a game, you can cause the application to pause. Although conditional breakpoints are useful, in some cases you may not be able to rely on them.
One way to solve this problem is to add conditional expressions to the code and use no OP expressions so that they can attach breakpoints:
After modifying the code, you may decide to restart the application and clickDebugButton, but if your app is running on Android 8 or later, you can use theApply Code Changes ：
Now that the embedded expression has patched your code, you will still be in theFramesIn the window, you can see that the method you have updated is marked asObsolete :
This is because although the new code has been patched, the debugger is still pointing to the old code. You can use the function of discarding the current frame to leave the old method and enter the modified method.
Although not required in this case, there is an alternative:Apply Changes and Restart Activity。 Different fromApply Code Changes, which will restart the activity. This option is useful if you modify layout resources or code that you want to debug, such as in the oncreate method.
Analyzing stack information
Even if you know all the tricks and tricks, you may still have bugs in your code, and you will receive some crash reports that may contain text copies of the exception stack information. You can use theAnalyzeIn the menuAnalyze Stack Trace or Thread DumpTurn that information into meaningful content.
This tool provides a text box for pasting stack information, but it also automatically fills in the text in the system clipboard:
ClickOKAfter that, stack information with full comments is added to the console:
You can see at a glance the content from your own code file (highlighted in blue) and code that you may not need to pay attention to (highlighted in gray). And you can jump in your code file by clicking the link.
This article provides some tips and tricks to speed up debugging. Due to the limited space, more skills are summarized as follows:
- In debug mode, clicking on the number of lines of code can directly execute this line of code
- CTRL + drag to copy breakpoints
- You can set a breakpoint at the right parenthesis of a function
- You can set breakpoints on fields and properties called “field watchpoints”
- You can set breakpoints for methods in an interface so that all its implementations trigger breakpoints
You can learn more about this article’s details and demos in this video:
- Tencent video link:
- BiliBili video link: https://www.bilibili.com/video/av78114615/
Please also check out more resources related to this topic:
- Official document of Android Developer | debugging pre built apk
- Control the display of data in debugger through data browsing
- How to use and understand the overhead tab
- Official document of Android Developer | Android Studio – debugging your application
- IntelliJ idea debugging code