对于开发的同学来讲,Debug并不陌生,对于做Android开发的同学也是一样的。
可能有些同学会问,调试程序谁不会呢?的确,调试程序对现在的Android开发来说太常见了。我见过有些同学“熟练”的使用Log打印来看数据,不停的添加/删除Log,然后Run。这样有错吗?我想是没有的,但Android开发的同学都知道Run一次的时间是不短的。
也许很多同学会不削的说,我早就在使用Android Studio的Debug功能来调试程序了。的确,使用工具会为我们提供很多便利,也会为我们节省很多的时间。
但你真的了解Android Studio的调试工具的强大之所在吗?
你肯定遇到过,在断点的地方想查看其他变量的值,或者干脆想在断点的地方运算一段代码来了解当前条件下的结果。
你也肯定遇到过,想某一个断点只执行一次,然后自动的删除掉;或者干脆是某一个条件发生了再打开断点。
也许会想,在调试代码的时候有没有什么方法,当某一个异常抛出,或者被捕获的时候我能知道。
当然,你也有可能被多线程的调试困扰过,怎样才能只让目标线程执行断点而避开其他无关线程的干扰。
这些,Android Studio的Debugger已为你准备好。
为了更好的阅读本文,我们约定:
Debugger特指Android Studio Debugger
<10> 表示代码的第10行
[Step Over] 表示Step Over这个功能
"Step Into" 表示Step Into这个菜单选项
文中所有快捷键使用Android Studio Default的Keymaps
1一切从设置开始
有使用过Debugger经历的同学,对下图中<38>断点处的黄色字体不会陌生。细心的同学可能已经发现,<38>与<39>的debug信息与<37>的信息是不一样的,或者说与你平时看到的debug信息是不一样的。
在<38>的debug信息中,我们看到了自定义的信息: #LeiGuoting# id:4, createAt:0
这个自定义信息有时可以给我们带来很多方便,它可以很直观的给我们呈现出想要的信息,而不需要多余地操作。
那么这个自定义信息我们怎么做呢? 我们先把这个问题命名为: 问题1.1
在这个世界上,什么都不是单一存在的,有阴就有阳,有debug信息显示就有不显示的时候。
那么,怎么去关闭这个debug信息? 我们把这个问题命名为: 问题1.2

如果你对debugger的使用比较熟悉。 一定使用过F8执行[Step Over],一步一步的调试;
你也知道F7执行[Step Into]进入到一个方法继续调试。
在使用[Step Into]时,你肯定发现了,有时候按F7是“无效”的。
还有些方法,你是不用进入的,但你并不确定是否为这些方法,于是你还是F7了,进入该方法后,你发现你浪费了时间。
那么,上面的问题我们怎么去解决呢? 不急,暂时将这个问题命名为: 问题1.3
使用Android Studio的同学对于设置菜单是很熟悉的,但也许你忽视了下图中Debugger选项:

在这篇文章不会详细介绍设置。
Debugger分为三个子选项,"Data View", "Stepping", "HotSwap"。
"Data View" 中 "Show values inline" 能解决我们的问题1.2
问题1.1
我们需要使用到"Java Data Type Renderers"
点击左上角的绿色+创建一个新的Renderer
"Apply renderer to objects of type"中选择你要的class
"When rendering a node"中的"Use following expression"选择上面类的一个方法
问题1.3
在"Stepping"中,Debugger为我们做了一些默认的Skip设置,所以有些类我们F7是“无效”的。
如果有些类我们不想"Step Into",也可以在"Stepping"中添加。
Data View 负责Debugger显示相关的配置
Java
Java Data Type Renderers 自定义显示数据的渲染器,就像问题1.1中所看到的,我们可以自己定义需要显示的内容。
Stepping 对于Java代码,这项设置主要设置[Step Into]的功能,什么时候该跳过,什么时候该进入。
HotSwap Class Reload的一些策略。
2断点(Breakpoint)
断点,不只一种。
Debugger为我们提供了5种断点类型,但我们使用的最多的是行断点(Line breakpoint)
Line breakpoint:当我们点击方法内一行代码左边的gutter时,我们设置的断点就是一个Line breakpoint,也是我们使用的最多的一种断点。
Temporary Line breakpoint:当我们设置一个断点为Line breakpoint时,将该断点的属性"Remove once hit"勾选,我们就设置了一个临时行断点。Line breakpoint如果不主动删除,这个断点就会一直存在;而一个Temporary Line breakpoint是临时的,使用一次后该断点就自动被删除。
Method breakpoint:当我们点击一个方法名左边的gutter时,生成的就是一个方法断点。Method breakpoint有两种触发方式,一是进入方法的时候,二是退出方法的时候,两种可以同时存在。
Java Exception breakpoint:根据我目前的了解,异常断点只能通过[View Breakpoints]来打开断点管理器创建一个该类型的断点。 顾名思义,该断点是用来调试跟踪Java异常使用的,当其他条件都满足的情况下,如果我们勾选了"Catch exception"选项时,只有我们catch了某个类型的异常时触发该断点。 如果我们勾选了"Uncatch exception"选项时,只有我们没有catch了某个类型的异常时触发该断点。
Field watchpoint:这是一个可以随时观察变量的断点。当我们点击一个共享变量的左边gutter时,我们就会设置一个该类型的断点。只要这个变量有变化都会触发该断点。
在Android Studio中,5种断点都有不同的小图标对应来标识具体的类型。并且,断点的不同状态,也是有不同的小图标来标识的。这样我们就很容易记住他们。

(断点的类型图标和状态图标,图片来源:https://www.jetbrains.com))
上图中我们可以很清晰的看到不同断点对应的图标,还有他们各自不同状态时对应的图标。
在实际使用的时候,我们就可以很容易,也很清晰的分辨出断点的类型及状态,方便我们的使用。
断点的前4状态我们是很容易理解的,最后一种Conditionally disabled我们在后面会介绍。
如果说断点仅仅只有这5种状态,那么它的功能是很有限的,但现实是Debugger为我们提供了很强大的功能。
它有Actions,也有Filter。这两种的组合可以为我们提供很强大的功能了。
[View Breakpoints],可以打开如下图的一个菜单:

我们在断点红色小圆点上右键,我们会看到一个弹出框,上面有简单的enable,Suspend的策略选项。如果我们继续点击"More"也会看到上面的图。
Enabled:开启/关闭断点
Suspend:如果这个checkbox未勾选,程序执行到这一行时不会暂停
Condition:条件,如果该条件为true时才会暂停该断点,如果条件不满足时是不会暂停的
Log message to console:开启将log打印到Debugger的console
Log evaluated expression:我们可以使用这个选项填写自己想要输出的信息到console,这样我们可以在代码运行的时候通过该选项动态的修改输出的信息。
Disabled until selected breakpoint hit:断点与断点之间的关联,我们可以在下拉列表中选中一个断点A,当这个A断点被触发的时,该断点执行"Disable again"或者"Leave enable"
Filters:
Instance filters 根据给定的对象ID来过滤
Class filters 根据class条件来过滤
Pass count 跳过的次数;比如,这个值为2,那么该断点会跳过2次后才会触发
3调试(Stepping)
Step Over:执行下一行语句(下一行语句不需要打断点)
Step Into:当前Suspended的语句是一个方法(非Skip方法),进入到该方法内(该方法内不需要断点)
Force Step Into:当前Suspended的语句是一个方法(包括Skip的方法),强行进入到该方法内(该方法内不需要断点)
Step Out:退出当前方法
Run to Cursor:跳转到光标所在的行
Force Run to Cursor:强行跳转到光标所在的行。如果当前断点与所在光标之间还有其他断点,那么我们执行[Run to Cursor]时是不会跳转到光标所在行的,而是到下一个断点处;而如果执行的是该命令,则会直接跳转到光标所在行。
下图中右上角一行的几个蓝色和红色箭头分别对应着这几个Step,当然在这个图中是没有"Force Run to Cursor"。在我们Android Studio菜单"Run"里面包含了所以的Step。

4Debugger窗口
当我们启动一个Debugger Session后,会看到下面的一个窗口。

1)Debugger debugger的主窗口。在设置一节中我们讲到了Data View设置,它所对应的部分显示效果就在这个窗口中提现出来。
Frames:所有Frame的列表,Frame体现一个线程的堆栈信息。
Threads:所有线程列表,其中我们可以看见线程的状态。并且在该列表中线程只有三种状态,RUNNING, WAIT, UNKNOWN
Variables:变量池, 显示当前断点对应的上下文的变量。在这个窗口中,我们可以选中某个变量,右键我们可以修改该变量的值。还可以[Inspect]打开变量的监视窗口,这样就方便我们对变量的观察了。
Watches:我们可以通过Watches窗口的绿色+号来创建一个需要观察的变量。 这样我们就能很直观的在这个窗口看到该变量的变化,而无需去跟踪代码。
2)Console 我们在讲断点属性的时候知道,我们可以开启或者关闭某一个断点的log,还可以自定义需要输出到控制台的log信息,方便信息的输出,而不必去修改代码。
3)Favorites 在该窗口中我们可以看到所有的断点。
在这一个章节中,我们介绍了Debugger的相关概念以及说明。在下一个章节《被忽视的利器--Android Studio Debugger(下)》中,我们会用实际例子来看看Debugger到底可以做些什么,我们也可以看到什么是Conditionally disabled。

本文作者:雷国廷(点融黑帮),现任职点融Mobile团队,Android开发工程师。热爱生活,也热爱代码。
随着点融网新一轮融资,点融网即将开始大规模的扩张,需要各种优秀人才的加入,如果你觉得自己够优秀,欢迎加入我们!获取更多职位信息,请关注点融黑帮。


