Android DDMS:终极 Android 控制台指南

已发表: 2022-03-11

开发是一项棘手的业务。 目标不断发展,新技术和领域定期出现,新工具不时出现,语言在似乎受到管理的破坏中发生变化。

尽管如此,即使有所有这些变化,基本规则仍然保持不变。 这些基本规则中最重要的一条是,要创建真正出色的软件,您必须深入、持续和详细地检查您的执行系统。 诊断调试分析是有时在这种情况下使用的术语,但规则更深入。 一流的开发人员确实“感觉”了他的系统。 他知道什么会导致块等待更多内存释放,什么会将其线程运行到 CPU 饥饿状态,哪些操作将导致大量 I/O 或网络访问,因此会减慢其整个操作。

真的没有办法解决。 您可能是一个非常聪明的开发人员,编写了很棒的代码,但是,除非您不具备上述技能,即能够监控和研究系统运行时行为的细节,否则您在交付真正一流的代码方面仍然会失败应用程序。

事实上,在获得一些经验之后,你会发现一整类“代码病”,这可以追溯到忽略了自省规则:简单地说,编写代码(有时是智能代码)而没有持续监控其在实际平台上的影响.

Android 中的 DDMS:我自省的首选武器

对我们来说幸运的是,Android 社​​区已经设法提供了这么多一流的内省工具。 Facebook 的 Stetho 是最好的,AT&T 的 ARO(“应用程序资源优化器”)有点老,但仍然是一流的,可能是最好的网络监控控制台,而 LeakCanary 采取了一种更有限的方法来集中注意力(并且在它)在运行时内存泄漏检测库上。 长话短说,那里不乏 Android 调试工具。

尽管如此,皇冠上的钻石,在需要提取有关应用程序运行时行为的关键、准确和格式良好的数据时值得信赖的内省工具仍然是 Android Studio 中的旧版 Dalvik 调试监视器服务器 (DDMS),它自从 Eclipse Android 插件时代以来,我们就一直在使用(可惜没有被这么多团队使用)。

DDMS 在 Android 开发中有多重要? 好吧,作为一个经验不足的 Android 开发人员,知道我现在对 DDMS 和移动应用程序监控的一般了解是 5 到 6 年前,这将为我省去很多麻烦和调试之夜。

Android DDMS 封面插图:使用 DDMS 进行开发人员调试。

问题是 DDMS 非常容易掌握!

当然,与任何其他软件工具一样,正确使用它的很大一部分都带有经验。 你需要磨练你的专业技能一段时间,直到你真正擅长运行时性能监控。 但即使在几个小时内,读完这篇文章后说,如果你按照我的建议并将它们应用到你的下一个应用程序中,结果将是惊人的! 即使是复杂的系统,分析和调整也不是那么难。 它也可以很有趣!

经常被问到关于新手和大师级移动开发人员之间的区别的问题。 掌握 Android 中的 DDMS(或者一般而言,应用程序分析和自省功能)就是这样的主要区别之一。

注意:成为一流开发人员的主要部分是使用您所在域中可用的最佳库。 在之前的 Toptal 文章中,我列出了一些可用于 Android 的最佳开发人员库。 从某种意义上说,这篇文章是“库”文章的续集,涵盖了众多 Android 工具之一。 不用说,如果您的目标是提高您的 Android 开发人员技能,请立即阅读!

Android Studio 中的 DDMS 快速指南

现在,事不宜迟,让我们现在深入了解 DDMS,这是终极的 Android 开发人员工具之一。

在权衡努力与收益时,可能没有其他工具可以提高您的应用程序的质量并帮助您找到它可能包含的真正混乱和难以捉摸的错误。 但是,由于某种原因(懒惰,有人吗?),很多团队没有使用 DDMS。

让我们从 DDMS 的速成课程开始:

可以通过Studio > Tools > Android > Android Device Monitor访问 DDMS,然后单击菜单上的 DDMS 按钮。 您也可以在上面板中放置一个快捷方式图标(我愿意)。

打开后,您将看到以下内容:

屏幕截图:通过 Android 设备监视器访问 DDMS

左侧面板允许选择设备/应用程序,右侧控制台为您提供多个视图,每个视图都位于其自己的选项卡中,每个视图都显示您的应用程序的特定视图。

Dalvik Debug Monitor Server 提供的主要服务有:

  • 应用内存使用统计(总堆和对象分配统计)
  • 应用线程统计
  • 设备屏幕截图
  • 设备文件浏览器
  • 来电和短信欺骗
  • 位置数据欺骗
  • 日志猫

要获取您的应用程序使用的当前堆内存值,只需执行以下操作:

  • 连接运行您的应用程序的设备
  • 单击更新堆按钮以启用堆统计信息收集
  • 打开堆选项卡
  • 点击“Cause GC”强制GC运行。 只有在这样的运行之后,堆数据收集才会开始
  • 保持选项卡打开,继续处理您的应用程序,并定期重新单击“Cause GC”以刷新堆统计数据

最后一行可能需要额外的解释。 内存使用量是其中动态比初始值更重要的分析值之一。 对于大多数应用程序,我们不会太在意初始堆使用值。 我们将非常关心这个值的进展,因为它将为我们提供一个明确的指示,即等待移动开发人员的真正噩梦之一——Android 内存泄漏:

屏幕截图:堆数据用于识别 DDMS 中的 Android 内存泄漏

我对堆统计模块的使用很简单; 作为开发应用程序生命周期的一部分,在引入可能影响堆使用的更改后,我将激活模块,“Cause GC”以初始化统计数据收集,激活(通常不止一次)我的应用程序的堆密集型位置,并定期“导致 GC”刷新。 如果堆使用量不断增长,我的手上有内存泄漏,我需要解决它(详细说明如何 - 下面)。 如果不是,并且无论实际堆大小如何,我都很好。

如果检测到内存泄漏,我将使用的下一个工具是对象分配跟踪器。 让我们看看它可以为 Android 中的内存管理做些什么。

对象分配跟踪器

简而言之,分配跟踪器将为您提供所需的信息,以确定谁是当前堆大小的“罪魁祸首”。 该模块将实时告诉您分配命令来自哪些线程和方法,这对于 Android 中的内存分析非常有用。

要开始跟踪,请执行以下操作:

  • 像以前一样选择相关的设备/进程
  • 切换到 Allocation Tracker 选项卡,然后单击 Start Tracking 开始。
  • 从这里开始,将跟踪所有新的分配
  • 单击“获取分配”以获取所有最新分配的列表视图(自上次“开始”以来的最新)
  • 要找出分配机构是谁,请单击列表中的特定行

DDMS 中的对象分配跟踪器,用户界面

现在,根据我自己的经验,在您的应用程序上执行分配密集型操作,然后单击“获取分配”以查看分配计数器通常应该以直接的方式引导您找到泄漏; 有时,当泄漏是非线性的(即,时不时发生)或者当您的应用程序包含多个可能无法工作的泄漏时。 在这种情况下,我还没有遇到很多,您将需要手动创建转储 HPROF 文件并对其进行分析。 本文不会深入介绍内存分析和 Android 内存管理。 请参阅此处了解一些线索。

线程信息控制台:轻松使用 Android CPU

任何开发人员都知道,执行逻辑的同步路径被分组到线程中,每个线程在您的应用程序中构成一个串行执行流。 从字面上看,所有应用程序都使用多个执行线程。 其中一些使用数十个。

使用线程时对潜在问题的全面检查超出了本文的范围。 然后让我们专注于一个问题,即线程饥饿,这是您访问线程信息控制台的主要问题。

在所有移动应用程序中,不同的线程都会争夺 CPU 时间。 根本没有足够的人可以四处走动。 如果出于某种原因,一个或多个线程无法获得所需的执行时间,会发生什么情况? 通常是坏事。 系统不会像您计划的那样运行,这总是一个坏主意。 这个问题的潜在原因可能是设置低优先级,同时执行的其他线程将自己设置为过高的优先级,在同步监视器上花费很长时间等等。 众所周知,仅通过代码审查很难检测到所有这些。

Android DDMS 线程控制台来救援!

DDMS 中的线程信息控制台,用户界面

当您进入线程视图时,您将看到一个由线程记录组成的列表,每个记录包含线程的名称和 ID,以及两个称为 utime 和 stime 的额外计数器。 Utime 衡量线程执行用户代码所花费的总时间(想想你的函数和第三方库),而 stime 衡量花费在系统代码上的总时间(睡眠、同步、系统调用——很多)。 第一个——utime——通常对我们来说更有趣,尽管我能想到的问题大多会通过时间计数器来体现。

好的,我们的代码正在运行,包括几个线程,我们希望确保我们所有的线程都能获得它们的 CPU 时间份额。 为此,我们首先让系统运行一段时间,然后打开线程选项卡并开始寻找“特殊”的 utime 值。 零当然可以代表一个问题——线程实际上没有 CPU 时间和 CPU 利用率。 但是过高的值可能代表同一问题的不同方面:即优先级高到导致其他线程饿死的线程。

请注意,对于一种类型的线程,零或接近零的 utime 值并不表示真正的问题。 这些是 I/O 绑定线程,主要执行网络或磁盘(或数据库)访问的线程。 这些线程应该花费大部分时间等待数据到达或阻塞挂起的系统调用,这些操作都不会增加 utime 计数器。 了解你的线程!

提示:切勿使用线程的默认名称。 它没有任何意义,您通常无法在 DDMS 视图中检测到它。 相反,每当创建一个线程或从线程池中获取它时,通过为其分配一个不言自明的名称来开始您的交互。 这将使您的生活比调试/分析您的系统更容易。 我通常会在应用程序名称前加上 Android 生成的线程和我自己的代码生成的线程,例如:MyApp-server-connector、MyApp-db-interactor 等。

提示:线程的优先级表示(粗略地说)调度程序将授予的 CPU 时间量。 分配给工作线程的优先级对应用程序的整体性能和“流畅性”至关重要,在许多情况下,这可能是快速行为与颠簸缓慢行为之间的区别。 这里的规则很简单:Android 分配的默认优先级,即 NORMAL=5,几乎总是不是您想要使用的优先级。 相反,对于大多数工作线程,您希望对整体 CPU 使用率的影响更小。 为此,在线程启动时,将其优先级设置为较小的值,我通常使用优先级=3。

网络统计控制台

网络统计是关于允许您以合理的人类可读方式监控应用程序的传入和传出通信通道。

网络图表中的 y 轴代表以 KB/秒为单位测量的传输传输速度,而 x 轴代表以秒为单位的经过时间。 因此,为了快速估计传输的大小,请尝试估计相关尖峰的面积。 一段时间后,这变得相当容易。

请注意,进入此控制台后,您需要单击上方的“启用”按钮才能开始显示网络测量。

在网络控制台成熟到现在的水平之前,开发人员通常不得不求助于使用嗅探器应用程序(有些仍然这样做)来获取类似的信息。

这个控制台的伟大之处在于它可视化了主要的电池消耗行为之一 - 正在进行的小数据包大小的通信。 正如你们许多人所知,使您的应用程序耗电的不是它所做的五分钟密集网络,而是长时间的短时间重复网络,例如,为了保持活动、诊断或状态更新。

一旦检测到这样的模式,并且网络控制台的可视数据包显示使其变得如此简单,立即考虑批处理。 我可以将多个小传输批量成一个大传输吗? 这种变化对电池的影响势必会将应用程序从电池消耗者转移到表现良好的类别!

DDMS 中的网络统计控制台

提示:切勿将图像按原样加载到内存中。 这是等待发生的内存不足崩溃。 相反,执行按比例缩小的加载,甚至更好的是,使用第三方库为您管理缩放。

尽管您很少使用此信息,但请注意,DDMS 依赖于 Android 调试桥 (ADB) 堆栈来将数据传回/传出设备。 如果 DDMS 无法显示您的应用程序,或在 DDMS 会话中间冻结,您最好的办法是打开控制台并输入:

 adb devices

确保您的设备可以访问并获得亚行的授权。 如果不是这种情况,在许多情况下,重新启动本地 ADB 服务器应该可以解决问题:

 adb kill-server adb devices # restarts the adb server and displays all detected devices

如果您仍然遇到问题并且您的应用程序安装在物理设备上,请尝试断开所有模拟器实例的连接。 为什么? 因为 DDMS 将自己连接到物理设备设备和仿真器实例,所以默认为后者。

现实生活中的 DDMS 用法示例:应用程序停止(不是崩溃,只是停止)。 用户立即冲到附近的工作站,连接到 USB,并在线程视图中打开 DDMS 以找出线程堆栈 » 失败线程 » 堆栈跟踪 - 在我的情况下,由于同步死锁,一旦检测到,通过切换很容易解决。

提示:如果 Android 分配给您的应用程序的标准 RAM 内存不够,例如媒体密集型应用程序可能会发生这种情况,请注意,您可以通过提高 _ largeHeap清单标志在大多数设备上获得大约 15-20% 的额外内存:https://developer.android.com/guide/topics/manifest/application-element.html_

Android DDMS 中的设备状态仿真

通常,移动应用程序不是线性结构。 相反,他们部署了意识策略,使他们能够监控设备状态的变化并做出反应。 例如,应用程序可以收听来电或短信,可以根据网络状态重新调整其状态,并可以跟踪设备位置的变化并做出反应。

后者的一个简单示例是 GPS 应用程序。 我们大多数人不开发这样的应用程序(唉,市场不够大......)但在许多情况下,我们确实部署了与位置相关的逻辑,无论是用户当前位置的简单地图视图,路线跟踪,或位置敏感的数据显示。

测试这种状态敏感条件是出了名的复杂,有时甚至比编写实际代码还要复杂。 如果您有带 SIM 卡的物理设备,您当然可以发出和接听电话和短信。 更改设备的电话状态要困难得多,但仍然可以完成。 更改测试位置可能会比较棘手,但可以选择带着笔记本电脑在城里闲逛……

但是——我们将如何处理模拟器实例? 我们如何测试它们的这些变化?

DDMS 中的设备状态仿真示例

DDMS 再次进行救援。 DDMS 更强大但经常被忽视的特性之一是它能够将模拟事件发布(“欺骗”)到正在运行的模拟器实例中。 DDMS 可以从特定号码向模拟器发出呼叫、发送 SMS、更改电话状态数据等等。

一旦进入仿真器,所有这些欺骗事件将不再与“真实”事件区分开来,即好像被底层硬件传感器接收到一样。 具体来说,您的所有相关应用程序的接收器都将以与接收到真实呼叫/短信时相同的方式激活。

激活电话状态和操作相当简单:

要测试您的应用程序的低网络连接情况(您应该在任何以网络为中心的应用程序中),请转到电话状态部分并将速度和延迟值设置为所需的值。 我通常将 GPRS 值作为模拟低连接性的有效方法,但您可以随意设置自己的值。

要模拟电话或短信,请转到电话操作部分,设置原始电话号码,如果需要添加短信,然后开火。 当您为来自国外的呼叫设置专用代码路由并希望在预算范围内对其进行测试时,此工具特别有效。

当涉及到模拟一个新位置时,事情变得更加有趣。

如果您的目标只是为您的模拟器实例设置一个新位置,请选择手动,设置所需的纬度/经度值,然后点击发送。

用于 DDMS 中的欺骗的手动位置控制

但是,如果不是设置一个固定位置,而是希望您的应用程序通过预先设置的路线——例如,在用户从一个城市到另一个城市旅行时检查其行为,该怎么办? 这样的测试对于任何支持地图的应用程序以及其他根据用户位置设置数据窗口的位置敏感应用程序都具有很大的价值。 在这里,您将希望看到以不同速度移动的位置将使显示的数据窗口保持最新。

为此,我们将使用一种称为 KML 的特殊格式,该格式专为与 Google 地球一起使用而开发,将路线或路径表示为空间中的一组连接点,可以由支持 GPS 的设备使用。

GPX 是 DDMS 支持的另一种路径格式。 出于所有实际目的,当用于移动位置欺骗时,这两者应该被认为是可互换的。

现在让我们来看看在模拟器中设置模拟路由的各个阶段。

  1. 创建路线。 到目前为止,最简单的方法是使用谷歌地图方向选项设置适当的起点和终点。

使用谷歌地图在 DDMS 中进行位置欺骗

  1. 路线显示在地图上后,转到地址行并复制 URL

  2. 使用剪贴板中的 URL,转到 GPS Visualizer,将其粘贴到“提供 URL”文本框中,然后单击转换按钮:

DDMS 位置欺骗:GPS Wisualizer 设置

并单击下载生成的 GPX 文件(名称有点乱,例如 20170520030103-22192-data.gpx)

  1. 回到 DDMS 位置控制,打开 GPX 选项卡,单击加载 GPX 并选择新下载的文件

在 DDMS 位置控制中导入 GPX

  1. 我们完成了! 您现在可以通过单击后退和前进按钮在不同的路线位置之间导航,或者单击播放按钮以设定的速度自动通过路线。

您不需要创建自己的路线。 从 OpenStreetMap 等站点下载大量路线(参见“GPS 轨迹”部分)。

最后,请注意,与较旧的 DDMS 版本不同的是,较旧的 DDMS 版本可以轻而易举地加载路径文件,而较新的版本在加载特定路径时可能需要一些试验和错误。

例如,DDMS 似乎只支持 GPX 1.1。 新的 GPX 版本可能需要一些手动调整。

此外,不再支持 GPX 航路点格式。 相反,使用 GPX Track 格式:

 <trk> <name /> <cmt /> <trkseg> <trkpt lat="27.0512" lon="-80.4324"> <ele>0</ele> <time>2017-02-02T08:01:41Z</time> </trkpt> </trkseg> </trk>

调试 Android:每周一小时会有所作为!

理论够了! 现在是时候进行一些练习了。 我建议,假设您是一名 Android 开发人员,从您的下一个项目开始,您将每周只花一个小时来通过 DDMS 对您的应用程序的性能进行内省。

您会对这将为您提供的大量质量信息(即可用于立即改善您的应用程序状态的信息)感到惊讶!

Android DDMS,正如我在新手开发人员中一次又一次地看到的那样,是一个可以大大提高开发人员能力的工具,只要掌握并正确使用它。 一旦 Android 开发人员充分利用 DDMS 在 Android 开发中的潜力,他们交付一流系统的能力就会提高一两个档次。 因此,留出几个小时用好 DDMS 听起来是一项明智的投资,因为它可以大大提高 Android 的性能和效率。

成为聪明人之一。 用它。