通过上一篇 Android FrameWork学习(一)Android 7.0系统源码下载\编译 我们了解了如何进行系统源码的下载和编译工作。

为了更进一步地学习跟研究 Android 系统源码,今天我们来讲讲如何进行 Android 系统源码的调试,只有学会了如何进行系统源码的调试,才能帮助我们更高效地阅读跟理解源码。

我们知道,Android Framework 的代码主要由Java、C\C++等代码组成,因此,对于系统源码的调试,我们这里将其分为了两部分

  • Java 相关代码的调试
  • C\C++ Native 相关代码的调试

#一、Java 相关代码的调试

对于 Java 相关代码的调试,这里我们主要使用 Android Studio 开发工具来进行。

###导入源码到 Android Studio
要在 Android Studio 中调试源码,那第一步自然是导入系统源码到 Android Studio 中了。

####1. 编译 idegen
对于 Android 源码的导入, Google 官方给我们提供了一个很方便的工具 idegen

它位于我们所下载的系统源码路径中:

developement/tools/idegen
复制代码

引用 README 的一句话

IDEGen automatically generates Android IDE configurations for IntelliJ IDEA
and Eclipse.

idegen 工具会自动生成针对 Android 开发工具(Android Studio和Eclipse)的配置文件。

既然如此,那我们就来使用 idegen 工具生成导入源码所需的配置文件。

首先打开命令行工具,cd 进入到源码路径下,

执行如下指令:

#初始化命令工具
soruce build/envsetup.sh
#编译 idegen 模块,生成idegen.jar
mmm development/tools/idegen/
#生成针对 Android 开发工具的配置文件
sudo ./development/tools/idegen/idegen.sh
复制代码

在执行完上述指令后,会在源码路径下生成下面三个文件

android.ipr:工程相关的设置,比如编译器配置、入口,相关的libraries等。

android.iml:描述了modules,比如modules的路径,依赖关系等。

android.iws:包含了一些个人工作区的设置。

####2. 导入源码
接下来我们可以开始导入源码了.

如果你是第一次导入源码, Android Studio 可能需要占用大量的内存,我们需要设置下我们的 VM 选项。

Linux 设备的话在 Android Studio 的 bin/studio64.vmoptions 文件中添加-Xms748m -Xmx748m

如果你使用的是 Mac ,那么在 AS 目录的 Contents/Info.plist 目录中进行添加。

由于 Android 的系统源码非常庞大,一次性导入 Android Studio 的话需要加载非常长的时间

因此,在正式开始导入前,我们可以打开 android.iml 文件根据自己需要调整要加载的源码。

这里 <excludeFolder> 表示不需要加载的目录,我们根据自己的需要使用 <excludeFolder> 标签添加对应的目录地址即可。

接着,选择 File -> open 选中 android.ipr 文件,打开

这时 Android Studio 就会开始加载源码了

在没有添加修改 <excludeFolder> 的情况下,这个加载的时间会比较长,经过一段时间的等待后,代码就加载完毕了,如图:

这里红色的目录代表被 exclude 排除了,代码加载 scan index 的时候会过滤掉该目录。

在加载完源码后,我们也可以在 Project Structure 中的 Module 选项中右键 exclude 来排除不需要加载的源码目录,如图:

####3. 配置代码依赖,确保代码跳转正确

为了阅读和调试代码的时候能够保证代码跳转正确,我们需要配置下相关依赖。

首先是 AOSP 源码的跳转,我们通过 File -> Project Structure 打开 Module,然后选中 Dependencies, 保留 JDK 跟 Module Source 项,并添加源码的 external 和 frameworks 依赖,如图:

然后是 SDK 的设置,确保关联对应版本的 SDK 于系统版本一直

###开始调试源码

调试前要设置 Project 的 SDK , File -> Project 下打开 Project Structure,选中 Project 设置对应版本的 SDK,于系统版本一致:

确保 Android Studio 允许 ADB 调试

接着我们参照上一篇文章中讲的方法打开 Android 模拟器

此时点击 Android Studio 工具栏的 attach debugger to Android process 按钮,会打开 Choose Process 窗口,我们根据自己需要调试的代码选择对应的进程:

这里假设我们要调试 Android 自带浏览器的源码,如图,我们在它的入口文件 WebViewBrowserActivity 中的 loadUrlFromUrlBar 方法中打上断点。

点击 WebViewBrowser 打开 app

打开之后,点击 attach to Android process 按钮打开 choose Process,可以看到 webViewBrowser 运行的进程,选中,ok

然后我们在 app 的 url 输入栏输入 网址进行跳转

如图所示我们可以看到,代码成功进入了断点,然后我们就可以随心所欲地调试我们想要的调试的 Java 代码了。


#二、Native C\C++ 相关代码调试
对于 Framework Native 代码,我们这里使用 GDB 工具来进行调试。

###什么是 GDB 呢?
它是一款 GNU 项目调试工具,它的功能非常强大,可以用来调试 C 、C++、Object-C、Pascal 等语言编写的项目。

对于使用习惯了可视化 IDE 的同学们来说,它最大的缺点可能就是它不支持图形化了

但是 GDB 提供的指令非常灵活,通过指令我们

  • 可以随心所欲地启动程序,
  • 可以根据自己的需要设置断点,
  • 可以查看断点处的变量,代码信息
  • 可以查看程序运行的调用栈

一旦你熟悉了它,你便可以玩得飞起!

一般情况下,使用 gdb 来调试 Android 源码需要在 Android 设备上安装 gbdserver attach 关联我们需要调试的进程,再使用 gdb 指令去连接 gdbserver 进行调试

不过官方给我们提供了 gdbclient 工具,可以让我们方便地进行 gdb 调试。

###开始 GDB 调试

这里我们就基于 gdbclient 来进行实际的 gdb 调试演示:

跟上面 Java 调试一样,我们这里还是以系统自带的浏览器为例。

####1. 点击启动图标打开浏览器 app:

####2. 打开一个命令行终端,cd 进入到系统源码目录(我的源码路径为 aosp),初始化命令工具:

#进入源码路径
cd aosp
#初始化命令工具
source build/envsetup.sh
#选择编译的源码的版本,参考上一篇文章
lunch
复制代码

####3. 通过 adb 指令来查找要调试进程的 PID

# 通过 shell ps 指令查找相关进程,grep 搜索过滤 webview 进程
adb shell ps -A | grep webview
复制代码

如图,2157 为系统自带浏览器 app 所在进程的 PID

####4. 使用 gdbclient 命令工具启用 gdb 调试 PID 对应进程

# gdbclient <app pid> 可以启用 gdb 调试对应 PID 进程
gdbclient 2157
复制代码

等待进入 gdb 调试命令界面

####5. 使用 GDB b 命令打断点

在 gdb 指令中,我们使用b <代码文件>:行号 来设置断点.

这里我们选择 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp 代码文件的 drawFrame 方法打上断点,位于文件 71 行:

该方法主要用于绘制帧,当浏览器 app 的界面发生变化时会触发该方法。

我们输入设置断点命令:

b frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:71
复制代码

输入指令后显示
Breakpoint 2 at 0x7f69e9892c11: file frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp, line 71.
说明我们的断点设置成功了。

####6. 在命令行输入c 开始监听

c 即 continue,此时界面上出现 Continuing 说明开始监听进程了

我们点开模拟器,随意操作,触发界面变化时,便会进入绘制帧的代码断点了:

如图,显示进入断点,这样代表我们的代码调试成功了。


这里我们只是演示了一个大概流程,

gdb 代码的调试需要你对源码有一定的熟悉,知道哪个进程会调用哪个文件方法。

同时,我们还需要熟悉 gdb 的各种命令,这里给大家推荐一篇不错的入门文章,可以快速入门:

GDB十分钟教程

这里补充一点,如果你希望在某个进程启动时就监听,可以使用下面的指令关联目录,得到 pid,再通过 gdbclient 来进行调试

adb shell gdbserver :5039 /system/bin/my_test_app
Process my_test_app created; pid = 3460
Listening on port 5039
复制代码
gdbclient <app pid>
复制代码

如果你希望通过 Android Studio 来调试 Framework 的 C\C++ 代码的话,也可以参考下面的两篇文章,不过个人觉得这种方法有一定的局限性。

如何调试Android Native Framework

用Android Studio调试Framework层代码


#结语
正如文章开头所说,只有学会了如何调试 Framework 源码,才能帮助我们更好地学习 Android Framework,希望这篇文章能给大家一些帮助,如果有更好地调试方法,欢迎大家给我留言咯!

Android FrameWork学习(二)Android系统源码调试的更多相关文章

  1. Android FrameWork 学习之Android 系统源码调试

    这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...

  2. [spring源码学习]二、IOC源码——配置文件读取

    一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...

  3. Android开发学习之路-Volley源码解析

    从简单的StringRequest入手看看Volley的工作机制. 先简单说下Volley的用法: ① 获取一个RequestQueue mRequestQueue = Volley.newReque ...

  4. Android进阶:二、从源码角度看透 HandlerThread 和 IntentService 本质

    上篇文章我们讲日志的存储策略的时候用到了HandlerThread,它适合处理"多而小的任务"的耗时任务的时候,避免产生太多线程影响性能,那这个HandlerThread的原理到底 ...

  5. mybatis源码学习(二)--mybatis+spring源码学习

    这篇笔记主要来就,mybatis是如何利用spring的扩展点来实现和spring的整合 1.mybatis和spring整合之后,我们就不需要使用sqlSession.selectOne()这种方式 ...

  6. Android开发学习之路-SimpleAdapter源码分析学习

    今天在课堂上,老师用到了SimpleAdapter,然后女神在边上问我为什么这个SimpleAdapter不能做到我app那种带有进度条的效果,言语说不清,然后就开始看源代码,发现这个Adapter的 ...

  7. Redis学习之对象系统源码分析

    背景知识: Redis并没有直接使用sds,双端链表,字典,压缩列表,跳表等这些数据结构来直接实现键值对数据库,而是基于这些对象创建了一个对象系统,这个对象系统包含5个对象:字符串对象,列表对象,哈希 ...

  8. 【转】编译Android系统源码和内核源码

    原文网址:http://blog.csdn.net/jiangwei0910410003/article/details/37988637 好长时间没有写blog了,之所以没有写,主要还是工作上的事, ...

  9. 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介

    前言        古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...

随机推荐

  1. Pytest系列(10) - firture 传参数 request的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 为了提高复用性,我们在写测试 ...

  2. 搭建KVM

                                                                       系统&服务管理进阶 搭建KVM服务器 虚拟化概述 • vi ...

  3. flask-宏

    flask-宏 模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,使用宏的时候,参数可以为默认值. ...

  4. Python安装MySQL数据库模块

    背景 折腾: [记录]使用Python操作MySQL数据库 的过程中,需要去安装MySQLdb. 下载MySQLdb 去官网: http://pypi.python.org/pypi/MySQL-py ...

  5. 30.1 HashSet存储自定义对象 未去重解决

    问题: package day30_HashSet; import java.util.HashSet; /* * 通过hashset存储自定义对象,没有进行去重. * * */ public cla ...

  6. Daily Scrum 1/18/2016

    Yandong & Zhaoyang: Prepare bug bash slides for Beta release; Dong & Fuchen:Prepare demo for ...

  7. AI vs PS 矢量 VS 位图

    矢量图 AI最大可以放大64000%.不会失真,依然很清晰.原理是不同的点以及点与点之间的路径构成的,不论放大的多大,点在路径在,就可以精确的计算出它的区域.AI中无法直接编辑位图. 位图 代表PS, ...

  8. ESI web学习记录

    Ezbypass 解出 放在php新特性的文章最后了,利用json反序列化脚本绕过disable_functions来get flag Ezupload 没解出 登陆页面查看源代码发现,备份文件泄露. ...

  9. 【题解】P3349 [ZJOI2016]小星星 - 子集dp - 容斥

    P3349 [ZJOI2016]小星星 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 小 \(Y\) 是一个心灵手巧 ...

  10. redis: 乐观锁(十)

    监视:watch 正常业务(单线程): 127.0.0.1:6379> set money 100 #模拟存款100元 OK 127.0.0.1:6379> set moneyout 0 ...