Android-0.调试技巧相关简介


官方参考:
https://developer.android.com/studio/debug?hl=zh-CN

调试应用

选择菜单 run -> attach debugger to android process,注意不是Attach to process.., Android Studio的调试功能非常强大,同时支持多个调试上下文,可以attach多个进程,每attach一个进程,都会在AS底部的debug窗口打开一个新的Android debug标签页。每个标签页都有独立的调试上下文,分别对应一个进程。AS可以自动在多个debugger之间切换。

使用 Debug.waitForDebugger 调试

可以应用于以下场景:

  1. 被调试程序运行时会创建一个新进程,该进程很快执行完毕,来不及触发并attach
  2. 被调试程序运行时会启动一个新进程,但是想要调试触发动作之前代码逻辑

此时,可以使用Android提供的调试机制android.os.Debug.waitForDebugger();

Wait until a debugger attaches. As soon as the debugger attaches, this returns, so you will need to place a 
breakpoint after the waitForDebugger() call if you want to start tracing immediately.

该函数会等待调试器attach。该函数在调试器attach后立刻返回,因此如果想开始调试,那么需要在`waitForDebugger`后设置断点。

在这里插入图片描述
attach debugger to android process也对应快捷图标在这里插入图片描述

有时Debugger设置为Dual会附加失败,这时可以切换到Java试试
在这里插入图片描述
另外特别注意,比如VA的服务进程,在attachBaseContext时附加断点,停留1-2秒,Service进程就会重启,用adb查看,你会发现它又创建了一个新的Service进程,从而原来的附加自动结束,显示:

Disconnected from the target VM, address: 'localhost:8602', transport: 'socket'

所以要选好适当的延后的时机设置断点。

求值表达式[Evaluate Expression]

要在当前断点对某个表达式求值,在断点处,右键选择Evaluate Expression,之后你可以执行任何你感兴趣的表达式,并查看结果:
在这里插入图片描述

条件断点

例如:

        for (Integer i=0; i<1000; i++) {
            VLog.e("hgy413", i.toString());
        }

我们想在i=500时断下,在断点处鼠标右键就会弹出条件断点的小框,输入i.equals(500)
在这里插入图片描述
运行后结果如下:
在这里插入图片描述

日志断点

我们经常做的事情就是在代码里面添加日志信息,但是我们添加了日志代码需要重新编译, 从而浪费时间,这时就需要日志断点。
以前面示例继续,结合条件断点,我们想在i=300时打印一条日志,我们可以这样设置:
右键弹出的是小框(如上面gif),去掉Suspend勾选或点击More,才会出现下图大框:
关键设置示图:
在这里插入图片描述
结果:
在这里插入图片描述
但在logcat中其实会输出一连串hgy413 i=xx日志,说明它不受条件控制

方法断点

很多时候我们关心的是某个函数的参数,返回值,我们可以在函数级别进行调试,最简单的是在你感兴趣的函数头那一行设置断点,这时候你会发现断点图标有点不一样:在这里插入图片描述
而它断下后,你继续运行,它会在函数尾自动断下,如下图所示,它会自动在函数尾增加一个断点,用于你观察返回值。
在这里插入图片描述

异常断点

我们希望程序发现某种异常时断下,这时可以使用异常断点,Run -> View BreakPoints ,选择Java Exception Breakpoints
在这里插入图片描述
点击左上角的 ➕ ,会出现一个选择框;选择Exception Breakpoint,然后会出现一个对话框,选择你感兴趣的异常:
在这里插入图片描述

数据断点(访问或修改)

Run -> View BreakPoints ,选择Java Field Breakpoints, 我们可以在某个Field被访问或者修改的时候让程序断下来
在这里插入图片描述
设置被访问或者修改的图如下:
在这里插入图片描述

设置变量的值

在调试过程中,你发现某个变量的值跟你预期的结果不一样,或者你怀疑某变量出现某些特殊值时程序会发生崩溃,在变量区右键你想监控的变量,设置你想要出现的值,然后继续运行。
在这里插入图片描述

源码调试

  1. 模拟器调试 + AndroidXRef 下载
  2. 真机调试,需要设置参数debuggable为1, 参考IDA 动态调试简介,同时刷原生Android系统。
  3. 要调试android sdk,必须保证编译版本机器上的版本完全一致,简单的说,就是要保证是同一份代码, 不然断点就可能错位。
  4. 找对进程,如frameworks\base\services\core\java\com\android\server中的代码总是位于sytem_process进程中,具体参考下面:

对应AOSP的目录结构:
第一层:应用程序层(applications)对应根目录下platform/packages/apps
第二层:应用程序框架层(application framework)对应根目录下的platform/frameworks
第三层:运行库层包括运行库(libraries)和android运行时环境(android runtime)
运行库(libraries)对应目录很多,其中libc库对应的是platform/bionic
android运行时环境(android runtime),Core Libraries 对应根目录下的platform/libcoreDalvik Virtual Machine 对应根目录下的platform/dalvik ,不过现在已经是ART了,所以目录是platform/art
第四层:Linux内核层对应根目录下的kernel,每一个目录对应了一个kernel的版本
三、四层中间还有个硬件抽象层(HAL)对应根目录下的platform/hardware

1. 调试framework中的代码

API 25为例,先下载源码:
在这里插入图片描述
你可以直接用android sdk 25中的源码,也可以自己copy一份android sdk 25中的源码到本工程,不需要编译,反正只需要保证是同一份代码,就行了

简单写一个demo,然后新建一个API 25的模拟器,并把demo安装到模拟器。

ActivityThread.java
ActivityThreadmain是app启动的入口,之后会走到

 final IActivityManager mgr = ActivityManagerNative.getDefault();
 mgr.attachApplication(mAppThread);

–>ActivityManagerNative.java

public void attachApplication(IApplicationThread app) throws RemoteException {
	 .....
	 mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
	 .....
 }

这里使用了iBinder跨进程通讯,会调到ActivityManagerServiceattachApplication,而它位于system_process进程

使用Android Studio附加到模拟器的system_process进程
在这里插入图片描述
利用Android Studio搜索到attachApplication,并下断点,注意,断点要设置在适当的位置,具体参考前面所述
在这里插入图片描述
从模拟器中启动demo, 理论上会断点成功:
在这里插入图片描述

2. 调试其他进程的代码

比如调试系统Setting,先在 AndroidXRef 中找到 API25的代码,也就是 http://androidxref.com/7.1.1_r6/ , 系统App的源码在/packages这个子目录下面, 搜索Settings关键字就可以找到源码位于:
http://androidxref.com/7.1.1_r6/xref/packages/apps/Settings/ 然后我们把要调试的文件下载下来

androidxref只支持下载单个文件,可以通过 https://android.googlesource.com/ 找到对应的版本下载整个目录,打开 https://android.googlesource.com/ 然后直接搜索packages/apps/Settings,就可以找到了,然后选择对应的android-7.1.1_r6分支
在这里插入图片描述

打开AndroidManifest.xml可以查到包名为:com.android.settings,我们先attach到这个进程 :
在这里插入图片描述
查到设置程序的入口界面为:Settings,它继承于SettingsActivity,我们在SettingsActivityonCreate里面打一个断点,然后点击启动Setting程序,即可断下来了:
在这里插入图片描述

如果你觉得下载一个完整目录太麻烦,最简单的方式是在Android Studio中新建一个同名的文件,如Settings.java,从源码中复制一份相同的代码,这样就可以在你的同名文件中直接下断点了,注意包名要一致,比如都是com.android.settings
在这里插入图片描述

3.行号不对应怎么办?

行号不对应带来的一个首要问题就是,下断点的时候都有可能出现问题;比如你在TestClass的第100行下了一个断点,但是由于行号不对应,有可能真正执行的代码第100行是没有意义的空行或者是在下一个函数里面,这样断点就没有起到应有的作用了。

要解决行好对应的问题,必须使用方法断点;我们直接在某个函数的入口设置断点,这样即使行号对不上,也能在正确的入口出断下来,这一点非常重要。

在Android Studio的调试器的左边,显示了每一个线程执行的栈桢,栈桢里面包含了当前线程丰富的信息:
在这里插入图片描述
看到没,真正运行的代码在哪一行,当前运行的是什么函数, 局部变量是多少,一目了然;接下来你在step into/out的时候,不能以源代码的行数为准,而应该以这个栈桢所显示的代码行数为准。

4.利用xposed调试system等系统进程的启动流程

首先我们需要一个装了xposed框架的模拟器:参考xposed在真机和AVD上安装流程
然后写一个简单的xposed插件,打印所有的进程我, 确认system_process的进程名为android, 这时就简单过滤下,加个 android.os.Debug.waitForDebugger();再附加,如下简单代码:

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        // system_process对应的名字为android
        if (!TextUtils.isEmpty(lpparam.processName)
                && lpparam.processName.equals("android")) {
            android.os.Debug.waitForDebugger();

AMS初始化和启动简介可以看出,系统启动AMS的流程是从startBootstrapServices开始的,如下调试断下了:
在这里插入图片描述
之后进入AMS的构造函数:
在这里插入图片描述

5.调试源码版本不对应

这种问题经常出现,比如目标手机的SDK是23,我在android-23中的源码下了断点,却发现它跳转到android-26的源码中了,这是因为系统判断android studiocompileSdkVersion26, 很明显,这严重影响内部调试, 这是已知issues: https://issuetracker.google.com/issues/37058409

解决方式:

  1. 修改compileSdkVersion,但这可能会引起大量的错误
  2. 使用issues中介绍的临时解决方案,把原始的android-26文件夹备份,并把android-23文件夹复制一份,改名为android-26,记得调试完再改回去!

参考:
Android Studio你不知道的调试技巧
如何调试Android Framework

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值