逮虾户!Android程序调试竟简单如斯

17,346 阅读14分钟

PS:行吧,不用百度了,逮虾户是《头文字D》的一首配乐《Deja vu》,中文谐音 “逮虾户”,飙车漂移专用BGM,有时音乐响起也暗示着:开车。

当然本节讨论的不是开车,而是Android开发中老生常谈的程序调试。

一个开发仔的日常离不开:写BUG和解BUG,特别是多人协作的时候, 帮别人擦屁股(解Bug)的情况屡见不鲜。另外,接盘别人的项目,着手解 Bug也能帮你快速的上手项目。综上,修炼好「调试」这门技能显得尤为重要。 但是,但是感觉很多玩家还停留下无脑打印的阶段,所以有了这篇文章。

笔者尽量以最精简的方式来过一过Android调试中的(qi)(ji)(yin)(qiao)。

逮虾户~


1.无脑静态调试

解释下这个标题:

  • 无脑觉得哪里有问题就打印哪里
  • 静态:每次想打印,都要去修改代码,然后重新运行程序。

适用于:想查看变量的值在某一时刻是否异常!

接着来说下两种常用的调试方法:

Toast打印法

新手Android开发仔最爱调试法,使用简便,仅需一行代码,轻松打印:

Toast.makeText(MainActivity.this, "Toast调试", Toast.LENGTH_SHORT).show();

方便是挺方便的,不过有一点要注意:Android 5.0后,如果把「消息通知的权限」 关闭掉的话,部分机型是不会显示Toast的!你还可以使用Snackbar来显示值进行调试:

Snackbar.make(父view, "Snackbar调试", Snackbar.LENGTH_SHORT).show();

或者其他变通的方法,比如在页面上添加一个TextView,把值直接显示在文本框上。

Log日志打印法

Toast调试是挺爽的,但是有两个问题:

  • 1.想调试打印多个值的话,Toast会弹个不停,毕竟同一时刻只有「一个」Toast显示在前台;
  • 2.Toast间隔一段时间后会消失,即使你设置了Toast.LENGTH_LONG

可能你一走神,没来得及看调试的值,Toast就消失了。我们着实需要一种无需担心调试 结果消失的方法——「Log日志打印法」,就是利用Android系统提供的Log类,在调试 的地方,把日志打印到「Logcat控制台」上,使用方法也非常简单:

Log.d("TAG", "Log调试")

当代码执行到这一句的时候,就会在Logcat控制台打印调试信息,另外Logcat默认 会打印出所有的日志信息,我们可以做一些过滤来定位到我们调试的日志信息。 首先是:「日志类型」,Android支持6种日志类型,依次如下:

  • Verbose:详细,所有类型的日志信息。
  • Debug:调试,调试用的日志信息。
  • Info:信息,正常使用时需要关注的日志信息。
  • Warn:警告,可能有问题,但没发生错误的日志信息。
  • Error:错误,运行时出现严重错误的日志信息。
  • Assert:断言。

温馨提示

不要上来就Error级别,我以前打Log全部用Log.e,原因是日志信息是红色的,好看... 结果被组长屌了一顿,o(TωT)o ,觉得颜色不好看,你可以按照下述的操作进行自定义。

打开「Settings」-> 「Editor」->「Colors Scheme」->「Android Logcat」 选择日志类型,然后去掉勾选,然后点击选择颜色色值

这是笔者配色方案,读者可以自行调整为喜欢的颜色:

  • Assert:8F0005
  • Debug:0070BB
  • Error:FF0006
  • Info:48BB31
  • Verbose:BBBBBB
  • Warning:BBBB23

设置后的配色如图所示:

行吧,知道可以通过Log类打印调试和自定义Logcat配色,顺带也提提「日志过滤」的姿势吧。

  • 自定义Logcat日志头信息的显示内容:点击面板上的「Logcat Header」来设置日志头信息

可选设置内容如下:

右侧还可以「过滤日志信息」,支持正则,再右面是过滤特定日志的选项。

如果觉得还不够的话,可以点击最右侧的「Edit Filter Configuration」来配置一个自己的过滤器。

另外,还可以对进行「日志搜索」,鼠标点击Logcat中间区域获得焦点,Ctrl + F 调出 搜索工具栏,接着搜索相关的日志内容。

顺带提下Log类的一个容易忽略的小坑:

Log类只会打印4000个字符,超过部分不打印!!!

2.有脑动态调试法

其实就是用Android Studio提供的Debug模式来程序调试,相比起前面的 Toast打印法和Log日志打印法,稍微复杂一点,要点学习成本,还有动脑子, 最重要是可以动态调试,很多新手玩家貌似对这个都望而却步,其实不难,待我 带你九浅一深,em...由浅入深走一遭,来学学Android Studio Debug核心技巧。

1)基本的调试流程

一般的调试流程图如下所示:(核心就是下断点,单步调试,值跟踪

2)下断点

先说说断点,不是张敬轩的《断点》,调试时的断点的作用是:

当程序执行到断点所在的代码时,会暂停应用程序的运行,线程被挂起,然后 可以通过调试器进行跟踪。

下断点的方式也很简单,点击某行代码的左侧,会出现如图所示的小红点。

这个小红点就是断点,而在AS中,又有着多种类型的断点,带你们过一遍吧:

  • 行断点

就是上面这种,对特定行进行调试时用到,点击行所在的左侧边栏即可设置。 右键点击这个断点,会弹出如下所示的设置对话框:

如果你取消了Enabled勾选,断点就处于如图所示的禁用状态:

  • 方法断点

如果你把断点下到一个方法前,断点就会变成这样:

这个就是方法断点,一般用来检查方法的「输入参数」与「返回值」。

  • 变量断点

有时我们对程序运行过程并不关心,而只关注某个变量的变化,可以在变量定义前加一个断点。

在程序运行过程中,如果该变量的值发生改变,程序会自动停下来,并定位到变量值改变的地方,供开发者调试。

另外,右键还可以设置断点,Watch面板有两个特有的选项,可按需勾选:

  • Field access:字段被访问时触发断点。
  • Field modification:字段被修改时触发断点。
  • 条件断点(断点设置Condition)

有时会有这样的场景:把断点打到循环体的中,我们只关心特定循环次数下的运行情况。 比如一个循环10次的循环体,我们想知道循环到第8次时的运行情况,如果你不知道 条件断点的话,你需要一直按「Run to Cursor」直到满足我们的条件。比如下面的代码:

我们想知道当i = 8的时候,sum为多少,你需要一直按「Run to Cursor」

按7次,直到i = 9位置,如果用条件断点,当循环体执行到某个条件才停下来,右键断点,输入如图所示的等式条件:

然后可以发现,程序直接跳到i=8的时候才挂起,非常方便。

  • 日志断点

调试的时候我们可以通过打印日志的方式来定位异常代码大概位置,以缩小引发问题的范围,然后 再使用断点精确定位问题。如果是普通的打印日志,我们需要等待重新构建程序,如果用「日志断点」 就避免这个无意义的等待。使用日志断点非常简单,右键断点,去掉「Suspend」的勾选,会出现 如下所示的弹窗,勾选「Evaluate and log」在此输入想输出的内容。

运行调试后,当执行到日志断点的时候可以看到控制台输出了对应的日志信息,而且程序正常运行,并不会挂起。

如果想查看更详细的信息,比如断点的位置和触发时的堆栈信息,可以勾选「"Breakpint hit" message」和「Stacktrace」,勾选后输出内容会变得更详细:

  • 临时断点

所谓的临时断点就是:触发一次后就自动删除的断点。设置的方法有两种:

  • 1.光标移到想打点的行,点击菜单栏「Run」->「Toggle Temporary Line Breakpoint」, 等价于快捷键:「Ctrl+Alt+Shift+F8
  • 2.更便捷的操作:按住Alt,鼠标点击左侧边栏。

鼠标点击后可以去掉临时断点,如果想把临时断点变成普通断点,可以取消勾选 「Remove once hit」的选项。

  • 异常断点

用于监听程序异常,一旦程序奔溃,直接定位到异常所在的确切位置。依次点击: 「Run」->「View Breakpoints」打开断点视图。点击「+」,然后选择 「Java Exception Breakpoints」,在弹出的窗口中输入要调试的异常:

除了设置异常断点外,你在这里看到项目设置的所有断点,并进行断点管理与配置。 另外,你还可以设置自定义异常断点,点击「4.Exception Breakpoints」自行配置即可。


3)进入调试模式的另一种方式

大部分的同学调试都是通过点击下面这个只小虫子进入调试模式。

这种方法有个缺点就是:每次都需要重新运行程序,可能有这样的场景:把APP丢给测试 测试,然后出现了一个很稀有的BUG,此时你如果用普通的Debug模式,需要重启APP,但是 Bug不一定能够复现,这就很尴尬了。对于需要动态调试的场景,可以「直接调试正在运行的 Android进程」,点击如下所示的另一个有小虫子的图标:

然后选择要调试的包名,就可以无需重启应用直接进行调试了:


4)调试工具详解

这里把调试工具划分为如图所示的五个区域一个个讲解:

  • A区步进调试工具
图标名称功能描述
显示执行点定位到当前正在调试的断点。
单步跳过一步一步执行,遇到方法直接执行完方法,进入下一步,不会进入方法内部。
单步进入一步一步执行,遇到方法且是自定义的方法,则进入方法内部,否则不会进入。
强制进入遇到方法无论是自定义的还是官方类库的,都会进入方法内部。
单步跳出跳出当前进入的方法,返回方法调用处的下一行(也意味着方法被执行完毕)。
丢弃帧如果你在某个方法内,执行完丢弃帧,当前方法会被中断,返回方法被调用的
地方,变量的值也会重置。
执行到光标处可以看做是临时断点,程序运行到当前光标所在行暂停。
计算表达式支持在调试过程中,通过赋值或表达式方式修改任意变量的值。

!!!注意:上面的执行到光标处是有个前提的:中途没有断点,如果你想强制执行到 光标处的话,你需要「Force Run to Cursor」,你可以通过下述两种方式进行操作:

  • 1.光标处右键,选中「Force Run to Cursor」,如图:

  • 2.使用快捷键:Ctrl + Alt + F9。

  计算表达式(Evaluate Expression)

非常实用,可以动态地查看和修改任意变量得值,除了上面在调试窗口点击计算器图标可以打开外, 你可以在D区点击某个变量右键打开。演示下修改某个变量得值:

修改完后,点击Evaluate,结果如下:

你也可以在这里直接查看变量的值,示例如下:

同样点击Evaluate,结果如下:

  • B区控制调试工具
图标名称功能描述
继续程序运行程序运行到某个断点暂停,如果有下一个断点,点击后跳到这个断点,
没有的话,程序则继续运行。
暂停程序运行暂停程序运行。
终止程序运行终止程序运行。
查看断点可以查看所有的断点,管理与断点配置
禁用所有断点切换所有断点的状态(启用/禁用),禁用后,程序就不会触发断点。
获得线程堆栈显示线程的相关信息
恢复布局恢复到原始布局
设置调试的相关配置,比如是否显示执行方法后的返回值
  • C区帧调试窗口

这里的帧指的是:堆栈帧,一种用于「存储数据」和「部分过程结果」的数据结构; 每次调用方法在堆栈中都会占用一部分内存,单位是,随着方法调用而创建。 每个堆栈帧中包括了:「传入参数」、「返回地址」、「局部变量」以及「对程序调试提供支持的信息」。一个线程包括多个堆栈帧。

查看工具那里可以以上下顺序切换的方方式查看帧(鼠标直接点击也可以),最右侧的漏斗图标 点击后可以过滤掉:「非本应用的帧」。另外,你还可以右键堆栈帧,添加「步进过滤器」、 「导出线程」或「自定义线程显示」。

  • D区变量区

在这个区域可以看到堆栈帧中当前所有的数据(方法参数、本地变量、实例变量)。 右键某个变量,可以进行「设置\复制变量值」,「跳转到变量在代码中的位置」等操作。 一个常用的操作:选择**「Add to Watches」**,单独监视这个变量的变化。

  • E区监视窗口

除了前面这种右键添加监视的方法外,你还可以直接在E区点击「+」进行添加。

不止监控变量,还支持监控表达式,比如上面的i+1。 关于Android Debug调试的基本操作大概就这些,多去实战中历练巩固吧!


3.Android Wifi 无线调试

其实还是要先电脑这个也非常简单,安装一个「ADB WIFI」的插件,安装后重启:

然后把APP的调试模式为「Usb Device

手机连接电脑,依次点击AS的菜单栏:「Tools」->「ADB USB TO WIFI 」,然后左下角会出现:

一般来说,要扫描好久,建议自己看下手机ip,然后通过adb命令进行链接,示例如下:

adb connect 10.1.7.200:5555

# 出现下述信息代表连接成功:
connected to 10.1.7.200:5555

连接成功后就可以拔掉数据线,进行无线调试了。


4.利用Network Profiler抓包

Android Profiler是AS 3.0后引入的性能分析工具,而Network Profiler是其中一个用于网络分析的工具, 利用它我们直接抓包,而不需借助第三方的抓包工具(比如Charles)进行分析调试。 比较简单,界面如下:

利用它可以进行很方便的抓包操作。


关于Android程序调试的东西肯定不止这些,上述的算是基本功吧。 有更多调试技巧欢迎在评论区留言~谢谢


参考文献:


PS:上一篇有童鞋在评论区留言说道在小专栏看到一样的文章,作者其实都是我, 专栏付费,一般来说是不能把付费的文章免费放出来的,不过感觉文章对很多 Android开发仔很有用,就抽出来放掘金上了。目前在写,内容较少,就不张扬了, Android入门向教程:新手可以系统学习Android,中手可以查缺补漏巩固基础。
(/ω\)当然如果你觉得本文对你很有帮助,也可以购买本文资瓷下小猪,嘿嘿~