2008年9月28日星期日

Android开发 第一章

第一章 什么是Android

Android是移动设备的软件栈,包括操作系统、中间件和关键应用,Android SDK为在Android上开发应用提供了工具和API接口,它使用Java作为编程语言。

特性:

1. 应用框架保证了组件的可重用性和可替换性。 2. Dalvik虚拟机为移动设备做了优化。 3. 集成的浏览器基于开源的Webkit引擎。 4. 客户化的2D图形库优化了图形处理,具有基于OpenGL ES1.0规格的3D图形库(硬件加速可选)。 5. SQLite为结构化的数据存储提供了保障。 6. 丰富的多媒体支持,包括公共的声音、视频和静态图像(MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)。 7. GSM通话(需要硬件支持)。 8. 蓝牙、EDGE、3G和WiFi支持(需要硬件支持)。 9. 相机、GPS、compass搜索引擎、加速记支持(需要硬件支持)。 10. 丰富的开发环境:设备仿真器、开发工具链、调试工具链、内存和性能仿真、EClipse IDE插件等等。

Android结构:

下图显示了Android操作系统的主要组件。下面有每一部件的更细致的描述:

Applications:

Android配备了核心应用集:包括email客户端、SMS程序、计算器、电子地图、浏览器、联系人编辑器等等,所有应用的代码都用Java语言编写。

Application Framework:

开发者完全能够访问核心应用所使用的相同的框架API,应用架构设计简化了组件的重用,任何应用都可以开放它的能力集,并且其他应用可以使用这些能力集(框架增加了安全性),同样,用户可以替换所有的组件。 下面的应用都包含在服务和系统中: 1. 一个丰富而且可扩展的View集能够用于建立应用,包括lists, grids, text boxes, buttons, 甚至一个嵌入式的浏览器。 2. Content Provider可以让应用访问其他应用的数据、或者共享自己的数据给其他应用。 3. Resource Manager用于访问非代码资源,例于访问本地化的字符串、图像、和布局文件。 4. Notification Manager能够让所有应用在状态条显示客户化的告警。 5. Activity Manager管理应用的生命周期,并且提供一个公共的导航栈。

Libraries:

Android提供一个C/C++库用于各个Android系统组件,这些库通过应用框架向开发者开放。下面是一些核心库: 1. 系统C库:一个继承于BSD的标准C库(libc),基于嵌入式Linux做了一些微调。 2. 媒体库:基于PacketVideo的OpenCORE,这个库能够对流行的音视频格式、静态图像格式进行回放和记录,包括MPEG4, H.264, MP3, AAC,
AMR, JPG, 和PNG等。 3. 皮肤管理:管理显示子系统,并且无缝组合2D/3D图形层。 4. Web核心库:流行的web核心引擎。 5. SGL: 2D图形引擎。 6. 3D库:基于OpenGL ES 1.0 API实现,既可以3D硬加速,也可以3D软加速。 7. FreeType: 支持位图和向量字体。 8. SQLite: 一个强有力的、轻量级的数据库引擎。

Android Runtime:

Android提供了一个基于Java语言的运行时库。 每个Android应用运行在自己的进程上,和它自己的Dalvik虚拟机实例,一个设备能够运行多个Delvik虚拟机实例。Dalvik虚拟机执行文件(.dex)被优化成最小的内存使用。Dalvik虚拟机是基于寄存器的,它的运行类是Java语言编译器编译的并且被dx工具把格式转换成了.dex格式。 Dalvik虚拟机依靠Linux内核的支持,比如线程调度、底层内存管理等等。

Linux Kernel:

Android依靠Linux的核心服务,例如安全性、内存管理、进程管理、网络协议栈、底层硬件驱动模型等等。Linux作为硬件和其他软件栈之间的抽象层存在。

Android开发 第二章

第二章 开始

第一节 安装Android SDK

本节描述了怎样安装Android SDK和建立自己的开发环境。 如果是从老版本升级,暂时省略升级过程(以后看需要再补充)。

系统和软件需求

为了用Android SDK开发应用,你需要合适的开发计算机和开发环境,就像下面所描述的: 支持的操作系统: 1. Windows XP或Vista 2. Mac OS X 10.4.8 或更新版本 (只支持 x86) 3. Linux (在Linux Ubuntu Dapper Drake上测试过) 支持的开发环境: 1. Eclipse IDE 1.1. Eclipse 3.3(Europa)或3.4(Ganymede) 1.2. Eclipse JDT 插件 (大多数Eclipse IDE包里面都有这个插件) 1.3. WST(可选,但是Android编辑器需要,大多数Eclipse IDE包里面都有这个插件) 1.4. JDK 5或JDK 6(单单JRE是不够的) 1.5. ADT插件(Android Development Tools插件) 1.6. Java的非GNU编译器(gcj) 2. 其他开发环境或IDE 1.1. JDK 5或JDK 6(单单JRE是不够的) 1.2. Apache ANT 1.6.5或更新版本(Linux和Mac), 1.7或更新版本(Windows) 1.3. Java的非GNU编译器(gcj)

安装SDK

下载SDK之后,解压.zip包到一个合适的目录(建议某个盘的根目录),缺省的Android安装目录名字是android_sdk_<platform>_<release>_<build>,这个目录包含一些子目录,其中有tools/, samples/等等。 请记住解压后的SDK的目录名字和路径,后来建立Android开发插件或用SDK工具的时候需要知道SDK的位置。 可选的,你能增加SDK tools/目录到你的path,象上面所说的,tools/目录在SDK主目录下。 1. 在Linux上,编辑 ~/.bash_profile 或者 ~/.bashrc文件,寻找设置PATH环境变量的一行,增加tools/目录的全路径到PATH后面,假如不存在,就增加一行: export PATH=${PATH}:<your_sdk_dir>/tools 2. 在MAC上,编辑.bash_profile文件,同上Linux相同。 3. 在Windows上,右击”我的计算机“,选择"属性",在“高级”页,点击“环境变量”按钮,弹出对话框时,双击“系统变量”里面的“PATH”条目,增加tools/目录到PATH后面。 增加了tools/目录到path之后,当运行Android Debug Brdge(adb)和其他tools命令时能够省略掉全路径。记住,假如你更新SDK,记得更新你的PATH到新的路径。

安装ADT

假如你用Eclipse IDE作为你的Android应用的开发环境,你可以安装一个客户化的插件(ADT),它集成了Android项目和工具链,ADT插件包含各种强有力的扩展,以便建立、运行、调试Android应用更快、更容易。 假如你不用Eclipse IDE,你就不需要下载ADT插件。 下面是下载和安装ADT插件过程: Eclipse 3.4: 1. 开启Eclipse,选择Help->Software Updates ... 2. 出现对话框,点击 Available Software 页面 3. 点击 Add Site... 4. 输入站点URL:https://dl-ssl.google.com/android/eclipse/,点击OK 5. 返回Available Software页面,等一会儿你就可以看到这个插件,选中Developer Tools前边的复选框,接着点击Install... 6. 在接下来的安装窗口,"Android Developer Tools"和"Android Editors"都被选上了,Andriod Editors是可选的,但是强烈建议选上,假如安装它的话,需要提前安装上面提到的WST插件。点击Finish 7. 重启Eclipse. 重启之后,更新Eclipse设置,以便它能找到Android SDK位置: 1. 选择Eclipse的Window->Preferences...(Mac OS: Eclipse->Preferences) 2. 在左边面板上选择"Android" 3. 看到主面板上有输入框,用来输入SDK位置,点击Browser...并且选择SDK所在目录的路径。 4. 点击Apply,接着选OK. ADT安装问题: 假如你没法在上面的步骤里面下载ADT,下面是一些建议: 1. 在第4步,试着使用http,而不是https。 2. 假如你在一个防火墙后面,确保你的Eclipse代理配置正确。在Eclipse 3.3/3.4,你能从主Eclipse菜单设置代理,Window->Preferences->General->Network Connections。 假如你还是不能下载ADT,那么手工安装吧: 1. 下载ADT的.zip文件(不要解压)。 2. 同上面的安装ADT步骤的第1,2步。 3. 在Eclipse 3.3,点击New Archive Site...;在Eclipse 3.4,点击Add Site...,接着选择Archive... 4. 浏览和选择下载的.zip文件。 5. 同上面的安装ADT步骤第5步开始的剩下的过程。 假如你在安装ADT的时候碰到一些错误,很可能是因为你没有安装其他的一些必须的插件,例如WST之类的。 更新ADT插件: 在一些情况下,一个新的ADT插件也许有效,你能用下面的步骤去更新你的ADT插件: Eclipse 3.4: 1. 选择Help->Software Updates... 2. 选择Installed Software页 3. 点击Update... 4. 假如ADT更新有效,选择并且选择Finish 目前为止,你的开发环境已经建立了,接下来我们看看怎么建立Android应用吧。

第二节 更新SDK

暂时省略。。。

第三节 开发和调试

本节介绍怎么在Android上开发和调试应用。

在Eclipse上面开发Android应用

建立一个Android项目 ADT插件提供了一个新的项目导航: 1. 选择File->New->Project 2. 选择Android->Android Project,接着选择Next 3. 选择项目内容: 3.1. 选择Create new project in workspace去建立新项目,输入项目名字、包名字(格式aaa.bbb)、activity类名字去建立.java文件,并且为应用命名。 3.2. 选择Create project from existing source去建立新项目,源文件自动加入。 4. 选择Finish ADT插件为新项目建立了很多文件和目录: src/,包括模板式的.java文件 res/,本项目的资源 AndroidManifest.xml,项目清单 建立装载配置 在你运行和调试你的Eclipse项目之前,你必须建立装载配置,装载配置指定怎样装载、启动、仿真你的项目。 1. 选择Run->Open Run Dialog...或者Run->Open Debug Dialog...,两者选一个就可以,系统会自动设置到两边。以后可以单独修改run或debug的配置。 2. 在左边的项目类型列表,选择Android Application,然后右键选择New 3. 键入装载配置的名字。 4. 在Android页面,浏览项目和Activity的设置 5. 在Target页面,设置想要的屏幕和网络属性,和其他的启动选项 6. 在Common页面,可以设置其他选项 7. 点击Apply保存装载配置,或者点击Run或Debug去执行或调试项目 运行和调试应用 一旦建立了项目和装载选项,就可以运行和调试项目了。 选择Run->Run或Run->Debug去运行或调试项目。 注意:激活的装载配置是最近一次在Run->Configuration Manager里面所选择的。除非你改变这个选择,否则它一直使用这个装载配置。 如果要设置或者改变激活的装载配置,使用Run Configuration Manager。 运行或调试项目将触发下面的行为: 1. 如果仿真器没有运行,则启动仿真器。 2. 如果项目有变化,则重新编译项目。 3. 运行的话,则开始运行应用。 4. 调试的话,在'Wait for debugger'模式启动应用,接着打开Debug perspective,并且调用Eclipse java调试器去调试应用。

用其他IDE和工具开发Android应用

建立Android项目 Android SDK包含activityCreator,它能产生Android项目所需要的大部分模板文件,可以使用它来生成新的Android项目,对于Linux和Mac,SDK提供activityCreator.py(Python脚本),对于Windows,提供activityCreator.bat(批处理脚本)。 用activityCreator建立Android项目: 1. 命令行下,改变路径到SDK tools/目录,并且建立你自己的新项目目录。如果从已存在的代码建立项目,改变应用的根目录。 2. 运行activityCreator,指定一个类名字作为参数,如果是新代码项目,这个类表示根类的名字,如果是从已存在的代码建立的项目,这个类名字必须是包里面的Activity类的名字。命令选项如下: --out <folder> :设置输出目录,缺省,输出目录是当前目录。 --ide intellij :在新项目里产生IntelliJ IDEA项目文件。 一个例子:
~/android_linux_sdk/tools $ ./activityCreator.py --out myproject your.package.name.ActivityName
package: your.package.name
out_dir: myproject
activity_name: ActivityName
~/android_linux_sdk/tools $
activityCreator脚本产生下列文件和目录,但是不会覆盖已经存在的文件和目录: 1. AndroidManifest.xml:应用的清单文件。 2. build.xml:一个Ant文件用来建立/打包应用。 3. src/your/package/name/ActivityName.java:指定的Activity类。 4. your_activity.iml, your_activity.ipr,
your_activity.iws
[only
with the -ide intelliJ flag
],intelliJ的项目文件 5. src/,包括模板式的.java文件
6. res/,本项目的资源
7. bin/,build脚本的输出目录 现在你可以移动你的目录到任何你想放置的地方,你必须用tools/目录的adb程序去发送文件到仿真器。另外应该避免移动SDK目录。 生成Android应用 activityCreator建立了build.xml文件,现在可以使用它来生成你的应用。 1. 假如没有Apache Ant,可以从网上下载,安装。 2. 用Ant之前,需要声明JAVA_HOME环境变量,指向JDK目录。 注意,在Windwos上安装JDK时,缺省安装在"Program Files"目录,因为有空格存在,会导致Ant失败,所以必须指定JAVA_HOME:set JAVA_HOME=c:Prora~1Java,最容易的解决方案是安装JDK的时侯,选择一个新的目录,比如:c:javajdk1.6.0_02。 3. 这些弄好之后,建立一个新的Android项目。 4. 使用Ant命令去编译生成你的应用,每次改变源文件,都需要重新运行一次Ant。 运行Android应用 为了运行编译生成好的应用,需要更新.apk文件到仿真器/data/app/目录,并且使用adb工具: 1. 启动仿真器(命令行执行 <your_sdk_dir>/tools/emulator) 2. 在仿真器里面,到主屏幕。 3. 运行adb install myproject/bin/<appname>.apk去上载你的应用,例如,安装Lunar Lander例子,进入目录:<your_sdk_dir>/sample/LunarLander,运行:../../tools/adb install bin/LunarLander.apk。 4. 在仿真器里面,打开有效应用列表,选择你的应用并且运行它。 注意,当你第一次安装这个Activity,你需要重启仿真器,因为仿真器的包管理器常常只在仿真器启动的时候检查应用清单。 调试你的应用 这部分介绍怎么在屏幕上显示调试信息(例如CPU使用情况),怎么样挂起IDE去调试应用。 如果用Eclipse IDE,调试器会自动调用: 1. 启动Dalvik Debug Monitor Server(DDMS)工具,它在IDE和仿真器之前执行端口前转服务。 2. 在仿真器里面设置可选的调试配置,阻塞应用直到调试器准备好。 3. 配置IDE去监听端口8700,以便调试应用。 配置IDE监听调试端口 DDMS将分配一个特定的调试端口给仿真器上的每个虚拟机,你的IDE必须监听这个端口,或监听缺省端口8700,以便连接到当前的应用。 你的IDE应当能够联系仿真器上的应用,显示它的线程并且可以挂起它,看到它的状态,能够设置断点。假如你在开发设置里面选择 "Wait for debugger",那么当Eclipse连接上的时候,这个应用将继续运行。因此在连接前你必须设置好你所需要的断点。 改变正在调试的应用,或者"wait for debugger"选项,将导致系统杀死当前正在运行的应用。你能够简单的选择或去选择这个复选框去杀死处于错误状态的应用。

签署应用

Android的所有应用都需要数字签名,没有签名的应用将不能安装和运行,不管是在仿真器还是实际设备,都需要签名,因此在应用能够运行和调试之前,必须先签名。 1. 所有的应用必须签名 2. 必须用自签名证书签署你的应用 3. 系统在安装的时候测试签名证书的有效期,假如过期,则不能继续运行 4. 可以用标准工具生成证书和实现签名:Keytool和Jarsigner Android SDK工具在调试的时候能够帮你签署你的应用,Eclipse ADT插件和Ant build工具提供两种签名方式-调试模式和发布模式。 1. 调试模式,生成工具使用SDK里面的Keytool建立含有别名和口令的调试密钥和密钥存储。每次编译的时候,都会使用调试密钥来签署.apk应用。因为口令已知,所以不用提醒输入口令。 2. 当你的应用发布的时候,你用发布模式签署你的应用,在发布模式,编译.apk文件的时候不会签署它,必须用Keytool生成你自己的发布密钥和密钥存储,接着用JDK 里面的Jarsigner来签署这个.apk文件。 签名基本设置 为了支持调试密钥/密钥存储的生成,必须确保Keytool有效,多数情况下,Keytool使用JAVA_HOME变量知道相应的JDK是什么,可选的,你可以在PATH变量里面增加Keytool的JDK版本。 假如你在GNU Java的Linux版本上开发,确保系统正在用Keytool的JDK版本,而不是gcj版本,假如Keytool已经在你的PATH里面,也许是/usr/bin/keytool的符号连接,这时,请检查符号连接的目的点是在JDK里面的Keytool。 Eclipse/ADT里面签名 在Eclipse/ADT里面的调试签名是自动的,当你运行或调试应用,ADT自动签名.apk文件并且安装到仿真器,所以没有特别需要手工做的事情。 发布模式编译应用,在Package那边的项目上右键点击,接着选择Androi Tools->Export Application Package,另外,你也可以在Manifest Editor页面,执行“Exporting the unsigned .apk”,在你保存了.apk文件后,你需要用Jarrsigner来签名.apk文件(密钥是你自己之前分发的),假如你没有密钥,你可以用Keystore去建立密钥和密钥存储。 ANT里面签名 如果是用createActivity建立的build.xml来生成应用,缺省调试模式的签名是有的。 发布模式,你需要做的是在ANT命令行指定生成目标是“release”,例如,假如你在build.xml文件所在位置运行Ant,命令应该是这样的:ant release。 生成脚本编译.apk文件,但是没有签署,编译之后,你必须用Jarsigner签署你的应用。 调试证书过期 调试模式的自签名证书过期时间是从建立开始的1年。 当证书过期,生成的时候有一个错误,在ANT上,象这样:
debug:
[echo] Packaging bin/samples-debug.apk, and signing it with a debug key...
[exec] Debug Certificate expired on 8/4/08 3:43 PM
在Eclipse/ADT上,在Android console上有个类似的错误。 为了修复这个问题,删除debug.keystore文件,在Linux/Mac os上,这个文件在~/.android,在Windows上,这个文件在C:Documents and Settings\Local SettingsApplication DataAndroid下面。 这样子,当下次生成的时候,生成工具将自动产生新的调试密钥和密钥存储。

使用ApiDemos例子应用

Android SDK包含大量的例子应用,他们给出了大部分API的使用方式。这个ApiDemos包已经安装在了仿真器里面,因此你能直接看到他们。 你能找到这些应用的源代码在<SDK>
/samples/ApiDemos。 如果你愿意,你可以载入这些例子并且修改他们,然后在仿真器上执行,不过呢,这样做之前,你必须卸载掉已经安装在仿真器里面的哪些例子,否则不能安装进仿真器,会显示如下错误:
[2008-08-13 15:14:15 - ApiDemos] Re-installation failed due to different application signatures.
[2008-08-13 15:14:15 - ApiDemos] You must perform a full uninstall of the application. WARNING:
...This will remove the application data!
[2008-08-13 15:14:15 - ApiDemos] Please execute 'adb uninstall com.android.samples' in a shell.

卸载和重新安装过程:
1. 在一个终端里面,切换到SDK的tools目录。
2. 假如没有仿真器实例运行,启动一个仿真器:emulator &
3. 卸载老的程序:adb uninstall com.android.samples
4. 重新安装新的程序:
adb install <path to the ApiDemos.apk>,如果用Eclipse IDE直接编译运行就可以了。

注意,假如有多个仿真器实例在运行,你需要在你选中的仿真器上卸载、安装这些程序,需要增加仿真器参数-s:
adb -s emulator-5556 install

调试

Android扩展了一些工具来方便调试:
1. DDMS: 一个图形化的程序,支持端口前转,屏幕捕获,线程和栈信息等等。也可以用logcat恢复Log消息。
2. logcat: 输出系统消息log,消息包括仿真器异常时候的栈跟踪,类似于:
...
I/MemoryDealer( 763): MemoryDealer (this=0x54bda0): Creating 2621440 bytes heap at 0x438db000
I/Logger( 1858): getView() requesting item number 0
I/Logger( 1858): getView() requesting item number 1
I/Logger( 1858): getView() requesting item number 2

D/ActivityManager( 763): Stopping: HistoryRecord{409dbb20 com.android.home.AllApps}
...
3. Android log: 一个log类用来输出log到文件,能够在DDMS的logcat实时读取消息,在你的代码里面增加几行Log方法。
为了用log类,必须根据重要性调用Log.v(详细),Log.d(调试),Log.i(信息),Log.w(警告),Log.e(错误):
Log.i("MyActivity", "MyClass.getView() - Requesting item number " + position)
4. Traceview: Android能够保存方法调用顺序之类的详细信息保存到文件里,然后可以用图形化traceview工具读取、分析。
5. Eclipise插件:插件里面可以直接调用上面这些工具(ADB,DDMS,logcat输出等等)。
6. 调试和测试设备设置: 参考下面。

设备的调试和测试设置

Android提供一些设置,让你更容易测试和调试你的应用。进入仿真器的开发设置页面:Dev Tools->Development Settings,打开设置页面,页面上有些选项: 1. Debug App: 选择应用进行调试,假如选中它: 假如你停在断点很长时间,这个选项将阻止Android扔出异常。 它将让你选择"Wait for debugger"选项。 2. Wait for debugger: 阻塞选中的应用直到一个调试器连接上,因此你可以在onCreate()的时候设置断点,对于Activity的启动过程调试是很有用的。当你改变这个选项,当前运行的应用的所有实例都将被杀死。使用waitForDebugger()方法也可以实现相同的功能。 3. Immediately destroy activities: activity停止的时候立即回收资源,在测试onSaveInstanceState(Bundle)/onCreate(android.os.Bundle)时是非常有用的。选中这个选项将暴露非保留状态应用的一些问题。 4. Show screen updates: 记下重画屏幕的快照。在发现不需要的屏幕重画方面很有用。 5. Show CPU usage: 在屏幕顶上显示CPU使用量,顶上的红条显示CPIU使用量,绿条显示合成本屏幕时CPU所用的时间,一旦打开这个开关,就不能关掉,除非重启仿真器。 6. Show background: 当没有activity可见的时候,显示背景模式。只在调试的时候发生这种情况。 这些设置即使重启仿真器,也能被仿真器记住,除非手工关闭或打开这些选项。

调试技巧

1. 快速栈输出 从仿真器得到栈输出,先登入adb shell,用ps去找到你的进程,接着用kill -3, 栈信息将显示在log文件里面。 2. 在仿真器屏幕上显示有用的信息 参考上面小节设置 3. 从仿真器得到系统状态信息(dumpstate) 用DDMS工具可以得到系统状态信息。 4. 从仿真器得到应用状态信息(dumpsys) 用DDMS工具可以得到系统状态信息。
5. 得到无线连接信息 DDMS工具,菜单Device->Dump radio state。 6. 记住跟踪数据 能够记住方法调用和其他跟踪数据通过调用android.os.Debug.startMethodTracing()。 7. 记住无线数据 缺省,无线数据不会log,可以用命令打开:
adb shell
logcat -b radio
8. 运行ADB 9. 在仿真器上捕获屏幕 DDMS能够捕获屏幕快照。 10. 使用调试帮助类 Android使用调试帮助类,例如util.Log和Debug。

生成和安装Android应用

.apk文件包括.dex文件、资源文件、原始数据文件和其他文件的压缩。Android不支持原始的C/C++代码。

移除Android应用

要移除已经安装在仿真器上的应用,需要运行adb删除.apk文件,用adb shell进入/data/app/,接着移除文件:rm your_app.apk。

Eclipse注意事项

执行Java表达式断言 当程序停在断点时,可以执行断言代码,例如,当在一个包含字符串参数'zip'的函数里面,你能得到包的信息,并且可以调用类方法。也可以使用静态方法:例如,进入android.os.Debug.startMethodTracing()将开始dmTrace。 打开一个代码执行窗口,从主菜单选择Window->Show View->Display打开Display窗口,一个简单的文本编辑器。键入表达式,高亮表达式,点击'J'图标(CTRL + SHIFT + D)运行代码。代码将在所选择的线程(停在断点或单步状态)的上下文里运行(假如手工挂起了线程,必须单步执行一次,处于Object.wait()的线程是不行的)。 假如你停在一个断点,可以简单的通过CTRL + SHIFT + D高亮执行一块源代码。 高亮文本时,可以用ALT +SHIFT
+ UP ARROW选择更大的文本块,或者用DOWN ARROW选择更小的文本块。 下面是几个关于Eclipse Display窗口的输入和响应的例子:
输入 响应
zip(java.lang.String) /work/device/out/linux-x86-debug/android/app/android_sdk.zip
zip.endsWith(".zip")(boolean) true
zip.endsWith(".jar")(boolean) false
不在调试状态的时候,能够用剪贴薄页面来执行断言。搜索Eclipse文档关于"scrapbook"的部分。 手工执行DDMS 建议使用ADT插件调试代码,但是你能手工运行DDMS配置Eclipse用8700端口来调试程序(确保你启动了DDMS)。

第四节 “Hello, Android!”

作为第一印象,你将看到Android开发框架写一个“Hello World”是多么容易。

建立项目

尽可能简单的建立项目,一个Eclipse插件用来做Android开发。 需要一个开发电脑安装Eclipse IDE,需要安装ADT,做完这些之后回来。 第一步:建立一个新的Android项目 从Eclipse里面选择File->New->Project菜单,假定ADT安装没问题,将显示一个对话框,里面有标签为"Android"的目录,目录下面有个"Android Project"的条目。
选择”Android Project",点击"next"。 第二步:填写项目细节 下个屏幕显示这个项目的相应细节:
下面是各个单项的意义:
项目名字 包含这个项目的目录名字
包名字 包名称空间(跟Java里面的包是一个概念),包含所有源代码,这个也是根激活的包名字。 系统中所有的包必须是统一标识的,所以最好用一个域类型的包名字,就象上面的"com.android",你可以选一个符合自己组织的名字。
Activity名字 插件产生的根类的名字,它是Android激活类的子类。一个激活是一个简单的能运行和工作的类,它可以建立一个UI,但不是必须的。
应用名字 本应用的便于理解的标题
“Use default location”复选框允许改变这个项目在磁盘上的存放位置。 第三步:编辑自动产生的源代码 选择"Finish"之后,将有一个名叫HelloAndroid的类(在包下面,HelloAndroid->src->com.android.hello)产生,类似于下面例子:
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
现在,可以立即运行本项目,让我们更进一步,理解得更多一点,因此下一步是修改一下代码。


构造UI用户接口


看看下面代码,在你的HelloAndroid.java文件里面做相同的修改,接着我们一行一行的剖析:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Hello, Android");
setContentView(tv);
}
}
提醒:假如你忘记导入TextView包,试着用Ctrl+Shift+O组合按钮,这是Eclipse的快捷键,它会帮你把丢失的包增加进来。 Android里面,用户接口是由叫做Views的类分层组成的。一个View是单个可显示的对象,例如一个单选按钮,一个动画,或者一个文本标签。处理文本的View子类的特定名字是TextView。 下面是怎么构造一个TextView:
TextView tv = new TextView(this);

TextView构造函数的参数是一个Android Context实例,这个Context是个系统句柄,它提供这些服务:解析资源、访问数据库等等,Activity类从Context继承。既然我们的HelloAndroid类是从Activity类继承,因此也是Context的子类,我们可以this参数代替。

一旦构造了TextView,需要告诉他怎么显示:
tv.setText("Hello, Android");

现在,我们构造了TextView并且告诉它哪些文本需要显示,最后一步是连接TextView到屏幕上显示:
setContentView(tv);

Activity的setContentView()方法指示哪个View将和Activity的用户接口相关联。假如Activity不调用这个方法,没有UI呈现,系统显示一个空白屏幕。象这个例子,我们需要显示一些文本,因此我们使用它。


接下来看怎么运行。

运行代码

ADT下面很容易运行代码,选择Run->Open Run Dialog(Eclipse3.4是Run->Run Configurations),将打开一个对话框:
接着,选中"Android Application",双击它,右边显示一个新的页面,名字叫"New_configuration"。
修改名字,例如改成“Hello, Android",接着点击”Browse..."按钮,选择我们的"HelloAndroid"应用,ADT将自动扫描这个项目下面的Activity类,并且增加每个条目到"Activity:"下拉列表,选择正确的条目。 点击"Apply"图标。
点击”Run“按钮,Android仿真器将启动,等他启动成功,我们的应用将显示,最后,你将看到:
这就是Android的Hello,World。接下来将提供更多的细节以供学习。

用XML布局更新UI

上面的例子用的是程序里面的UI布局,就是说你在程序里面直接建立应用的UI布局。假如你做了很多UI编程,你应该熟悉这样子实现有时是多么的脆弱:布局的微笑改变将导致大量的令人头痛的源代码修改。 因此Android提供XML方式的UI布局,最好的解释方法还是看一个例子,上面例子改成XML布局:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Hello, Android"/>
一般来讲,写一个XML布局文件是简单的,它就是一个标记树,每个标记是一个View类的名字,本例子很简单只有一个TextView类。你能够使用任何类名区扩展View标记名字,包括客户化的View类。因此能够比代码更快速和更轻松的建立UI。这个模型的灵感来自网站开发模型,它能够分离应用的UI界面和应用逻辑。 在这个例子里面有四个XML属性:
属性 意思
xmlns:android根元素,每个布局文件都需要这个标签
android:layout_width屏幕宽度,"fill_parent"标识占据整个屏幕
android:layout_height屏幕高度
android:text文本内容
上面是XML布局文件的样子,把他放到工程的 /res/layout目录下,"res"是resource的缩写,这个目录包含所有的这个工程的非代码资源。比如:图像,本地化的字串,XML布局文件等等。

Eclipse插件自动建立这些XML文件,在上面例子里,我们没有使用它们,在"Package Explorer",展开/res/layout,编辑main.xml文件。用上面的文本替换里面的内容,并且保存。

现在在"Package Explorer",打开源代码目录的R.java文件:
public final class R {
public static final class attr {
};
public static final class drawable {
public static final int icon=0x7f020000;
};
public static final class layout {
public static final int main=0x7f030000;
};
public static final class string {
public static final int app_name=0x7f040000;
};
};
一个工程的R.java文件是所有资源的一个索引,你可以用它作为整个项目快速资源的参考。这是一个基于代码的强有力的IDE特性,因为这可以让你快速的交互式的定位所需要的参考。

主意上面的内部类叫layout,它的成员有main。Eclipse插件在你增加一个新的XML布局文件的时候将重新生成R.java文件,就像你增加其他资源一样R.java将保存更新。

最后要做的是修改HelloAndroid源代码,以便使用新的XML文件而非硬编码的布局。下面是源代码,变得更简单:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
当你作这个改变时,不仅仅是拷贝和黏贴,试试其他完全基于代码的特性,也许会发现很多有用的信息。

你做这样一个改变,然后重新运行应用,你只要点击绿色的run箭头,或从菜单选择Run > Run History > Hello, Android,你将看到跟之前一样的界面。

建立XML文件有大量的东东需要学习,你能够阅读“实现用户接口”这一章学习更多的细节。

调试项目


Android跟Eclipse调试器有非常好的集成,我们加入一个bug在程序里面来看看怎么调试:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object o = null;
o.toString();
setContentView(R.layout.main);
}
}
代码里加入了NullPointerException,再次运行应用,将看到下面的输出:


点击"Force close"终止应用,关闭仿真器窗口。

为了进一步理解,在源代码里面设置一个断点在Object o=null;这一行,接着选择Run->Debug History->Hello,Android进入debug模式。应用重启仿真器,这次它将在断点处挂起,接着你可以单步执行代码在Eclipse的Debug Perspective,就象你调试其他应用一样。

非Eclipse方式建立项目

假定你不使用Eclipse,照样可以开发Android程序。
Eclipse插件只不过把Android的各个SDK工具集成到了Eclipse而已(例如仿真器,aapt,adb,ddms等),因此,集成这些工具到其他开发环境也是完全可能的,比如ant。

Android SDK包含一个叫activitycreator.py的Python脚本,它用来建立一个项目的所有源代码和目录,就像一个ant兼容的build.xml文件。这点可以让你用命令行方式建立项目,或者根其他IDE集成。

例如,要建立一个HelloAndroid项目:
activitycreator.py --out HelloAndroid com.android.hello.HelloAndroid

要生成这个项目,需要运行命令“ant”,当命令成功运行后,你将在bin目录下看到一个文件名为HelloAndroid.apk文件,这个.apk文件是一个Android包,能够在仿真器上安装和运行。
更多的关于这些工具的信息,请读相关的文档。


第五节 Android应用的剖析

一个Android应用有4个建成块:
Activity
Broadcast Intent Receiver
Service
Content Provider

不是每个应用都必须要这四个块,但是至少是这些的组合。

一旦你决定你的应用需要哪些组件,你应当在AndroidManifest.xml文件中列出来,这个XML文件声明你的应用所需要的组件和它们的能力及需求。

Activity

Activity是四个组件中最公用的一个,一个activity通常是应用的一个单个屏幕,每个activity是从Activity基类继承扩展的而来的一个单个的类。这个类将显示一个用户接口,它由View和相应的事件组成。大多数应用具有多屏幕设计,例如,一个文本消息应用也许有个屏幕用来显示发送消息的联系人列表,第二个屏幕显示书写给联系人的消息,其他屏幕查看老的消息或者改变设置,这些屏幕中的每一个都被作为一个activity实现。屏幕之间的移动是由开始一个一个新的activity来实现的。一些情况下,一个activity也许返回一个值到以前的activity,例如:一个activity让用户选择一个图片,必须把所选择的图片返回给调用者。

一个新的屏幕开启,旧屏幕暂停,并且放进历史栈,用户能够在历史栈里面后向浏览以前打开过的屏幕,当的确不需要的时候,这些屏幕也能清除掉。Android保留任何从主屏幕装载的应用的历史栈。

Intent和Intent过滤

Android使用Intent类从一个屏幕移动到另一个屏幕,一个Intent描述一个应用想做的一切。Intent数据结构的两个最重要部分是程序行为和数据行为,典型的行为有MAIN,VIEW,PICK,EDIT等,这些行为数据表达为URI。例如,查看一个人的联系信息,你可以建立一个行为叫VIEW的数据集URI来表示这个人。

有个相关的类叫IntentFilter,一个Intent是一个有效的请求,一个IntentFilter是一个activity(或者BroadcatReceiver)能够处理的Intent的描述。一个activity能够根据IntentFilter怎样处理VIEW行为来显示某个联系人的信息。activity在AndroidManifest.xml文件发布IntentFilter。

从屏幕到屏幕的切换有解析Intent来完成,往前切换,activity调用startActivity(myIntent),系统接着从所有安装的应用里面寻找IntentFilter,并且选择跟这个IntentFilter最匹配的activity来处理myIntent。新的activity被Intent通知,导致它得到运行。解析Intent的处理发生在startActivity被调用的时候,这样有两个优点:
activity能够简单的重用其他组件功能通过制造一个Intent请求。
activity能够在任何时候被具有相同IntentFilter的activity替换。

广播Intent接收器

当你想在你的应用里面执行一个行为来响应外部事件的时候,你可以用BroadcastReceiver,例如,电话振铃时,或者网络数据有效,或者深夜。BroadcastReceiver并不会显示UI,虽然它们假如有些事情发生的时候也许用NotificationManager来提醒用户。BroadcastReceiver注册在AndroidManifest.xml里,但是你也可以在代码里用Context.registerReceiver()注册它们。你的应用并不是飞得用BroadcastReceiver,当BroadcastReceiver被触发时,系统将启动你的应用。应用也能通过Context.sendBroadcast()发送它们自己的Intent广播给其他应用。

Service

一个service是一个没有UI的长期活着的代码,举个例子,媒体播放器从播放列表里面播放歌曲。在媒体播放器应用,也许有一个或更多个activity允许用户选择歌曲并且开始播放。然而,音乐播放本身不应当由一个activity来处理,因为用户应该会希望当他改变到其他界面的时候,音乐还保持播放,这种情况下,媒体播放器activity能够用Context.startService()开始一个service运行在后台来处理音乐播放,系统将保持音乐回放直到它结束(你可以从Android应用的生命周期一节中学到更多关于service业务的优先级知识)。你可以使用Context.bindService()方法连接到一个service(需要时可以启动它)。当连接到一个service,你可以通过一个service接口跟它通信。上面的音乐服务,也许允许你暂停、快进、快退等等。

内容提供者

应用能够存储数据在文件、SQLite数据库,或其他存储机制。假如你想要你的应用的数据跟其他应用共享,则内容提供者是非常有用的。内容提供者是一个实现了标准方法集的类,能够让其他应用存储和恢复这些被内容提供者处理过的数据。

第六节 指南:一个笔记本应用

这部分的指南给你一个手把手的介绍Android框架和建立应用所需的工具。从一个预配置的项目文件开始,通过一个简单笔记本应用开发过程,看看怎么建立项目、开发应用逻辑和用户接口,接着编译运行程序。

这个笔记本开发作为一个练习集,每个练习由几步组成。你可以跟踪这个练习的步骤逐步的建立和精炼你的应用。这些练习详细解释了每一步,并且提供所有的例子代码。

当你完成了这个指南,你将建立了一个具有功能的Android应用,并且深入学习了许多Android开发的重要概念。假如你想给你的应用增加更多复杂特性,你能够在例子代码文档里面检查笔记本应用的代码实现。

谁应当使用指南

这个指南被设计给有应验的开发者,特别是熟悉Java语言的家伙。假如你没有写过Java应用,你也可以看看这个指南,不过你会领悟得更慢一点。

这个指南假定你熟悉了基本的Android应用概念和术语。这个指南用Eclipse和Android插件作为开发环境。

准备练习

这个指南使用了安装SDK章节和Hello,Android章节的内容。你开始这个指南之前,你应当读读这些章节的内容,安装好SDK,建立好工作环境。

课程的准备:
1. 下载项目练习包(.zip)
2. 解压包文件到一个合适的地方
3. 打开NotepadCodeLab目录

在NotepadCodeLab目录下,应当存在6个文件:
Notepadv1,
Notepadv2, Notepadv3,
Notepadv1Solution, Notepadv2Solution 和 Notepadv3Solution,这些Notepadv#项目是每个练习的开始点,这些Notepadv#Solution项目是这些练习的解决方案。假如你的练习有麻烦,你可以比较你当前的工作跟这个解决方案的差异。

练习

下面是练习列表,每个练习都假定你已经完成了之前的练习。
练习1
从这里开始,构造一个简单的通知列表,能够让用户增加新的通知,但是不能编辑。
例示ListActivity基础,并且建立和处理菜单选项,使用SQLite数据库保存这些通知。
练习2
增加第二个activity,例示构造新的activity,增加到Android的Manifest,在这些activity之间传递数据,使用更多先进的屏幕布局。显示怎样请求其他activity用startActivityForResult()返回结果。
练习3
增加一些处理生命周期的事件,让他通过生命周期维护应用状态。
额外练习
例示怎样用Eclipse调试器,例示用他查看生命周期事件。这部分可选,但是强烈推荐。

其他资源和进一步学习

  • 更进一步的概念的介绍没有在这个指南覆盖到,请看公共Android任务章节。
  • Android SDK包含大量的具有一定功能的例子代码,那些是最好的学习机会。你可以在SDK的/samples/目录下找到这些例子应用。
  • 这个指南是从SDK /samples/目录下的完整的笔记本程序来的,虽然并不是100%一致。当你完成了这个指南,强烈建议你看看/samples/目录下的笔记本程序,它例示了大量的有意思的功能,例如:
  1. 建立斑马式的通知列表
  2. 建立客户化的文件编辑视图,重载draw()方法使他看起来象一个有线纹的笔记本。
  3. 实现一个完整的ContentProvider。
  4. 转换和丢弃你的编辑内容而不仅仅自动保存。


第七节 开发工具

Android SDK包含大量的客户工具,它们能够帮助你开发Android移动应用。其中最重要的是Android仿真器和ADT插件,当然,SDK也包含大量的其他工具,诸如调试、打包、安装应用到仿真器等等。

Android 仿真器
运行在你的电脑上的虚拟移动设备,你可以在一个真实的Android运行环境用仿真器设计、调试、测试你的应用。

分层视图
分层视图工具允许你调试和优化你的用户接口,它提供可视化的布局分层表述,以网格方式显示,因此你可以正确的调整你的布局。

9-patch图像处理
9-patch工具允许你轻易使用WYS|WYG编辑器建立NinePatch图形,还能对图像处理,高亮一些允许高亮的区域。

Android开发工具ADT插件
ADT插件增加了Eclipse开发环境的能力,能够更快地建立和调试Android应用。假如你用Eclipse,ADT能够给你难以置信的方便:
让你直接从Eclipse IDE里面使用Android开发工具,例如,ADT能让你使用DDMS工具-屏幕快照、管理端口前转、设置断点、查看线程和进程信息。
提供新项目向导,让你快速建立所有的新项目的基本文件。
自动化和简化建立应用的过程。
提供Android代码编辑器,帮助编写AndroidManifest和资源XML文件。

Dalvik调试监控服务DDMS
集成Dalvik,Android平台客户化的虚拟机,让你管理仿真器或设备的进程,辅助调试。可以用它杀死进程、选择一个特定进程调试、生成跟踪数据、查看堆栈和线程信息,保存仿真器或设备的屏幕快照等等。

Android调试桥ADB
让你在仿真器或设备上安装.apk文件,从命令行访问仿真器或设备。也可以用它链接一个标准的调试器到仿真器或设备上的应用。

Android资产打包工具AAPT
可以建立包括二进制文件和资源文件的.apk文件。

Android接口描述语言AIDL
产生交互式接口代码。

SQLite3
让你访问SQLite数据文件。

Traceview
产生图形化的调试信息分析视图。

mksdcard
帮助用仿真器建立磁盘映像,仿真外部存储卡,例如SD卡。

DX
改写.class字节码为Android字节码(.dex文件)。

UI/应用训练猴
运行在仿真器或设备上的一个程序,能产生用户事件(点击、触摸、手势)的伪随机流,就像系统级别产生的事件,可以用Monkey在随机而重复的方式做应用的压力测试。

activitycreator
产生Ant工程文件的脚本,可以用来编译Android应用。假如用ADT插件在Eclipse上开发,则不需要用这个脚本。

第八节 应用模型:应用、任务、进程、线程


大多数操作系统,在它的可执行映像、进程、用户交互图标及应用之间存在一一对应关系。在Android里面,它们更为灵活。

因为灵活的Android应用生态,有一些基本的术语需要理解:
一个Android包是一个包含应用代码和资源的文件,用户需要安装应用的时候,应用被用户分发和下载。
一个任务是指用户感觉到的一个应用被装载,通常一个任务在主屏幕上有一个图标,通过这个图标可以访问到这个任务,作为一个顶级图标能够处于其他任务前面的前景上面。
一个进程是一个运行应用代码的低级别的内核进程,正常情况下一个应用的所有.apk代码运行在一个进程里面,然而呢,这个进程标记能够被修改作为整个.apk运行、或者作为分立的activity,receiver,service,provider等组件运行。

任务

关键点:当用户看到一个应用,实际处理的是一个任务。假如建立一个.apk文件,具有一些activity,其中的一个是顶级入口点(通过android.intent.action.MAIN、Android.intent.category.LAUNCHER等行为的IntentFilter),将有一个真正的.apk任务被建立,你所开始的任何activity都将作为任务的一部分运行。

一个任务,从用户观点和开发者观点来看,它是一个或多个activity,用户在没有关闭的任务或activity栈之间来回切换。一个新任务通过启动一个带有Intent.FLAG_ACTIVITY_NEW_TASK标记的activity intent来启动,这个intent作为这个任务的根intent,决定了这个任务是干啥的。任何其他不带这个标记的activity将运行在相同的任务里面(除非这个activity作为一个特定的装载模式,后面会讨论到)。任务能够重新排序:假如你用FLAG_ACTIVITY_NEW_TASK标记,但是已经有一个相同intent的任务在运行,当前任务的activity栈将推到前边而不开始一个新任务。

FLAG_ACTIVITY_NEW_TASK标记使用时必须注意:从用户观点来看,就是一个新的任务开始了。假如不是你期望的行为,你不应该建立一个新任务。另外,当用户从主屏幕返回它们原来的地方、并且作为一个新的任务装载相同的intent,你应当尽量用新任务标记。否则,假如用户从你已经装载的任务按了HOME键而不是BACK键的时候,你的任务和它的activity将被隐藏在主屏幕后面,没有办法返回。

任务姻亲

一些情况下,Android需要知道一个activity属于哪个任务,甚至它还没有被装载进一个特定的任务的时候。这个有任务姻亲完成,它为所有的activity提供统一的任务静态名字。一个activity的缺省任务姻亲是它的.apk包的名字。在一个.apk文件里的所有activity是单个应用的一部分。

启动一个不带Intent.FLAG_ACTIVITY_NEW_TASK标记的activity,任务姻亲不影响在这个任务里面的新activity:它总是运行在启动它的activity的任务里面。然而,假如NEW_TASK标记在用,姻亲关系将决定一个任务是否已经运行在相同侧姻亲里面。假如是这样的话,任务将显示到前端,新的activity被装载在任务的最前面。

这种情形下用NEW_TASK标记最有用,特别是从状态条通知或主屏幕快捷方式装载activity。结果是,当用户装载你的应用,当前任务状态将处于前景,用户想看的activity处于最前面。

你可以为.apk文件里的所有activity在manifest.xml的application标记分配你的任务姻亲,或者为单个activity用activity标记。下面是一些例子:

假如你的.apk包含用户能装载的多个顶级应用,你也许想给用户看到的.apk文件里每个activity分配不同的姻亲。一个好习惯是用你的.apk包文件的用“.”分隔的名字来区别,例如,com.android.contacts .apk也许有姻亲com.android.contacts.Dialer和com.android.contacts.ContactsList。

假如你替换能从外部装载的通知、快捷方式、或者内部activity。你也许需要明确设置你的activity的taskAffinity,就像你替换正在替换的应用一样。例如,你替换联系人细节,你应当设置任务姻亲为com.android.contacts。

装载模式和装载标记

控制activity和任务交互的主要方法是通过activity的launchMode属性和跟intent相关的标记。这两个参数可以以不同方式控制activity装载的结果,就像其他文档所描述的。下面是它们的一些共同用法和组合:

最共同的装载模式(除了缺省的multiple模式)是singleTop。这对于任务没有影响,仅仅避免在栈的顶部开始相同的activity多次。

singleTask装载模式有主要的影响:它导致activity总是从新任务启动(或使已经存在的任务显示在前景)。用这个模式需要非常小心怎样跟系统交互,他影响所有到activity的路径。他应当仅仅用来作为应用activity的前门(就是说,支持MAIN行为和LAUNCHER类别)。

singleInstance装载模式更为特殊,仅仅适用于整个应用一个activity的情况。

经常碰到的一种情形是其他实体(例如SearchManager或NotificationManager)启动你的activity。这种情况下,Intent.FLAG_ACTIVITY_NEW_TASK标记需要使用,因为activity从任务外面启动。象以前描述的,这种情形的标准行为是把当前任务带到前景,匹配新的activity姻亲,在最前面启动activity。当然,其他类型的行为你也可以实现。

一个通用方法是intent.FALG_ACTIVITY_CLEAR_TOP标记跟NEW_TAKS标记一起用。假如你的任务已经运行,被带到前景,在栈里面除了根activity外的所有activity被清除,根activity的onNewIntent被调用。注意activity通常用singleTop和singleTask装载模式,当前实例得到新intent,而不是毁坏之后重新开始一个实例。

另一个你能选择的方法是设置通知activity任务姻亲为空字符串"",并且设置finishOnBackground属性。假如你希望通知带用户到一个分离的activity而非返回应用任务的话,这个方式是有用的。通知指定这个属性,不管用户用BACK还是HOME离开,activity都将结束;假如这个属性没有指定,点击HOME将导致activity和它的任务保留在系统里,可能没有办法返回。

详细情况请读launchMode属性和intent标记文档。


进程

进程是应用的实现细节,用法简单:
通过放置不可信任或不稳定的代码到其他进程来提高稳定性或安全性
通过运行多个.apk代码在同一个进程减少头开销
通过放置重量级代码到一个分离的进程帮系统管理资源,这个进程可以独立地被应用的其他部分杀死

象之前所描述的,process属性用来控制进程,注意这个属性不能用来背离系统安全性。假如两个不共享用户 ID的.apk试图运行在同一个进程,这个是不允许的,这样子只能是每一个建立一个独立的进程。

关于安全限制请看security文档。

线程

每一个进程有一个或多个线程,大多数情况下,Android避免在一个进程里面建立多个线程,保持一个应用为单线程,除非他建立他自己的线程。一个反应是所有调用Activity,BroadcastReceiver,Service的实例都只能在一个进程的主线程里面。

为每个调用Activity,BroadcastReceiver,Service,ContentProvider的实例,没有新线程建立:这些应用组件被实例化在特定进程的主线程里面。意味着这些组件的执行不能过长或阻塞,否则将阻塞这个进程的所有其他组件。可以用标准线程库类或Android的HandlerThread类建立新线程来执行长操作。

几个注意点:
IBinder接口的实现必须总是线程安全的,因为它们可能被同时被几个线程同时调用。
ContentProvider类的方法必须是线程安全的,理由同上。
应从view窗口所在的线程调用view类和它的子类。正常情况下这个应该是进程的主线程,假如你建立一个线程并显示窗口,窗口的继承关系将从这个线程调用。

第九节 Android应用的生命周期

多数情况下,每个Android应用运行在自己的Linux进程,当应用的代码需要的时候,进程被建立,保留到不再需要,系统需要回收它的内存给其他应用。

一个Android的不寻常但基本的特性是:应用进程的生命周期并不是直接由应用本身控制。相反,它由系统控制,主要取决于系统内存。

必须理解应用组件怎么影响应用生命周期,不正确的使用组件将导致系统做重要事情的时候杀死应用进程。

一个例子:当BroadcastReceiver用BroadcastReceiver.onReceive()方法接收到intent时启动一个线程,接着从这个函数返回。一旦返回,系统考虑到BroadcastReceiver不再激活,它的主处理进程不再需要,因此,系统杀死进程以便回收内存。这个问题的解决方法是:从BroadcastReceiver启动一个service,这样系统知道这个进程还有一些工作需要完成。

当内存不足时决定终止哪个进程,Android放所有进程到一个基于组件和组件状态的重要性分级列表。进程类型:

1. 前景进程是用户正在执行操作的进程,下面条件满足时它是前景进程:
在屏幕在前端运行和用户交互的activity(onResume方法被调用);
有一个BroadcastReceiver在运行(BroadcastReceiver.onReceive()方法被执行);
有一个service在回调函数里执行(Service.onCreate(), Service.onStart(), Service.onDestroy())。

系统里仅有几个这样的进程,在系统内存严重不足时,这些进程将被最后杀死。通常,设备达到了内存页状态,因此首先保证用户接口的响应。

2. 一个可见的进程是有一个activity在屏幕上可见,但不在前景(onPause方法调用),例子:假如前景显示一个对话框允许之前的activity能够在它后面被看见,这样的进程也被当成重要的,除非内存不足以保证前景进程和可见进程的正常运行。

3. 一个服务进程是用startService启动的一个service,虽然这些进程用户不可见,但它们通常做些用户关心的事情(例如MP3在后台播放,或网络数据的后台上载、下载),除非内存不足以保证前景进程、可见进程和服务进程的正常运行。

4. 一个背景进程是用户不可见的activity在运行(onStop()被调用),这些进程并不直接影响用户体验,它们完成了它们的生命周期,系统可以在任何事件杀死这样的进程以便回收内存。通常有许多这样的进程在运行,它们按顺序保持在LRU列表里面,内存不够时可以依次回收。

5. 一个空进程是没有任务激活组件的进程,保持这种进程的原因是缓存用来加速启动。系统经常性的杀死这些进程来平衡系统资源,这些资源主要是系统缓存和空进程缓存。

一个进程的优先级基于其他进程的依赖性也许被提升,例如,假如进程A用Context.BIND_AUTO_CREATE标记被绑定到service,进程B正在用ContentProvider,那么进程B的优先级至少应该跟进程A一样。