今天我要跟你分享的是Framework的学习和调试的方法。

首先,Android是一种基于Linux的开放源代码软件栈,为广泛的设备和机型而创建。下图是Android平台的主要组件。

从图中你可以看到主要有以下几部分组成:

  • Linux内核

  • Android Runtime

  • 原生C/C++库

  • Java API框架(后面我称之为Framework框架层)

  • 系统应用

我们在各个应用市场看到的,大多是第三方应用,也就是安装在data区域的应用,它们可以卸载,并且权限也受到一些限制,比如不能直接设置时间日期,需要调用到系统应用设置里面再进行操作。

而我们在应用开发过程中使用的四大组件,便是在Framework框架层进行实现,应用通过约定俗成的规则,在AndroidMainfest.xml中进行配置,然后继承对应的基类进行复写。系统在启动过程中解析AndroidMainfest.xml,将应用的信息存储下来,随后根据用户的操作,或者系统的广播触发,启动对应的应用。

那么,我们先来看看Framework框架层都有哪些东西。

Framework框架层是应用开发过程中,调用的系统方法的内部实现,比如我们使用的TextView、Button控件,都是在这里实现的。再举几个例子,我们调用ActivityManager的getRunningAppProcesses方法查看当前运行的进程列表,还有我们使用NotificationManager的notify发送一个系统通知。

让我们来看看Framework相关的代码路径。

代码 源码目录 编译目录

系统的相关Widget

frameworks/base/core/java/android
frameworks/base/core/java/android/view

/system/framework/framework.jar

系统中的AMS/PMS

frameworks/base/services/core/java/com/android/server

/system/framework/services.jar

系统中内置的资源文件,比如Button的背景图、属性值之类

frameworks/base/core/res

/system/framework/framework-res.apk

在系统开发中还会涉及到服务的对应本地实现部分,比如

frameworks/base/core/jni
frameworks/base/services/core/jni

视情况

如何快速地学习、梳理Framework知识体系呢?常见的学习方法有下面几种:

  • 阅读书籍(方便梳理知识体系,但对于解决问题只能提供方向)。

  • 直接阅读源码(效率低,挑战难度大)。

  • 打Log和打堆栈 (效率有所提升,但需要反复编译,添加Log和堆栈代码)。

  • 直接联调,实时便捷(需要调试版本)。

首先可以通过购买相关的书籍进行学习,其中主要的知识体系有Linux操作系统,比如进程、线程、进程间通信、虚拟内存,建立起自己的软件架构。在此基础上学习Android的启动过程、服务进程SystemServer的创建、各个服务线程(AMS/PMS等)的创建过程,以及Launcher的启动过程。熟悉了这些之后,你还要了解ART虚拟机的主要工作原理,以及init和Zygote的主要工作原理。之后随着在工作和实践过程中你会发现,Framework主要是围绕应用启动、显示、广播消息、按键传递、添加服务等开展,这些代码的实现主要使用的是Java和C++这两种语言。

通过书籍或者网络资料学习一段时间后,你会发现很多问题都没有现成的解决方案,而此时就需要我们深入源码中进行挖掘和学习。但是除了阅读官方文档外,别忘了调试Framework也是一把利刃,可以让你游刃有余快速定位和分析源码。

下面我们来看看调试Framework的Java部分,关于C++的部分,需要使用GDB进行调试,你可以在课下实践一下,调试的过程可以参考《深入Android源码系列(一)》。

我们这里使用Android Studio进行调试,在调试前我们要先掌握一些知识。Java代码的调试,主要依据两个因素,一个是你要调试的进程;一个是调试的类对应的包名路径,同时还要保证你所运行的手机环境和你要调试的代码是匹配的。只要这两个信息匹配,编译不通过也是可以进行调试的。

我们调试的系统服务是在SystemServer进程中,可以使用下面的命令验证(我这里使用Genymotion上安装安卓对应版本镜像的环境演示):
ps -A |grep system_server  查看系统服务进程pid
cat /proc/pid/maps |grep services 通过cat查看此进程的内存映射,看看是否services映射到内存里面

这里我们看到信息:/system/framework/oat/x86/services.odex

odex是Android系统对于dex的进一步优化,目的是为了提升执行效率。从这个信息便可以确定,我们的services.jar确实是跑到这里了,也就是我们的系统服务相关联的代码,可以通过调试SystemServer进程进行跟踪。

下来我们来建立调试环境。

  1. 打开Genymotion,选择下载好Android 9.0的镜像文件,启动模拟器。

  2. 找到模拟器对应的ActivityManagerService.java代码。 我是从http://androidxref.com/下载Android 9.0对应的代码。

  3. 打开Android Studio,File -> New -> New Project然后直接Next直到完成就行。

  4. 新建一个包名,从ActivityManagerService.java文件中找到它,这里为com.android.server.am,然后把ActivityManagerService.java放到里面即可。

  5. 在ActivityManagerService.java的startActivity方法上面设置断点,然后找到菜单的Run -> Attach debugger to Android process勾选Show all process,选中system_server进程确定。

这时候我们点击Genymotion模拟器中桌面的一个图标,启动新的界面。

会发现这时候我们设定的断点已经生效。

你可以看到断下来的堆栈信息,以及一些变量值,然后我们可以一步步调试下去,跟踪启动的流程。

对于学习系统服务线程来讲,通过调试可以快速掌握流程,再结合阅读源码,便可以快速学习,掌握系统框架的整个逻辑,从而节省学习的时间成本。

以上我们验证了系统服务AMS服务代码的调试,其他服务调试方法也是一样,具体的线程信息,可以使用下面的命令查看。

ps -T 353

这里353是使用ps -A |grep system_server查出 SystemServer的进程号

在上面图中,PID = TID的只有第一行这一行,如果PID = TID的话,也就是这个线程是主线程。下面是我们平时使用Logcat查看输出的信息。

03-10 09:33:01.804   240   240 I hostapd : type=1400 audit(0.0:1123): avc: de
03-10 09:33:37.320   353  1213 D WificondControl: Scan result ready event
03-10 09:34:00.045   404   491 D hwcomposer: hw_composer sent 6 syncs in 60s

这里我还框了一个ActivityManager的线程,这个是线程的名称,通过查看这行的TID(368)就知道下面的Log就是这个线程输出的。

03-10 08:47:33.574   353   368 I ActivityManager: Force stopping com.android.providers

学习完上面的知识,相信你应该学会了系统服务的调试。通过调试分析,我们便可以将系统服务框架进行庖丁解牛般的学习,面对大量庞杂的代码掌握起来也可以轻松一些。

我们回过头来,再次在终端中输入ps -A,看看下面这一段信息。

你可以看到这里的第一列,代表的是当前的用户,这里有system root和u0_axx,不同的用户有不同的权限。我们当前关注的是第二列和第三列,第二列代表的是PID,也就是进程ID;第三列代表的是PPID,也就是父进程ID。

你发现我这里框住的都是同一个父进程,那么我们来找下这个323进程,看看它到底是谁。

root 323 1 1089040 127540 poll_schedule_timeout f16fcbc9 S zygote

这个名字在学习Android系统的时候,总被反复提及,因为它是我们Android世界的孵化器,每一个上层应用的创建,都是通过Zygote调用fork创建的子进程,而子进程可以快速继承父进程已经加载的资源库,这里主要指的是应用所需的JAR包,比如/system/framework/framework.jar,因为我们应用所需的基础控件都在这里,像View、TextView、ImageView。

接下来我来讲解下一个调试,也就是对TextView的调试(其他Button调试方式一样)。如前面所说,这个代码被编译到/system/framework/framework.jar,那么我们通过ps命令和cat /proc/pid/maps命令在Zygote中找到它,同时它能够被每一个由Zygote创建的子进程找到,比如我们当前要调试Gallery的主界面TextView。

我们验证下,使用ps -A |grep gallery3d查到Gallery对应的进程PID,使用cat /proc/pid/maps |grep framework.jar看到如下信息:

efcd5000-efcd6000 r--s 00000000 08:06 684                                /system/framework/framework.jar

这说明我们要调试的应用进程在内存映射中确实存在,那么我们就需要在gallery3d进程中下断点了。

下来我们建立调试环境:

  1. 打开Genymotion,选择下载好Android 9.0的镜像文件,启动模拟器,然后在桌面上启动Gallery图库应用。

  2. 找到模拟器对应的TextView.java代码。

  3. 打开Android Studio,File -> New -> New Project然后直接Next直到完成就行。

  4. 新建一个包名,从TextView.java文件中找到它的包名,这里为android.widget,然后把TextView.java放到里面即可。

  5. 在TextView.java的onDraw方法上面设置断点,然后找到菜单的Run -> Attach debugger to Android process勾选Show all process,选中com.android.gallery3d进程(我们已知这个主界面有TextView控件)确定。

然后我们点击下这个界面左上角的菜单,随便选择一个点击,发现断点已生效,具体如下图所示。

然后我们可以使用界面上的调试按钮(或者快捷键)进行调试代码。

今天我讲解了如何调试Framework中的系统服务进程的AMS服务线程,其他PMS、WMS的调试方法跟AMS一样。并且我也讲解了如何调试一个应用里面的TextView控件,其他的比如Button、ImageView调试方法跟TextView也是一样的。

通过今天的学习,我希望能够给你一个学习系统框架最便捷的路径。在解决系统问题的时候,你可以方便的使用调试分析,从而快速定位、修复问题。

那么,你是否已经跃跃欲试,准备调试一下自己的应用呢?提出一个问题,我们调试Gallery应用的TextView时候,前提是让这个应用先运行起来,如果我们想调试从点击桌面Gallery图标到Gallery主界面绘制出来的过程,该如何调试呢?

我们是否能够调试市面上发行的三方应用呢?比如微信,支付宝的某个界面的TextView控件?

再深入一下,如果我们想研究市面一款应用的内部实现,需要反编译分析代码,我们能否调试反编译出来的Smali代码呢?

推荐阅读
Kotlin新版来了,支持跨平台!
Google I/O 和 Android 开发者有关的点

编程·思维·职场
欢迎扫码关注

安卓button设置背景图_这些安卓源码调试技巧,不懂的人月薪绝对不过 30k !相关推荐

  1. 安卓button设置背景图_背景图| 人生很短 不要浪费

    点击篮字[妞妞的大叔]- 右上角... 立刻设我为星标/置顶 - 下次不迷路 精美手机壁纸.朋友圈背景图.表情头像 壁纸|头像|表情| 潮图 就在"妞妞的大叔"壁纸公众号 手机壁纸 ...

  2. 安卓button设置背景图_背景图| 专业团队

    点击篮字[妞妞的大叔]- 右上角... 立刻设我为星标/置顶 - 下次不迷路 精美手机壁纸.朋友圈背景图.表情头像 壁纸|头像|表情| 潮图 就在"妞妞的大叔"壁纸公众号 手机壁纸 ...

  3. android自助终端界面源码_这些安卓源码调试技巧,不懂的程序员月薪绝对不过 30k...

    今天我分享的是Framework的学习和调试的方法. 首先,Android是一种基于Linux的开放源代码软件栈,为广泛的设备和机型而创建.下图是Android平台的主要组件. 从图中你可以看到主要有 ...

  4. MFC/WTL 设置背景图和控件透明的方法

    1.MFC 对话框设置背景图 方法一: 1>资源中导入一张bitmap图,如命名为 IDB_BITMAP1 2>对话框中添加一个 Picture Control,属性设置 Type 改为B ...

  5. 给button设置背景图片

    //在mfc中,是没有picturebutton的,如果需要一个类似picturebutton的按钮,可以通过设置button的 //背景图片来实现. 首先,准备好需要设置的图片.(这里以位图为例)把 ...

  6. 使用 XML 实现按钮改变焦点设置背景图

    在 drawable 文件夹中添加一个 advancedbutton.xml 设置<selector>和 <item>标签 <?xml version="1.0 ...

  7. 设置背景图时防止图片拉伸的解决方法

    在设置背景图时,如果图片不够大会被拉伸,使图片失真,如果图片太大会对view控件的显示造成影响. 如果只是在ImageView中设置图片的话,在程式中可以利用setScaleType进行动态设定,在x ...

  8. 从0开始html前端页面开发_CSS实现设置背景图自适应屏幕大小

    只需要在css样式里对BODY元素添加css样式即可 html代码如下 <STYLE TYPE="text/css"> BODY {background-image: ...

  9. QT上设置背景图不影响子控件以及按钮控件的透明化

    QT中设置背景图不影响子控件 QT中设置背景图的方法不少,我这里是直接使用stylesheet来设置背景图.如果直接在UI界面进行如下所示设置 border-image: url(:/resource ...

最新文章

  1. 分享一个有趣的学习方法,欢迎一起探讨如何提高学习兴趣作者:1-2-3 来源:博客园 发布时间:2009-03-09 16:20 阅读:2820 次 原文链接 [收藏]
  2. js中,(function(){})()的用法解析
  3. 历史是“混合体”,也是“内幕”:说说《三国大传》
  4. day11-元组与字典
  5. POI 导出导入工具类介绍
  6. AcWing 867. 分解质因数(唯一分解定理)
  7. 一站式VDI部署教程(4)配置SMB3.0共享
  8. marlab中主成分得分怎么求_羡慕神仙权重?主成分与因子分析带你揭开权重的秘密...
  9. 阿里云推送:Android8.0及以上收不到推送的解决
  10. 购买域名后如何使用?
  11. python:epub文件批量转TXT
  12. Android去除对话框边距,去除TextView默认内边距
  13. 2429: [HAOI2006]聪明的猴子
  14. matlab画图三维立体,matlab的三维图形绘制
  15. echarts柱状图改变标签的位置及柱状图颜色
  16. 素雅的登录界面,简单而优雅
  17. (25)Verilog实现三态门(inout)
  18. Spring-工作原理
  19. 浅谈Thread.setDaemon
  20. 业界前沿技术:从零开始学视觉Transformer-Data-Efficient Image Transformers

热门文章

  1. 小白学Python——Anaconda安装
  2. MySQL对于表中数据的增删改查
  3. Flask 中的上下文管理和请求钩子
  4. ExtJs2.0学习系列(8)--Ext.FormPanel之第五式(综合篇)
  5. ASP.NET企业开发框架IsLine FrameWork系列之十四--框架配置信息大全(中)
  6. Multisim、protel和proteus的区别
  7. 深度学习-tensorflow1.x:平均值(reduce_mean)与求和(reduce_sum) 小白理解 代码实现 Tensorflow1.x 和 Numpy
  8. LeetCode —— 145. 二叉树的后序遍历【递归与迭代】(Python)
  9. 数字图像处理--图像的缩放变换
  10. kaggle实战—泰坦尼克(五、模型搭建-模型评估)