Android帮助文档(第二部分)开发工具
二、Android Development Tools Plugin for the Eclipse IDE. 8
三、Dalvik Debug Monitor Service(ddms) 8
四、Android Debug Bridge (adb) 11
五、Android Asset Packaging Tool (aapt) 14
六、Android Interface Description Language (aidl) 15
Creating Trace Files 建立跟踪文件... 23
Copying Trace Files to a Host Machine 拷贝跟踪文件到主机... 24
Viewing Trace Files in Traceview 使用Traceview查看跟踪文件... 24
Data File Format Data文件格式... 25
Traceview Known Issues Traceview存在问题... 26
Using dmtracedump dmtracedump用法... 27
一、Android Emulator
Android模拟器
Android SDK自带一个移动设备模拟器 — 它是一个可以运行在你电脑上的虚拟设备. Android模 拟器可以让你不需使用物理设备即可预览、开发和测试Android应用程序.
Android模拟器能够模拟除了接听和拨打电话外的所有移动设备上的典型功能和行为. 如右图所示, Android模拟器提供了大量的导航和控制键,你可以通过鼠标或键盘点击这些按键来为你的应用程序产生 事件. 同时它还有一个屏幕用于显示Android自带应用程序和你自己的应用程序.
为了便于模拟和测试应用程序, Android模拟器允许你你的应用程序通过Android平台服务调用其他程 序、访问网络、播放音频和视频、保存和接收数据、通知用户、渲染图像过渡和场景.
Android模拟器同样具有强大的调试能力,例如能够记录内核输出的控制台、模拟程序中断(比如接受 短信或打入电话)、模拟数据通道中的延时效果和遗失。
下面的章节将提供关于模拟器的详细信息,以及如何在开发应用程序中使用模拟器。
http://code.google.com/android/images/emulator-hvga-p.png
启动和关闭模拟器
要启动Android模拟器,首先进入SDK的tools/
文件夹,然后输入 emulator
或 ./emulator
。这个操作将初始化Android系统,你将会在屏幕 上看到模拟器窗口。
要关闭模拟器,只需要关闭模拟器窗口即可。
操作模拟器
你可以通过模拟器的启动选项和控制台命令来控制模拟环境的行为和特性。一旦模拟器启动,你就可以通过键盘和鼠标来"按" 模拟器的按键,从而操作模拟器。
下面的表格总结了模拟器按键可键盘按键之间的映射关系。
模拟器按键 | 键盘按键 |
后退 | ESC |
菜单 | F1 或 PgUp |
开始 | F2 或 PgDn |
呼叫 | F3 |
挂断 | F4 |
--- | F5, F6 未分配 |
电源按键 | F7 |
禁用/启用所有网络 | F8 |
开始跟踪 | F9 (当且仅当有 |
停止跟踪 | F10 (当且仅当有 |
主页 | HOME |
方向键 左/上/右/下 | 小键盘 4/8/6/2 |
方向键 中心建 | 小键盘 5 |
调低音量 | 小键盘 负号(-) |
调高音量 | 小键盘 加号(+) |
模拟器启动选项
Android模拟器提供了很多启动选项,你可以在启动模拟器时指定,来控制其外观和行为。下面是用命 令行的方式启动模拟器并指定参数的语法:
emulator [-option [value]] ... [-qemu args]
下表总结了所有有效的选项。
类型 | 选项 | 描述 | 注释 |
帮助 |
| 以列表的形式打印模拟器的所有命令 | |
数据 |
| 使用当作用户数据的磁盘镜像 | 如果没有 如果使用了 |
| 使用作为RAM镜像 | 默认值为/ramdisk.img | |
| 使用 作为SD卡镜像 | 默认值为/sdcard.img | |
| 启动前清除用户磁盘镜像中的所有数据(参考 | ||
调试 |
| 允许当前中断使用控制台Shell | |
| 将内核输出发送到控制台 | ||
| 允许根据给定的标签为输出分类 | 如果定义了环境变量ANDROID_LOG_TAGS并且不为空, | |
| 允许代码剖析(按F9键开始) | ||
| 允许详细信息输出 | ||
| 允许详细输出按键信息 | ||
媒体 |
| 使用设备或者WAV文件作为音频输出 | |
| 禁用Android的音频支持 | 默认禁用 | |
| 将无线调制解调器接口重定向到主机特征设备 | ||
| 启用Android音频支持 | 默认不启用 | |
网络 |
| 设置网络延迟模拟的延迟时间为. | 默认值是 |
|
| ||
| 设置网速模拟的加速值为. | 默认值为 | |
系统 |
| 使用作为系统镜像 | 默认值为/system.img |
| 使用 作为模拟器内核 | ||
| |||
| 传递qemu参数 | ||
| 显示qemu帮助信息 | ||
| 在目录下查找系统、RAM和用户数据镜像 | ||
UI |
| 在设备皮肤上闪烁按下的键 | |
| 不使用任何模拟器皮肤 | ||
| 在屏幕上使用覆盖图 | 不支持JPEG格式图片,仅支持PNG格式图片 | |
| 指定onion皮肤的半透明值(单位%). | 默认值为50 | |
| 用指定皮肤启动模拟器 | SDK提供了4个可选皮肤: · QVGA-L (320x240, 风景) (默认) · QVGA-P (240x320, 肖像) · HVGA-L (480x320, 风景) · HVGA-P (320x480, 肖像) | |
| 在目录下查找皮肤 |
使用模拟器控制台
每一个运行中的模拟器实例都包括一个控制台,你可以利用控制台动态的查询和控制模拟设备的环境 。例如,你可以利用控制台动态的管理端口映射和网络特性,还可以模拟电话时间。要想进入控制台输入 命令,你需要使用telnet连接到控制台的端口号。
你可以使用下面的命令随时随地连接到任何一个运行中的模拟器实例:
telnet localhost
假设第一个模拟器实例的控制台使用5554端口,下一个实例使用的端口号会加2,比如5556、5558…… 等。你可以在启动模拟器是使用-verbose
选项来检测该模拟器实例使用的端口号,在调试 输出的找到以"emulator console running on port number"这一行。 另外, 你可 以在命令行中使用adb devices
来查看模拟器实例和他们的端口列表。最多可以有16个模拟 器实例同时运行控制台。
注意:模拟器监听端口5554-5587的来自任何电脑的连接。将来发布的版本将只接受本 机的连接,但目前,你需要用防火墙阻断外部对你开发设备的5554-5587这些端口的连接。
一旦连接上控制台, 你可以输入help [command]
来查看命令列表和指定命令的教程。
要离开控制台会话, 使用quit
或 exit
命令。
下面的章节将介绍控制台的主要功能区域。
使用模拟器皮肤
你可以让模拟器使用下表介绍的4种皮肤之一。要想指定皮肤,在启动模拟器是使用-skin
选项。
例如:
emulator -skin HVGA-L
注意: 必须用大写(如果你的开发设备大小敏感)。
皮肤ID | 描述 | 皮肤 |
| 320x240, 横屏 (默认) | |
| 240x320, 竖屏 | |
| 480x320, 横屏 | |
| 320x480, 竖屏 |
运行多个模拟器实例
如果必要的话,你可以同时运行多个模拟器实例。每个模拟器实例使用独立的用户数据内存和不同的 控制台端口。这令你可以独立的管理每一个模拟器实例。
然而,如果你要运行多个模拟器实例,请注意每个实例存储跨会话的持久用户数据的能力—用户 设置和安装的应用程序—会受限制。具体如下:
- 只有第一个模拟器实例能根据会话保存用户数据。默认情况下它把用户数据保存在开发设备 的
~/.android/userdata.img (on Linux and Mac)
或C:/Documents and Settings//Local Settings/Android/userdata.img
(on Windows)文件里。你可以 在启动模拟器时使用-data
选项来控制用户数据的存储(和加载)位置(请参考启动选项)。 - 在第一个实例后启动的模拟器实例(并行的)在会话过程中也保存用户数据;但它们but they 不 为下一个会话保存它。这些实例将数据保存在临时文件中,当实例退出时,相应的临时文件会被删除。
在模拟器上安装应用程序
要想在模拟器上安装应用程序安装,要用到adb工具。
注意:模拟器通过重启保存用户设置和安装的程序。默认情况下,模拟器将数据保存在开发设备的一 个文件里。在Linux和Mac操作系统下,模拟器将用户数据报讯在~/.android/userdata.img
。在Windows下,模拟器将数据保存在C:/Documents and Settings//Local Settings/Android/userdata.img
。模拟器用userdata.img文件的内容作为data/
的 目录。
SD卡模拟
你可以创建磁盘镜像并在模拟器启动时加载它,来模拟设备中用户的SD卡。下面的章节将介绍如何创 建磁盘镜像、如何向磁盘镜像像拷贝文件和如何在模拟器启动时加载镜。
注意:只能在模拟器启动是加载磁盘镜像。同理,模拟器运行时不能移除SD卡。然而,你可以通过adb 或模拟器浏览、发送、拷贝和删除模拟SD卡上的文件。
同时还要注意,模拟SD卡的大小不能超过2GB。
创建磁盘镜像
你可以用SDK中的mksdcard工具来创建可以在模拟器启动时加载的FAT32磁盘镜像。你可以在SDK的 tools/目录下找到mksdcard,用下面的命令船检磁盘镜像:
mksdcard
更多信息,请参考其他工具。
拷贝文件到磁盘镜像
一旦你创建了一个磁盘镜像,你就可以在模拟器加载它之前拷贝文件到镜像中。要拷贝文件,你可以 将镜像加载为循环设备然后向里面拷贝文件,或者你可以使用mtools工具包中的mcopy直接将文件拷贝到 镜像中。mtools包在Linux、Mac和Windows下均可用。
在模拟器启动时加载磁盘镜像
要想在模拟器中加载FAT32格式的磁盘,启动模拟器时带上-sdcard
标记并指定镜像的名 称和路径(相对于当前工作目录):
emulator -sdcard
故障排除
adb工具把模拟器当成是一个真实的物理设备。因此,你需要在使用adb命令--例如 install
--时加上-d标记。-d 标记允许你在众多连接设备中指定使用哪一个设备作为命令 的目标。如果不指定-d,模拟器会选择列表中的第一个设备。向了解更多关于adb的信息,请参考 Android Debug Bridge。
对于运行在Mac OS X上的模拟器,如果你在启动模拟器时遇到"Warning: No DNS servers found"错误,请查/etc/resolv.conf文件是否存在。如果不存在,请在命令窗口中运行下面的命令 :
ln -s /private/var/run/resolv.conf /etc/resolv.conf
请参考常见问题回答获得更多故障 排除信息。
模拟器的限制
这一版的模拟器存在如下限制:
- 不支持呼叫和接听实际来电;但可以通过控制台模拟电话呼叫(呼入和呼出)
- 不支持USB连接
- 不支持相机/视频捕捉
- 不支持音频输入(捕捉);但支持输出(重放)
- 不支持扩展耳机
- 不能确定连接状态
- 不能确定电池电量水平和交流充电状态
- 不能确定SD卡的插入/弹出
- 不支持蓝牙
二、Android Development Tools Plugin for the Eclipse IDE
用于Eclipse集成开发环境的Android应用开发工具插件:它为Eclipse集成开发环境增加了强大的功能,使得创建和调试Android应用程序更加简单和快速。如果你使用Eclipse来开发Android应用,ADT插件将给你带来极大的帮助:
- 可以从Eclipse集成开发环境内部访问别的Android开发工具。例如,ADT允许你直接从Eclipse访问DDMS工具的很多功能,包括截屏、管理端口转发(port-forwarding)、设置断点、查看线程和进程信息。
- 它提供一个新的项目向导,用于快速创建一个新的Android应用需要的所有基本文件。
- 它使构建Android应用的过程自动化和简单化。
- 它提供一个Android代码编辑器,用于为Android的manifest和资源文件编写有效的XML。
更多的关于ADT插件的信息,包括安装指令,请参见Installing the ADT Plugin for Eclipse。Hello Android描述了一个有用的例子程序和程序的效果图。
三、Dalvik Debug Monitor Service(ddms)
Dalvik调试监视服务:它集成在Dalvik(Android平台的虚拟机)中,用于管理运行在模拟器或设备上的进程,并协助进行调试。你可以用它来杀死进程、选择一个特定程序来调试、生成跟踪数据、查看堆和线程数据、对模拟器或设备进行屏幕快照等等。
使用Dalvik调适监视器服务工具
Android附带了一个调试工具, 叫做Dalvik 调适监视器服务(DDMS), 提供了端口转发服务, 手机设备屏幕截图, 设备上的线程和堆栈信息, 日志, 进程, 射频状态信息, 以及更
多的功能. DDMS 位于SDK主目录下的tools目录中; 在该目录的命令控制台输入ddms(小写)即可运行DDMS. DDMS可以配合模拟器或设备使用; 如果模拟器和设别同时被连接并且同时
在运行, DDMS默认会连接模拟器. 该文档对DDMS的功能做了简要的介绍; 这里没有对DDMS的特性和功能做过与详细的解释.
你可以对DDMS进行一些设置, 点击菜单中的File->Preferences即可开始设置, 设置文件存放在"$HOME/.ddmsrc"目录下.
左下角面板
左侧窗口中的列表显示了DDMS找到的设备或模拟器中的所有虚拟机(VM). 虚拟机用它的应用的包名来标识; 如果有多个Activities 在一个包内, 他们会在同一个虚拟机内运行. 利
用该列表找到你想调试的activity, 在该列表中, 每个虚拟机的前面会显示"调试端口"的端口号; 如果你使用调试器链接这些端口, 就相当于链接了该设备中相应的虚拟机. 一个
不错的特性是, Dalvik 还为当前被选择的虚拟机分配了一个附加端口号, 8700. 这个默认的"当前选择"端口可以让你在切换应用的时候不必重新配置调试端口(就是说调试器链接
8700, 之后不管你切换到哪个虚拟机, 8700都是你当前选择的虚拟机的调试口). 当一个运行在虚拟机中的应用执行了waitForDebugger()(或者你在手机的developer选项里面选择
了这项), 一个红色的图标会在客户端的名字后面显示, 当一个调试器链接上之后, 这个图标会变成绿色.
DDMS的一些标签页显示了当前需选择的虚拟机的有用信息:
信息标签页
这个标签页显示了所选虚拟机的常用信息, 包括进程的ID, DDMS链接的端口号. 这个不是您在IDE里面配置的那个端口号.
另外, 在虚拟机的列表页面的中, 当前所选的虚拟机也会同样将端口信息转发到8700端口, 使用这个"当前选择"端口, 避免您在调适多个Activities时重新配置eclipse的端口信息
.(该"当前选择"端口号可以在"preferences"对话框中设置.)
线程标签页
线程标签页显示了在目标虚拟机中当前进程中的所有线程信息. 为了减少数据传输, 仅仅在工具栏的"threads"被按下时设备才会发送线程的信息. 每一个虚拟机都有一个按钮开关
, 该标签页显示了如下信息:
· ID - 虚拟机分配的唯一线程ID. 在 Dalvik, 该数字是一个从3开始的奇数.
· Tid - Linux 线程 ID. 进程中主线程的ID, 会同进程的ID相匹配.
· 状态 - 虚拟机线程状态. 守护进程会附带一个'*'. 状态信息列表如下:
· running - 正在执行应用程序
· sleeping - 执行了Thread.sleep() 方法
· monitor - 在正等待获取一个监听锁
· wait - 在Object.wait() 方法中
· native - 执行了原生代码
· vmwait - 正在等待一个虚拟机资源
· zombie - 该线程已死
· init - 线程正在初始化 (你不会看到这个)
· starting - 线程正在启动中 (这个你也不会看到)
· utime - 执行用户代码的累计时间, 单位为"jiffies(表示系统启动以来的tick数)" (通常是 10ms). 仅在Linux系统中适用.
· stime - 执行系统代码的累计时间, 单位为"jiffies(表示系统启动以来的tick数)".
· 名字 - 线程的名字
"ID" 和 "Name" 在进程启动的时候就会显示. 其余的字段每个一段时间更新一次(默认是4秒钟)
堆标签页
显示内存堆的状态信息, 每次垃圾回收的时候更新.
端口转发(调试器链接到设备)
DDMS扮演了IDE和设备上运行的应用的中间人角色. Android持有多个进程, 每一个进程都有自己的虚拟机. 每一个应用运行在自己的进程中. 每个进程都有一个唯一的调试监听端口: 第一个进程的调试监听端口是8000, 下一个是8001, 依次向下. DDMS默认情况下会扫描设备上的8000-8019端口, 并且每两秒扫描一次, 以获取新的进程信息(你可以在DDMS的配置页改变这些信息). 当DDMS检测到一个虚拟, 他会链接到调试端口并且开始发送请求信息. DDMS和Dalvik 通过一个自定义的有线通信协议来相互联系. 链接建立之后, 他就开始把信息转发到你的IDE能够接受的端口, 以显示出来, 你的IDE会使用8700端口来进行调试.
调试Dalvik时会出现的已知问题
当single-stepping 与代码不同步的时候, 点击下一个step之后, 显示当前行的光标可能会直接跳到方法的最后一行.
截图
您可以截取设备或虚拟机屏幕, 选择主菜单中的Device > Screen capture.
进程信息查看
你可以查看指定虚拟机中 ps -x命令的输出, 在主菜单选择Device > Show process status.
手动垃圾回收
点击工具条的GC按钮, 会引发一个垃圾回收操作.
在设备上执行 Dumpsys 和 Dumpstate (logcat)
执行Dalvik的dumpsys(logcat)命令, 选择主菜单的Device > Run logcat...
执行Dalvik的dumpstate命令, 选择主菜单的Device > Dump device state...
检查射频状态
默认情况下, 射频的状态信息是不会输出的(因为这个信息非常多). 若想查看该信息, 点击Device > Dump radio state... 或是像记录射频日志中描述那样执行logcat.
停止虚拟机
你可以通过选择菜单中的Actions > Halt VM来停止一个虚拟机, 点击该按钮相当于让虚拟机执行System.exit(1).
DDMS的已知问题
DDMS目前有下列已知问题:
· 如果你链接和断开一个调试器, ddms 断开和重连客户端时, 虚拟机会认为调试器已经失去连接了. 这个问题最终会解决.
· 不要尝试让ddms链接一个运行在Sun Java 桌面虚拟机, 这样会引起崩溃.
·
四、Android Debug Bridge (adb)
Android调试桥:它用于向模拟器或设备安装应用程序的.apk文件和从命令行访问模拟器或设备。也可以用于将标准的调试器连接到运行在Android模拟器或设备上的应用代码。
Android Debug Bridge
Android Debug Bridge (adb)是通用的debug工具,使你管理设备或者仿真器的状态.它包括运行在后台预定程序下做为连接主机,仿真器,其他设备之间端口的一个 daemon. 与命令行接口一样通过控制daemon.仿真器和其它设备.在其他使用当中,adb使你能够:
快速更新设备或者仿真器代码,例如应用程序或Android系统程序的更新
在设备上运行shell命令
在仿真器或设备上面管理预定端口
仿真器或设备上同步文件
以上部分定义在adb使用命令当中
启动和终止adb系统
使用adb命令来启动.daemon启动自动使用默认端口5037.注意:当你运行多个仿真器的时候,adb只能连接第一个启动的实例. 如何要终止adb,使用命令
adb kill-server
安装应用程序
从主机程序(.apk)安装到仿真器或者设备,使用命令:
adb install
打印内核信息
打印内核信息命令:
adb shell dmesg
发布shell命令
可以用adb进入设备或者仿真器的shell.使用命令:
adb shell
CTRL+D或者exit命令退出.
对于单行shell命令,可以快速的执行:
adb shell [command]
端口转发
可以使用adb设置访问主机的端口,这些特殊的端口会转发给仿真器或设备上. 例如主机端口5555 转发给 仿真器/设备上8000端口的命令:
adb forward tcp:5555 tcp:8000
同样,可以用adb设置UNIX domain sockets抽象名称转发:
adb forward tcp:5556 local:logd
复制文件
可以复制道设备上面用文件,用adb命令.也可以复制文件或者目录到仿真器或者设备上面,使用:
adb push
也可以从仿真器或设备上面得到文件或者目录,使用
adb pull
下面是简单的例子:
adb push foo.txt /tmp/foo.txt
adb pull /android/lib/libwebcore.so .
询问设备状态/建立辅助设备和仿真器列表
要得到运行在当前仿真器/设备实例状态列表,命令:
adb devices
adb返回状态,类型和数量.如果没有运行仿真器/设备,adb返回结果会提示没有找到当前设备.
adb返回状态列表:
|id|serial #|type|lock|
where type can be either device (connected via adb) or offline (not connected via adb). 例如adb的输出:
$ adb devices
List of devices attached
6 emulator-tcp-5555 device 0
获得Debug信息
你可以运行debug命令清除系统和清除状态在adb shell.我们建议使用Dalvik Debug Monitor Server (DDMS) debug工具来直接清除adb shell下面的系统和状态.运行DDMS并且从设备菜单里选择清除设备状态来显示状态信息。从DDMS设备菜单里检索清楚系统信息,选择"Run logcat"。下一步,退出adb shell下清除系统命令。信息将显示在Dalvik窗口下。
创建射频日志
默认的射频信息没有日志,所有想要记录射频信息的话,要如下操作:
adb shell
logcat -b radio
等待设备状态改变
你可以运行命令去等候连接正在运行的设备:
adb wait-for-device
获取id或者序列号
如下命令可以得到设备的ID或者序列号.
adb get-product
adb get-serialno
检查sqlite数据库
另外的,Android SDK包括sqlite3,用于管理SQLite数据库命令命令行程序(例如通过Android程序来创建).sql3 工具包括大量有用的命令,例如 .dump 打印表格目录. .schema 打印SQL 以有表的创建状态.这个工具也可以用在移动设备上执行命令.
启动sqlite3,进入adb shell和sqlite3类型,遵循你想要的数据库的全路径名.在仿真器或者设备上的数据库存储目录 /data/data//databases/.
五、Android Asset Packaging Tool (aapt)
Android资源打包工具:你可以通过aapt工具来创建.apk文件,这些文件包含了Android应用程序的二进制文件和资源文件。
使用aapt
aapt即Android Asset Packaging Tool , 在SDK的tools/目录下. 该工具可以查看, 创建, 更新ZIP格式的文档附件(zip, jar, apk). 也可将资源文件编译成二进制文件.
尽管你可能没有直接使用过aapt工具, 但是build scripts和IDE插件会使用这个工具打包apk文件构成一个Android 应用程序.
获取更多的实用信息, 请打开终端控制台, 到tools/目录下, 执行命令:
· Linux or Mac OS X: ./aapt
· Windows: aapt.exe
六、Android Interface Description Language (aidl)
Android接口描述语言:它用来生成进程间接口代码。例如,在一个服务中可能就会用到。
使用AIDL(AndRoid接口描述语言)设计和使用远程接口
通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的 service。在AndRoid平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。
编写这种伪装代码相当的枯燥乏味,好在我们提供了AIDL工具可以来做这件事。
AIDL(AndRoid接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个AndRoid设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如:在一个Activity中)访问另一个进程中(例如:一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。
AIDL IPC的机制是基于接口的,和COM或Corba类似,但它是轻量级的。它使用代理类在客户端和实现层间传递值。
本页包含以下主题:
使用AIDL实现IPC
调用一个AIDL(IPC)类
使用AIDL实现IPC
使用AIDL实现一个IPC有下列步骤:
1、创建你的AIDL文件 - 这个文件定义一个接口(YourInterface.aidl),该接口定义了可供客户端访问的方法和属性。
2、添加AIDL文件到你的makefile中-(Eclipse plugin可以帮你管理)。AndRoid包括编译器,AIDL调用,这些都能在tools/directory中找到。
3、实现接口方法-AIDL编译器从你的AIDL接口中使用JAVA编程语言来创建一个接口。这个接口有一个名为Stub的内部抽象类,它继承接口(并实现供IPC调用的所必需的几个附加方法)。你必须创建一个类来实现该接口。
4、向客户端开放接口-如果你写个service,你应该扩展该Service并重载getBinder()方法来返回一个实现上述接口的类的实例。
创建一个AIDL文件
AIDL语法简单,你可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。这些参数和返回值可以是任何类型,甚至是其他的AIDL生成的接口。然而,值得重视的是你必须导入所有的non-bult-in类型,即使他们已经作为接口在其他包里定义了。下面是些AIDL支持的数据类型:
简单Java编程语言类型(int,boolean等) -不需要import声明。
下面类之一(不需要import声明)。
.String
.List - All elements in the List must be one of the types in this list, including other AIDL-generated interfaces
and parcelables. List may optionally be used as a "generic" class (e.g. List). The actual concrete class
that the other side will receive will always be an ArrayList, although the method will be generated to use the
List interface.
.List - List中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。List可以作为泛型类来灵活使用(比如
List)。而实际的接受方的类则总是ArrayList,尽管该方法将被生成来使用List接口。
.Map - All elements in the Map must be of one of the types in this list, including other AIDL-generated interfaces
and parcelables. Generic maps, (e.g. of the form Map are not supported. The actual concrete class
that the other side will receive will always be a HashMap,although the method will be generated to use the Map interface.
.Map - Map中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。泛型化的Maps(比如:Map)不被支持。
而实际的接受方的类则总是HashMap,尽管该方法将被生成去使用Map接口。
.CharSequence - This is useful for the CharSequence types used by TextView and other widget objects.
.CharSequence - CharSequence的作用是可以被TextView和其他Widget对象使用。
其他的AIDL生成接口通过引用方式进行传递。所以import声明是必须的。封装协议实现的自定义的类是值传递的方式。所以import声明也是必须的。
下面是基本的AIDL语法:
// My AIDL file, named SomeClass.aidl
// Note that standard comment syntax is respected.
// Comments before the import or package statements are not bubbled up
// to the generated interface, but comments above interface/method/field
// declarations are added to the generated interface.
// Include your fully-qualified package statement.
package com.google.android.sample;
// See the list above for which classes need
// import statements (hint--most of them)
import com.google.android.sample.IAtmService;
// Declare the interface.
interface IBankAccountService {
// Methods can take 0 or more parameters, and
// return a value or void.
int getAccountBalance();
void setOwnerNames(in List names);
// Methods can even take other AIDL-defined parameters.
BankAccount createAccount(in String name, int startingDeposit, in IAtmService atmService);
// All non-Java primitive parameters (e.g., int, bool, etc) require
// a directional tag indicating which way the data will go. Available
// values are in, out, inout. (Primitives are in by default, and cannot be otherwise).
// Limit the direction to what is truly needed, because marshalling parameters
// is expensive.
int getCustomerList(in String branch, out String[] customerList);
}
实现接口
AIDL生成一个接口文件,文件名和你的AIDL文件名一致。如果你使用的是Eclipse插件,AIDL会作为build过程的一部分自动运行(你不需要首先运行ADIL然后再去创建你的项目)。否则的话,你需要首先运行AIDL。
生成的接口包括一个名为Stub的内部抽象类,该类声明了你在aidl文件中声明的所有方法。Stub也定义几个有用的方法,最特别的是asInterface(),它执行一个IBinder(在 applicationContext.bindService()执行成功后传给客户端onServiceConnected()方法),并返回一个用来调用IPC方法的接口实例。更多细节请查看章节调用IPC方法。
实现接口,扩展YourInterface.Stub,并实现方法成员。(你可以创建一个aidl文件并实现stub方法而不用绑定-AndRoid创建过程在java文件之前会处理aidl文件)。
这里有个例子,它实现了一个调用IRemoteService的接口,并使用匿名实例公开一个简单的方法gerPid():
// No need to import IRemoteService if it's in the same project.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
public int getPid(){
return Process.myPid();
}
}
实现接口时有几个原则:
.抛出的异常不要返回给调用者。
.IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity/View线程中调用。
因为它会挂起应用程序(AndRoid可能会显示"应用程序没有响应"对话框)。试着在一个独立的线程中调用。
.只有方法才获得支持;你不能在AIDL接口中声明静态属性。
向客户端公开接口
现在你已完成了接口的实现,你需要向客户端公开该实现。这就是我们所熟悉的"发布服务"。发布一个Service,然后继承 Service并实现getBinder()返回一个实现的类的实例。下面是个Service的代码片断,该Service向客户端公了 IRemoteService接口。
public class RemoteService extends Service {
...
@Override
public IBinder getBinder() {
return mBinder;
}
/**
* The IRemoteInterface is defined through IDL
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid() {
return Process.myPid();
}
};
}
使用parcelables进行参数的值传递
警告:如果你现在使用Eclipse插件,Parcelables并不能工作。你会看到以下的错误信息:
.仅声明parcelables的.aidl文件不需要写进makefile
.aidl只能生成接口代码,而不是parcelables。
这是个众所周知的局限。Parcelables仍然可以被ant build的xml文件或自定义的build系统所使用。你应该在Eclipse项目中添加一个工作区,该工作区可以为所有的接口手动运行aidl工具。下面的步骤5说明为何Eclipse不该尝试编译这些aidl文件。
如果你有类需要通过AIDL接口从一个进程发送到另一个,你必须确保类代码可以被IPC接收端所使用。通常这意味着一开始你就要和service进行通讯。
让类支持parcelable协议,有五点需要注意
1. 让类实现Parcelable接口。
2. 实现public void writeToParcel(Parcel out),该方法可以将当前对象的状态写入parcel.
3. 实现public void readFromParcel(Parcel in),该方法可以在parcel中读出值到对象中.
4. 向类中添加一个静态成员,名为CREATOR。该对象实现了Parcelable.Creator接口.
5. 向parcelable类中添加一个.aidl文件,以便AIDl工具可以找到。但不要向build中添加该文件。该文件的用法类似于C中的头文件.你不需要为parcelable
编译aidl文件,就像你不会编译个.h文件一样。
AIDL将使用代码中生成的这些方法和成员来伪装或解读对象。
下面的例子说明了Rect类如何实现了Parcelable协议.
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator CREATOR = new Parcelable.Creator {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() { }
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
示例的Rect.aidl
Rect类中的伪装是相当简单的。仔细看看Parcel中的其他方法,你会看到其他各种值你都可以写进Parcel.
警告:不要忽视从其他进程接收数据时的安全性考虑。在本例中,rect将从parcel中读四个数字,而你的工作则是确保这些都在可接受的值得范围内而不管调用者想要干什么。AndRoid中的安全和访问许可中有更多关于如何确保应用程序安全的信息。
调用一个IPC方法
Here are the steps a calling class should make to call your remote interface:
调用类调用远程接口的步骤:
1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
2. 实现ServiceConnection。
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
4. 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用
YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
5. 调用接口中定义的方法。 你总会捕捉到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
6. 断开连接,调用接口实例中的ApplicationContext.unbindService()
调用IPC服务需要注意几点:
.Objects are reference counted across processes.
.You can send anonymous objects as method arguments.
.匿名对象可以通过方法参数发送。
下面的代码展示了在ApiDemos项目从远程Activity例子中调用AIDL创建Service的过程。
public class RemoteServiceBinding extends Activity{
IRemoteService mService = null;
Button mKillButton;
private boolean mIsBound;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill);
mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface((IBinder)service);
mKillButton.setEnabled(true);
// As part of the sample, tell the user what happened.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_service_connected,
getText(R.string.remote_service_connected),
NotificationManager.LENGTH_SHORT,
null);
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
// As part of the sample, tell the user what happened.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_service_disconnected,
getText(R.string.remote_service_disconnected),
NotificationManager.LENGTH_SHORT,
null);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a connection with the service, by its class name.
bindService(new Intent(RemoteServiceBinding.this,
RemoteService.class),
null, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mKillButton.setEnabled(false);
mIsBound = false;
}
}
};
private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mService != null) {
int pid = -1;
try {
pid = mService.getPid();
} catch (DeadObjectException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_call_failed,
getText(R.string.remote_call_failed),
NotificationManager.LENGTH_SHORT,
null);
}
if (pid > 0) {
// Go away!
Process.killProcess(pid);
}
}
}
};
}
七、sqlite3
SQLite3数据库:Android应用程序可以创建和使用SQLite数据文件,而开发者和使用者也可以方便的访问这些SQLite数据文件。
八、traceview
Creating Trace Files 建立跟踪文件
要使用Traceview进行分析,首先必须要生成你想要分析的跟踪信息日志文件。你可以这样生成跟踪信息日志文件,在你的代码中直接调用 Debug类的方法来记录跟踪信息并输出到日志文件中。当应用程序退出后,你就可以使用Traceview来对日志文件进行分析程序运行时的方法调用及运行次数等情况了。
为了生成跟踪日志文件,你应该在程序中包含Debug类并调用startMethodTracing()方法来开始进行日志跟踪,在这个调用方法中,你应该为跟踪日志文件指定一个名字。调用stopMethodTracing()方法来停止跟踪。这些方法可以在虚拟机上任意开始和停止方法跟踪。比如:你在activity's onCreate()方法中调用startMethodTracing()开始跟踪,并在activity's onDestroy()方法中调用stopMethodTracing()来结束跟踪。
// start tracing to "/tmp/calc"
Debug.startMethodTracing("/tmp/calc");
// ...
// stop tracing
Debug.stopMethodTracing();
当你调用startMethodTracing(),系统会生成以下两个文件:
- .data --方法跟踪数据的二进制文件.
- .key --一个映射二进制文件中线程和方法名的纯文本文件.
跟踪开始后,系统对生成的跟踪数据是先进行缓存的,直到程序调用了stopMethodTracing()方法时才把缓存的数据写到文件中。如果在调用stopMethodTracing()前,系统已经达到了缓存最大值时,则系统就停止跟踪并发一个通知到控制台。
跟踪文件的格式下面会详细进行说明.
Copying Trace Files to a Host Machine 拷贝跟踪文件到主机
当程序运行并在手机设备或者模拟器上生成了跟踪文件.data 和 .key后,你必须把这些文件拷贝到开发环境的主机上。使用adp命令进行拷贝,以下例子说明如何从模拟器默认位置拷贝calc.data 和 calc.key到模拟器的主机的/tmp目录下的。
adb pull /tmp/calc.data /tmp
adb pull /tmp/calc.key /tmp
Viewing Trace Files in Traceview 使用Traceview查看跟踪文件
输入traceview 命令运行traceview工具来查看跟踪文件。例如:通过Traceview工具运行上一节中用到的例子文件,你会看到:
traceview /tmp/calc
Traceview载入日志文件并显示数据在两个面板上:
- 时间轴面板—描述每个线程和方法的开始和终止。
- Profile面板—提供一个方法中发生了什么的摘要。
以下部分提供traceview输出面板的详细信息。
Timeline Panel
下图显示了时间轴面板的一个结果。每个线程的执行都显示在随着时间渐增右移的各自行上。不同的方法用不同的颜色来表示。第一行下面的细线显示选中方法的调用时长(由进入到退出)。本例中的方法是LoadListener.nativeFinished(),它是在profile面板中选中的。
Profile Panel
下图是profile面板,面板显示了每一个方法的所花费时间的概要.包括inclusive和exclusive时间(同时用百分比表示)。 Exclusive时间:方法执行所花费的时间。Inclusive时间: 方法执行所花费的时间+方法调用所花费的时间。通常调用方法为“父节点”,被调用方法为“子节点”。当一个方法被选中(单击),它就展开并显示父子节点,背景色为紫色的是父节点, 背景色为黄色的是子节点。表中最后一栏显示的是调用这个方法的次数+递归调用的次数。同时也表示的是调用次数/总调用次数。下图中,我们可以看出 LoadListener.nativeFinished()调用了14次。此时看一下时间轴面板,面板显示表明每次调用都花费了很长的时间。
Data File Format Data文件格式
The data file is a binary file with the extension .data. It is structured as follows (all values are stored in little-endian order):
Data文件是以扩展名.data的二进制文件。它的结构如下:
* File format:
* header
* record 0
* record 1
* ...
*
* Header format:
* u4 magic 0x574f4c53 ('SLOW')
* u2 version
* u2 offset to data
* u8 start date/time in usec
*
* Record format:
* u1 thread ID
* u4 method ID | method action
* u4 time delta since start, in usec
程序从文件开始解析header字段,并查找“偏移数据”,每次只读9-byte,直到EOF结束。以u8开始表示来表示输出的日期/时间,这样,你可以知道是昨天还是三天以前输出的了。方法动作用2个字节来表示,定义如下:
- 0 - 表示进行
- 1 - 表示退出
- 2 - 表示异常退出
- 3 - (保留)
32位的无符号整数可以表示70分钟以微秒为单位的时长.
Key File Format Key文件格式
Key文件是一个由三部分组成的纯文本文件,每一部分用关键字‘*’作为开始,如果你看到某一行以‘*’开始,则表示这是新部分的开始。
文件可能如下:
*version
1
clock=global
*threads
1 main
6 JDWP Handler
5 Async GC
4 Reference Handler
3 Finalizer
2 Signal Handler
*methods
0x080f23f8 java/io/PrintStream write ([BII)V
0x080f25d4 java/io/PrintStream print (Ljava/lang/String;)V
0x080f27f4 java/io/PrintStream println (Ljava/lang/String;)V
0x080da620 java/lang/RuntimeException ()V
[...]
0x080f630c android/os/Debug startMethodTracing ()V
0x080f6350 android/os/Debug startMethodTracing (Ljava/lang/String;Ljava/lang/String;I)V
*end
版本部分
第一行是文件版本号,通常是1,第二行clock=global描述所有线程共用的时钟.以后版本可能会用每个独立线程的CPU时钟来表示。
线程部分
每个线程一行,每行包括两部分:线程ID ,一个tab,线程名。线程名没有限制,到这行结尾都是线程名部分。
方法部分
每行表示一个方法,一行由四部分组成,用tab标识进行分隔:方法ID [TAB]类名[TAB] 方法名[TAB]信号 。无论是方法进入还是退出都会记录在这个列表上,注意:三个标识符是必须的,它唯一表示了方法.
除线程、方法部分外的都是类别部分。
Traceview Known Issues Traceview存在问题
Traceview日志对线程处理得不是很好,存在以下2个问题:
- 如果一个线程存在于作分析图期间,这个线程是不会被发表的。
- 虚拟机上线程ID重用问题,如果一个线程停止后另一个线程开始,它们可能使用了同一个线程ID。
Using dmtracedump dmtracedump用法
Android SDK有一个dmtracedump工具,这个工具可以让从跟踪日志中生图形化的方法调用图和堆栈图。工具使用Graphviz Dot组件来生成图形的,所以要运行dmtracedump就必须先安装Graphviz。
Dmtracedump用树状图来表示堆栈数据,每一个数据用一个节点来表示。用箭头来表示(从父节点到子节点的)调用。下图显示了dmtracedump输出的一个例子。
对于每个节点,dmtracedump显示格式: callname (, ,),其中:
- -- 编号
- -- Inclusive时间总和(微秒为单位,包括子方法时间)
- -- Exclusive时间总和(微秒为单位,不包括子方法时间)
- -- 调用次数
dmtracedump用法:
dmtracedump [-ho] [-s sortable] [-d trace-base-name] [-g outfile]
这个工具从.data and .key加载日志数据。下表描述dmtracedump的选项列表:
Option | Description |
-d | Diff with this trace name |
-g | Generate output to |
-h | Turn on HTML output |
-o | Dump the trace file instead of profiling |
-d | URL base to the location of the sortable javascript file |
-t | Minimum threshold for including child nodes in the graph (child's inclusive time as a percentage of parent inclusive time). If this option is not used, the default threshold is 20%. |
九、mksdcard
(xing:因为google放出了video的demo,很多人开始查sd卡的用法,所以抽时间把这个翻译了一下。keywords:android create sdcard)
mksdcard工具是用来创建虚拟的SD卡映像的,它创建SD卡是FAT32格式。创建好的SD卡映像可以被载入模拟器,如同使用一个真正的SD设备。下面是它的用法:
mksdcard [-l label] [K|M]
下面的表格列出了mksdcard所有的选项和参数
参数 | 注释 |
-l | 为SD卡创建一个卷标。 |
size | 用一个整数来设定SD卡的大小。缺省单位是byte,可以使用大写的"K"和"M"跟在数值后面改变这个单位,如 1048576K, 1024M(xing:有网友建议不要设置得太小,不然模拟器可能会崩溃。而且命令有提示,模拟器不能用8M的卡。我现在用32M没有任何问题。要注意一点,一旦生成映像,所有的空间都会被分配,就是说如果你使用了1024M作为参数,你的硬盘上就会出现一个1G的文件。) |
file | 映像的文件名。比如sdcard.img。 |
例:mksdcard -l mycard 32M mycard.img
创建了SD映像之后就可以在模拟器的启动参数里面加入-sdcard来载入它。更多信息参见[Android Emulator].
emulator -sdcard
(xing:文件名最好使用全路径,尤其是在eclipse里面,理论上将sdcard.img放在下是可以载入的,但事实测的时候并没有成功。)
附:如何将文件放到SD卡中
如果你使用eclipse,那就再简单不过了。首先在run dialog里面为emulator增加启动参数-sdcard ,模拟器启动后,在ddms里面就可以看到sdcard这个目录了,然后使用文件传送按钮就可以把文件传到SD卡中。
使用命令行也不麻烦,首先还是要有启动参数,然后使用
adb push
就可以将本地文件发送到模拟器,例如:
adb push temp.img /sdcard/audio
(xing:这个audio目录是使用adb shell创建的,好像在播放视频的时候,模拟器会自己创建video目录。)
十、dx
The dx tool lets you generate Android bytecode from .class files. The tool converts target files and/or directories to Dalvik executable format (.dex) files, so that they can run in the Android environment. It can also dump the class files in a human-readable format and run a target unit test. You can get the usage and options for this tool by using dx -help.
十一、activityCreator
If you aren't using the Eclipse IDE and ADT plugin, you can use the the activityCreator script to get started with a new application. When you run the script, it creates the structure of a minimal Android application that you can build on and extend to meet your needs.
For Linux and Mac, the SDK provides activityCreator.py, a Python script, and for Windows activityCreator.bat, a batch script that runs an executable. Regardless of platform, the usage for the script is the same:
activityCreator [--out ] [--ide intellij] your.package.name.ActivityName
Option | Description |
--out | Specifies where to create the files/folders. |
--ide intellij | Creates project files for IntelliJ |
When run, the script creates these files:
- AndroidManifest.xml -- The application manifest file.
- build.xml -- An Ant script to build/package the application.
- res -- The resource directory.
- src -- The source directory.
- src/your/package/name/ActivityName.java -- The Activity class.
- bin -- The output folder for the compiled .apk (when built by Ant).
When you are ready, you can use Ant to build the project so that you can run it on the emulator.
If you are using Eclipse with the ADT plugin, you do not need to use activityCreator. You can use the New Project Wizard, provided by the ADT plugin, instead.
十二、Application的生命周期
在多数情况下, 每个Android应用运行在自己的Linux进程中. 当一个应用的某段code需要运行的时候这个进程将会被创建, 直到不再需要该应用或系统要为其他的应用释放内存的时候才停止.
一个非常重要且少有的特性是, 应用进程的存活时间不是由这个应用直接控制的. 而是由系统决定的, 系统会根据每个已知的正在运行的应用情况来定夺, 包括, 该应用对用户的重要性和系统全部可用内存.
对于开发人员来讲, 了解每个应用组件(尤其是, Activity, Service, 和IntentReceiver)对于应用进程存活时间的影响是非常重要的. 如果没有正确使用, 可能会导致应用进程在处理重要工作的时候被系统杀掉.
在对应用进程生命周期的理解中, 一个典型的错误就是当一个IntentReceiver 接收到Intent 之后, 会在自己的onReceiveIntent()方法中开起一个线程, 而后return这个方法. 一旦这个方法return, 系统会认为这个IntentReceiver 不在处于活跃状态, 也就认为他的宿主进程不再需要(除非还包有其他活跃的应用组件). 以至于当系统需要回收内存的时候会随时释kill掉这个进程, 中止其中的子线程. 解决这个问题的办法是在IntentReceiver中启动一个Service, 这样系统会知道在这个进程中还有活跃的任务需要完成.
为了决定在内存较低的时候杀掉哪个进程, Android会根据运行在这些进程内的组件及他们的状态把进程划分成一个"重要程度层次". 其重要的程度按以下规则排序:
1. 前端进程可以是一个持有运行在屏幕最前端并与用户交互的Activity的进程(onResume方法被调用时),也可以是持有一个正在运行的IntentReceiver(也就是说他正在执行自己的onReceiveIntent方法)的进程. 在系统中, 只会有少数这样的进程, 并且除非内存已经低到不够这些进程运行, 否则系统不会主动杀掉这些进程. 这时, 设备通常已经达到了需要内存整理的状态, 所以杀掉这些进程是为了不让用户界面停止响应.
1. 可视进程是持有一个被用户可见, 但没有显示在最前端 (onPause方法被调用时) 的Activity的进程. 举例来说, 这种进程通常出现在一个前端Activity以一个对话框出现并保持前一个Activity可见时. 这种进程被系统认为是极其重要的, 并且通常不会被杀掉, 除非为了保持所有前端进程正常运行不得不杀掉这些可见进程.
1. 服务进程是持有一个Service的进程, 该Service是由startService()方法启动的, 尽管这些进程用户不能直接看到, 但是通常他们做的工作用户是十分关注的(例如, 在后台播放mp3或是在后台下载 上传文件), 所以, 除非为了保持所有的前端进程和可视进程正常运行外, 系统是不会杀掉服务进程的.
1. 后台进程是持有一个不再被用户可见的Activity(onStop()方法被调用时)的进程. 这些进程不会直接影响用户体验. 加入这些进程已经完整的,正确的完成了自己的生命周期(访问Activity查看更多细节), 系统会在为前三种进程释放内存时随时杀掉这些后台进程. 通常会有很多的后台进程在运行, 所以这些进程被存放在一个LRU列表中, 以保证在低内存的时候, 最近一个被用户看到的进程会被最后杀掉.
1. 空进程是没有持有任何活动应用组件的进程. 保留这种进程的唯一理由是为了提供一种缓存机制, 缩短他的应用下次运行时的启动时间. 就其本身而言, 系统杀掉这些进程的目的是为了在这些空进程和底层的核心缓存之间平衡整个系统的资源.
当需要给一个进程分类的时候, 系统会在该进程中处于活动状态的所有组件里掉选一个重要等级最高作为分类依据. 查看Activity, Service,和IntentReceiver的文档, 了解每个组件在进程整个生命周期中的贡献. 每一个classes的文档详细描述他们在各自应用的生命周期中所起得作用.