
安卓开发文档翻译:应用程序基础,Application Fundamentals
安卓应用 是使用Java 编程语言来编写的。安卓软件开发工具 包中的那些工具会将妳的代码和任何要用到的数据、资源文件打包成一个单独的 APK文件 :即为一个 安卓软件 包 , 它实际上就是一个以 .apk 作为后缀名的压缩文件。 单 个 APK文件 中包含着 一个安卓应用所需的全部内容, 同时作为基于安卓的设备用来安装该应用的安装文件。
一旦安装到了某个设备上去之后,每个安卓应用都在它自己的安全沙箱中运行:
•.安卓系统实际上是一个多用户的Linux系统,其中每个应用都是一个不同的用户。
•.默认情况下,系统会给每个应用赋予一个唯一的Linux用户编号(ID)(这个编号只由系统使用,而应用本身并不知道它的存在)。系统会为一个应用中的所有文件都设置好权限,以使得,只有与该应用相匹配的那个用户编号才能够访问到这些文件。
•.每个进程都有它自己的虚拟机(VM),因此,每个应用的代码都是与其它应用的代码隔离运行的。
•.默认情况下,每个应用都在它自己的Linux进程中运行。每当该应用中的任何一个组件需要被执行时,安卓就会启动该进程,然后,在以下情况下,安卓会关闭该进程:该应用不再需要运行;或者,系统必须为了其它应用的运行而回收内存。
通过 这种方式,安卓系统就实现了 最低权限原则 。具体 就是说,每个应用,在默认情况下,仅仅能够访问 到那些对于 它的运行有必要的组件,而不访问更多的组件。 这样就创造出了一个非常安全的环境,单个应用无法访问 到系统中未对它授予权限的组件。
然而,还是有特殊的手段,使得某个应用可以与其它应用共享数据,也可以访问到系统服务:
•. 可以安排两个应用共享相同的Linux用户编号,在那种情况下,它们能够互相访问对方的文件。出于节约系统资源的方面来考虑, 拥有相同用户编号的不同应用还可以在同一个Linux进程中运行,并且共享同一个虚拟机(这些应用必须使用相同的证书来签名)。
•. 一个应用可以请求获取各种权限以访问设备上数据,例如用户的联系人列表、短信、可挂载的存储设备(SD卡)、照相机、蓝牙等等。应用的所有权限都必须在安装时由用户批准。
以上说明了一个安卓应用是如何存在于系统中的。此文档中的余下部分将向妳介绍:
•.用于定义妳的应用的那些核心框架组件。
•.清单文件,妳将用它来声明应用中的各个组件,并且用来为妳的应用请求获取各种设备功能。
•.一些与应用代码相分离的资源,这些资源使得妳的应用可以优雅地适配到各种各样的设备,产生出最优化的行为。
应用程序组件是安卓应用中必不可少的组成元素。每个组件都是一个不同的入口,系统可通过该入口进入妳的应用程序。并非所有组件都是由用户实际可访问到的入口,并且其中的某些组件会依赖别的组件,但是,其中的每一个组件都是一个独立个体,扮演着一个特殊角色——每一个组件都是一个唯一的组成元素,它们共同定义了妳的应用的整体行为。
有四种不同的应用组件类型。每个类型都是为了不同的目的而存在的,它们都有不同的生命周期,控制着该组件何时被创建,何时被销毁。
以下是四种不同的应用组件:
活动 (Activities)
一个 活动 ,代表着一个拥有用户界面的单个屏幕。例如,在一个邮件应用中,可能会有一个活动显示着一个邮件列表,另一个活动用于编写一封新邮件,再有一个活动用于阅读邮件。尽管这些活动在该邮件应用中形成了一个内聚的用户体验,但是,每一个都是与其它活动相独立的。这样,别的应用也可以应用这些活动中的任何一个(前提是该邮件应用允许这样)。例如,一个照相机应用可以启动该邮件应用中用于编写新邮件的活动,以便让用户分享一张照片。
一个活动是以 Activity 子类的形式来实现的, 妳可以在 活动 开发者指南中了解关于它的更多信息。
服务 (Services)
服务 是一个运行于后台的组件,可用于进行长时间的操作,或者为其它的进程提供服务。服务并不提供用户界面。例如,一个服务可能在用户正操作另一个应用时在后台播放音乐,或者在不阻塞用户对于活动的操作的前提下通过网络获取一些数据。另一个组件,例如某个活动,可以启动该服务并且让它自己运行,或者绑定到它以便与它交互。
一个活动是以 Service 子类的形式来实现的, 妳可以在 服务 开发者指南中了解关于它的更多信息。
内容提供 者 (Content providers)
内容提供 者 管理 着应用数据中的一个可共享的子集。 妳可以将数据储存在:文件系统中、一个 SQLite数据库 中、网络上、或者任何别的可被妳的应用访问到的持久化存储位置。通过 该内容提供者,其它应用 可以查询甚至可以修改该数据 (前提 是该内容提供者允许这么做 ) 。例如,安卓系统提供 了一个用于管理用户的联系人信息的内容提供者。 这样,任何 一个拥有适当权限的应用都可以查询该内容提供者的某个部件 (例如 ContactsContract.Data ) 以读取及写 入 关于某个特定用户 的信息。
内容提供 者还可以用来读取或写入那些 由妳的应用私有并且未共享的数据。例如, 记事本 这个示例应用,就使用一个内容提供者来保存记录。
内容提供 者是以 ContentProvider 子类的形式实现的,必须实现 一组标准的应用编程接口,以使得别的应用 可以进行操作。 欲知更多信息,就参考 内容提供 者 开发 者指南。
广播接收 器 (Broadcast receivers)
广播接收 者 是一个用于对系统范围的广播声明作出响应的组件。 狠多广播都是由系统发起的——例如,一个广播可能会声明屏幕已经关闭 、电池电量低或者已经拍摄了一张照片。 应用程序自己也可以发起广播——例如, 向其它的应用告知,目前已经 向当前设备上下载了一些数据,它们可以使用那些数据了。尽管广播接收 器不会显示出用户界面,但是,它们可能 会 创建 一个状态栏通知 以提示用户,告知当前发生了一个广播事件。然而 ,在更常见的用法当中,广播接收器只起到一个“网关”的作用,将事件转告给其它组件,而自身并不做多少工作。例如, 它可能会启动 一个服务,并且按照该事件的内容来做某项工作。
广播接收 器是以 BroadcastReceiver 子类的形式来实现的 ,每个广播 都是以一个 Intent 对象的形式来传递的。欲知更多信息, 则参考 BroadcastReceiver 类。
安卓系统设计理念中的一个狠独特之处在于,任何应用都可以启动另一个应用的组件。例如,如果妳想让用户通过设备的摄像头来照一张照片的话,可能系统中已经有一个应用能够做到这一点了,于是妳的应用就可以直接使用该应用的功能,而不用亲自实现一个用来照相的活动。妳不需要将该照相机应用中的代码加入到妳的应用中,甚至不需要链接到它的代码。妳所需要做的只是,简单地启动该照相机应用中负责照相的那个活动。在拍照完成之后,该照片甚至会被返回到妳的应用中,这样妳就可以使用它了。对于用户来说,看起来好像照相机就是妳的应用中的一部分一样。
当系统启动一个组件时,它会启动该应用的进程 (前提 是该进程还未运行 ) ,然后 将那些被该组件所依赖的类实例化。例如, 当妳的应用启动该照相机应用中负责拍摄照片的那个活动时, 该活动会运行在属于该照相机应用的进程中, 而不是运行于妳的应用的进程中。因此 , 与大部分其它操作系统中的应用不同的是,安卓应用 并没有一个唯一的入口 (或者 说,没有 main() 函数) 。
因为系统 将每个应用放在单独的进程中运行,并且以文件权限来限制各个应用对于其它应用的访问能力,所以 , 妳的应用无法直接启动另一个应用中的某个组件。但是,安卓系统可以做到。所以 , 要想启动另一个应用中的某个组件的话, 妳必须向系统传递一个消息, 在其中指明 妳的用于启动某个特定组件的意图( intent )。然后 ,系统会为妳启动该组件。
四种类型的组件中,有三种——活动、服务和广播接收器——都是 由一个异步的消息启动的,该异步消息被称作 意图 。意图 会在运行时将单个 的组件绑定到一起 ( 妳可以将它们想像成那种请求 别的组件中的某个动作的信使 ) ,并且无论 该组件是属于妳的应用还是别的应用都可以实现绑定。
意图 是使用 Intent 对象 来创建的,它定义了一条消息,可用于启动某个特定的组件或者是 某种特定类型 的组件——相应 地,一个意图可以是显式的或者隐式的。
对于活动和服务,一个意图会定义好要进行的动作 (例如, 要 "查看" 或 " 发送 "某个东西) ,同时可能会指定要对其进行操作的数据的统一资源标志符(URI)( 以及被启动的组件还需要知道的其它事情 )。例如, 某个意图可能会向一个活动传出一个请求,要求显示 一张图片或者打开 一个网页。 在某些情况下, 妳可以启动一个活动并且从它那里接收到一个结果, 在那种情况下, 该活动也会以一个 Intent 来返回其结果 (例如, 妳可以发出一个意图,让用户选中一个联系人然后将该联系人的信息返回给妳—— 所返回的意图中会包含一个指向所选中的联系人的统一资源标志符 ) 。
对于广播接收器,意图对象只是简单地定义了将要广播的声明内容(例如,一个用于告知设备的电池电量不足的广播,其中只包含一个已知的动作字符串,其意义是"电池电量不足")。
另一个组件类型,内容提供者,不是由意图来启动的。 这种组件,只有在被某个 ContentResolver 中的一个请求命中时才会被启动。内容解析 器会处理与内容提供者之间的所有直接事务, 这样,正在进行相应事务 的那个组件不需要调用内容提供 者的方法,而只需要调用 该 ContentResolver 对象的方法就行了。 这样,就在内容提供 者和请求信息的组件之间产生了一个抽象层 (起到安全作用)。
对于每种类型的组件,都有各自的启动方法:
•. 可以向 startActivity() 或 startActivityForResult() ( 当妳希望该活动传回一个结果时便用这个 ) 传递一个 Intent ,以启动一个活动(或者 给它安排一些新的任务 ) 。
•. 妳可以向 startService() 传递一个 Intent ,以启动一个服务(或者 向一个已经在运行的服务发出新的指令 )。或者 妳可以向 bindService() 传递一个 Intent ,以绑定到该服务。
•. 妳可以向 sendBroadcast() 、 sendOrderedBroadcast() 或 sendStickyBroadcast() 方法传递一个 Intent ,以启动一个广播。
•. 妳可以调用某个 ContentResolver 的 query() 方法,以对一个内容提供者进行查询。
欲知更多关于使用意图的信息,参考 意图 和意图过滤器 文档。 在以下文档中,还提供了关于如何启动特定组件的更多信息: 活动 、 服务 、 广播接收 器 和 内容提供 者 。
在安卓系统启动一个应用组件之前,系统必须从该应用的 AndroidManifest.xml 文件( “清单”文件 )中读取到该组件存在的声明。 妳的应用必须在这个文件中声明它的所有组件, 这个文件必须位于应用程序项目目录的顶层。
除了声明该应用的各个组件之外,清单文件还有好几个作用:
•.标识该应用所要求的任何的用户权限,例如互联网访问权限或者对用户的联系人的访问权限。
•. 基于 该应用所使用的应用编程接口,声明该应用所要求的最低 应用编程接口版本 。
•.声明该应用所要求的硬件或软件特性,例如要有摄像头、蓝牙服务或者多点触摸的屏幕。
•. 还有更多的东西
清单文件的主要作用就是向系统声明该应用的组件。例如,一个清单文件可能像以下例子这样声明一个活动:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:icon="@drawable/app_icon.png" ... >
<activity android:name="com.example.project.ExampleActivity"
android:label="@string/example_label" ... >
</activity>
...
</application>
</manifest>
在 <application> 元素中, android:icon 所指向的资源将作为该应用的图标。
在 <activity> 元素 中, android:name 属性指定 了该 Activity 子类的完整类名, android:label 指定 了一个字符串,将作为该活动的用户可见的标签。
妳必须以这种方式来声明所有的应用组件:
•. <activity> 元素用于声明活动
•. <service> 元素用于声明服务
•. <receiver> 元素用于声明广播接收 器
•. <provider> 元素用于声明内容提供 者
那些包含 在妳的源代码中却又没有声明到清单文件中去的活动、服务和内容提供者,对于系统 是不可见的,因此永远不会被运行。然而 ,广播接收 器,既可以在清单文件中声明,也可以在代码中动态创建( BroadcastReceiver 对象)然后调用 registerReceiver() 注册到系统中。
欲知更多关于如何 在妳的应用中组织清单文件的信息,就参考 AndroidManifest.xml文件 文档 。
之前 在 启动组件 中已经说过, 妳可以使用一个 Intent 来启动活动、服务及广播接收器 。妳可以在意图中显式地指定目标组件 的名字( 即为目标组件的类名 )。然而 ,意图的真正强大之处在于 隐 式意图 的概念。 一个隐式意图,只是简单 地说明要进行的动作类型 ( 以及,可选地, 带上妳要对之进行操作的数据 ) ,由系统 来在设备上找到一个可以完成该动作的组件并且启动它。如果 有多个组件都能够完成该意图中所指定的动作,那么, 会由用户选择一个来最终完成该动作。
系统是如何确认哪些组件能够对某个意图作出响应的呢?原理就是将该意图与设备上其它应用的清单文件中提供的 意图过滤 器 相比较。
当妳在应用的清单文件中声明一个活动时, 可以顺便包含一些意图过滤器,它们 会声明该活动的能力, 以让该活动对其它应用发来的意图作出响应。 妳可以在一个组件的声明元素中加入一个 <intent-filter> 元素, 这样来为该组件声明一个意图过滤器。
例如,假设妳开发了一个邮件应用,其中有一个活动可用于撰写一封新邮件,那么,妳可以声明一个意图过滤器,让它来对“发送”("send")意图(用于发送一封新邮件)作出响应:
<manifest ... >
...
<application ... >
<activity android:name="com.example.project.ComposeEmailActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="*/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
日后 ,当另一个应用创建一个带有 ACTION_SEND 动作的意图并且将它传递给 startActivity() 的时候,系统 就可能启动妳的活动,以让用户撰写并且发送一封邮件。
欲知更多关于创建意图过滤器的信息,则参考 意图 和意图过滤器 文档。
世界上有狠多种运行着安装的设备,并非所有的这些设备都提供了相同的特性和能力的。为了阻止妳的应用被安装到那些缺乏该应用所必需的特性的设备上去,有一件狠重要的事要做,就是,妳要清楚地告知妳的应用所支持的设备的类型,具体做法就是在清单文件中声明对于设备和软件的要求。大部分这种类型的声明都是建议性的,系统并不会读取它们,但是外部的某些服务,例如Google Play,会读取它们,以便在用户从自己的设备上搜索应用时提供过滤功能。
例如,假设 妳的应用要求设备上有个摄像头并且使用了安卓 2.1( 应用编程接口版本 7)中引入的应用编程接口,那么 , 妳应当像这样在清单文件中声明这些要求:
<manifest ... >
<uses-feature android:name="android.hardware.camera.any"
android:required="true" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
...
</manifest>
这样, 不带 摄像头的设备,以及安卓系统版本 低于 2.1的设备,都无法从 Google Play 中安装妳的应用。
然而 , 妳还可以声明妳的应用会使用摄像头却不 要求 带有摄像头。 在那种情况下,妳的应用必须 将 required 属性设置 为 "false" ,并且 在运行时检查该设备是否带有摄像头, 在必要的情况下禁用掉摄像头相关的功能。
欲知更多关于如何管理 妳的应用在不同设备上的功能的信息, 则参考 设备能力 文档。
一个安卓应用不仅仅是由代码组成的——它还需要一些与源代码分离的资源,例如图片、声音文件以及任何其它与应用的视觉外观相关的东西。例如,妳应当以XML 文件来定义活动的用户界面中的动画、菜单、样式、颜色及布局。利用应用的资源,可以轻易地在不修改代码的情况下更新妳的应用的各种各样的特性,并且——通过提供若干组替代资源——使得妳能够针对各种不同的设备配置情况(例如不同的语言和屏幕尺寸)来对应用进行优化。
对于 妳在安卓项目中包含的每个资源,软件开发工具 包中的构建工具都会定义一个唯一的整数编号, 妳可以在应用的代码中使用这个编号来引用到该资源, 也可以在使用XML 定义的其它资源中引用该资源。例如,假设 妳的应用中包含一个名为 logo.png 的图片文件(保存 在 res/drawable/ 目录 中 ),那么 ,软件开发工具 包中的那些工具会生成一个名为 R.drawable.logo 的资源编号, 妳可以使用该编号来引用到该图片,并且将它加入到妳的用户界面中。
对于 将资源与代码相隔离这一点, 狠重要的一个好处就是, 可以为不同的设备配置情况提供不同的替代资源。例如, 将用户界面的字符串定义在 XML中, 这样,妳就可以将字符串翻译成其它国家的语言并且保存在单独的文件中。日后 ,根据 妳附加到该资源目录名中的一个语言 标识 符 (例如 res/values-fr/ 表示法国语言 ( French )的字符串值 )以及用户本身的语言设置,安卓系统 就能够 将适当的语言字符串显示到妳的用户界面上。
安卓支持 用狠多不同的 标识符 来表示替代资源。 该标识符是一个短的字符串, 妳将该字符串加入到资源目录名中去, 以定义那些资源应当用于哪种设备配置情况。 再举一个例子, 妳一般都应当为妳的活动创建不同的布局文件,它们分别匹配 设备的不同朝向及尺寸。例如,如果设备 的屏幕是竖向的(高度 比宽度大 ),那么 , 妳可能会在布局中将按钮竖向排列 ,相反 地,如果设备的屏幕是横向的(宽度 比高度大 ),那么 ,应当将按钮水平排列。 要想依据设备朝向来改变布局的话, 妳可以定义两个不同的布局,并且 给每个布局目录名中加入一个适当的标识符。日后 ,系统就会根据设备当前的朝向来自动应用适当的布局。
欲知更多关于 妳能够在应用中包含的不同类型的资源的信息,以及如何针对 不同的设备配置情况创建替代资源的信息, 则参考 提供资源 。
HxLauncher: Launch Android applications by voice commands