Android调试技巧汇总
Android开发过程中,定位是解决疑难问题的第一步,Debug的能力往往反映一个开发人员解决问题的能力。有时候亲手手刃一只知名第三方的BUG往往能带来满满的快感。
日志调试
一、使用场景:
-
偶发现象,难以复现
a. 偶发的线上问题,没办法调试,基本上只能依赖崩溃日志。
-
偶发现象,难以复现
-
调试会影响程序执行逻辑
a. 时序敏感类问题,比如多线程、多进程问题。这类问题出现时,程序表现可能时好时坏,也可能一直有问题。但一旦我们进行断点调试就会发现一切又正常。这种BUG似乎有灵性,就是躲着你捣乱。
-
调试会影响程序执行逻辑
二、解决方案:
-
偶发现象,难以复现
a. 使用全局异常UncaughtExceptionHandler捕捉全局异常,捕捉到异常之后上传服务器(方便后台查看崩溃日志),同时打印异常日志到本地存储卡,方便在能拿到出问题的设备开发人员直接查看日志。[注:如果使用第三方的统计SDK可能会因对UncaugthExceptionHandler的多次设置导致覆盖问题。例如如果使用友盟统计SDK,友盟初始化时会设置UncaugthExceptionHandler,如果在此之后我们再去设置UncaugthExceptionHandler,就会导致友盟的异常捕捉机制失效。但如果我们再友盟SDK初始化前面设置UncaugthExceptionHandler,那么友盟会获取该Handler,并代理掉该Handler,就不会出现问题了] -
突出问题,暴露问题
a. 开发阶段,可以在出现异常的时候弹出异常通知,引起开发、测试人员的警觉
b. 一些偶发问题,如果怀疑多线程的时序问题,可以采取一些主动的方式来增加错误出现的频率。- 使用弱网(通过Fiddler、Charles等代理软件配置弱网代理,也可以手机切换成2G网络等)条件来测试网络请求过快或过慢造成的一些问题。
- 在可能存在问题的线程里,主动Sleep一段时间,来增加线程冲突的可能。
- 暴力测试,如果两个线程冲突的情况不明显,可以尝试开100个、1000个线程来增加冲突的可能。
-
突出重点,减少干扰
a. 没有必要的日志不要打印,用完随时清理临时日志代码
b. 打印日志加上限定条件,有的放矢,减少干扰
c. 通过特殊的字符串,比如一行很长很长的#作为日志的分割线,让有效日志容易被识别 -
统一管理
a. 对所有关键(异常)日志的打印,可以统一使用一个日志处理工具类,好处是方便集中管理,包括控制日志是否输出、统一输出日志的样式、上传日志到后台、输出日志到本地存储卡内等。
b. 不要让类似以下的代码随意出现在你“正式” 的代码里,但在开发调试过程是可以用这种随意的方式来快速验证问题的。//直接使用使用Android自带的工具类 Log.e("Tag", "Log Content"); //直接使用控制台输出 System.out.println("Log Content"); try { //do something }catch (Exception e){ //直接打印异常信息 e.printStackTrace(); }
断点调试
-
普通断点调试
- 注意断点是打在源码Java文件上还是字节码Class文件上,注意Java文件和Class文件是否一致。
- 注意运行时的代码和IDE中调试的代码是否一致
- 调试过程就是不断逼近有问题代码的过程。在这个过程中,注意随时增加新的断点,去掉已经排除了问题的断点。在一些调试准备环节比较麻烦的情况下,能节省很多时间。
-
条件断点调试
- 在多次执行的代码中打断点,但不确定什么时候会出现自己想要的情况,可以通过条件断点快速进入自己需要的代码运行状态
- 在循环里打断点,对于特定情况下才需要拦截,通过条件断点可以有效提升定位问题的速度。
- 在List的Adapter的适配器里面出现问题需要打断点,通过条件断点可以有效提升定位速度。
- 在多次执行的代码中打断点,但不确定什么时候会出现自己想要的情况,可以通过条件断点快速进入自己需要的代码运行状态
-
查看变量值
- 断点拦截到相应代码后,可以查看当前环境相关的各种变量、对象的属性值,
- 注意有些方法只能运行一次,比如对Stream类的读写操作
-
改变变量值
- 有的时候,我们可以通过在断点处修改变量的值,让我们很方便的走入我们需要的分支流程。相对于通过修改代码,然后编译,然后再进入我们需要的逻辑分支,修改变量值可以大大提高验证效率。
-
查看堆栈信息
-
在单步调试代码的时候,我们需要搞清楚现在代码是运行到了哪里,或者想搞清楚代码又是从哪里走到当前位置,我们可以通过AndroidStudio中的Debug窗口中的Frames窗口查看当前的堆栈情况。
-
在单步调试代码的时候,我们需要搞清楚现在代码是运行到了哪里,或者想搞清楚代码又是从哪里走到当前位置,我们可以通过AndroidStudio中的Debug窗口中的Frames窗口查看当前的堆栈情况。
-
来不及打断点
-
有时候应用程序已启动就崩溃,来不及Attach Debuger To Android Process,那么可以通过Debug App方式重新编译启动APP。
-
如果不能通过Debug App的方式处理(本地代码和设备中跑的代码节点不一致,需要尽可能保护现场),可以通过开发者选项里面,选择好调试的应用,打开等待调试器的开关。
-
有时候应用程序已启动就崩溃,来不及Attach Debuger To Android Process,那么可以通过Debug App方式重新编译启动APP。
源码调试
- 源码不匹配
- 设备运行的系统API版本和编译工程的版本不一致。找一个系统版本API和buildToolVersion一致的设备或虚拟机进行调试。
- 代码被混淆导致运行在设备上的字节码和本地的源码不一致。在出现无法用IDE在某些行打断点时,或者感觉断点错乱的时候,要注意检查是否是开启了混淆。
- IDE无法链接源码(Hidden Api)
-
在研究系统类时,我们会发现有些类找不到(Java代码中无法直接跳转到对应类),或者有些类的方法找不到(Java代码中无法直接跳转到对应方法),但实际上这些类或者方法是存在的。我们可以通过直接打开对应的类去查看里面的实现
-
在研究系统类时,我们会发现有些类找不到(Java代码中无法直接跳转到对应类),或者有些类的方法找不到(Java代码中无法直接跳转到对应方法),但实际上这些类或者方法是存在的。我们可以通过直接打开对应的类去查看里面的实现
抓包调试
- 线上一些偶发的问题出现在身边同事的设备上,由于是偶发,不能轻易破坏现场,如果怀疑是数据问题,可以通过抓包工具(Windows上可以用Fiddler,Mac上可以使用Charles)来查看网络请求数据。
- 混合开发遇到问题,总是需要通过抓包的方式来查看到底发出了哪些请求,是否携带上了必要的Cookie等。
WebView调试
- 设备USB方式链接好电脑,WebView配置成可调式模式,使用Chrome浏览器,地址栏输入chrome://inspect,可以在线调试网页。注意可能需要翻墙,且稳定性、兼容性会导致一部分手机无法调试。