Android应用程序是用Java编程语言编写的。Android SDK工具把应用程序的代码、数据和资源文件一起编译到一个Android程序包中(这个程序包是以.apk为后缀的归档文件),一个Android应用程序就是一个单独.apk文件中的所有内容,并且Android设备使用这个文件来安装应用程序。
安装在设备上的每个Android应用程序都生活在它们自己的安全沙箱中:
1. Android操作系统一个多用户的Linux系统,在这个系统中每个应用程序都是一个不同的用户。
2. 默认情况下,系统给每个应用程序分配一个唯一的Linux用户ID(这个ID只能被系统使用,并且对于应用程序是未知的)。系统给应用程序中的所有文件设置权限,以便只有跟用户ID匹配的应用程序能够访问他们。
3. 每个进程都有它们自己的虚拟机,因此应用程序的运行是彼此隔离。
4. 默认情况下,每个应用程序运行在它们自己的Linux进程中,当任何应用程序需要被执行时,Android启动这个进程,在不再需要的时候或系统必须为其他应用程序恢复内存时这个进程被关闭。
Android系统实现了最小特权原则,也就是说,默认情况下,每个应用程序只能访问支持它工作的必须的组件。这样就创建了一个安全的环境,在这个环境中应用程序不能访问系统没有给它授权的部分。
但是,还有一些应用程序间共享数据和应用程序访问系统服务的方法:
1. 两个应用程序共享相同的Linux用户ID是可能的,这样它们就能够访问彼此的文件。为了节省系统资源,拥有相同用户ID的应用程序也可以运行在相同的Linux进程中,并且共享相同的虚拟机(应用程序必有拥有相同的数字签名)
2. 应用程序能够请求访问设备数据的权限(如用户的通讯录、短信、可安装的存储设备、照相机、蓝牙等等),所有的应用程序的权限都必须在按照时被用户授予。
在这篇文章的剩余部分会介绍Android应用程序是如何存在与系统中的基础知识:
1. 定义应用程序的核心框架组件;
2. 在manifest文件中为应用程序声明组件、请求设备功能;
3. 把应用程序的代码与资源分离,并且允许应用程序使用各种设备配置优化它们的行为。
应用程序组件
应用程序组件是Android应用程序的重要基石,每个组件都是系统进入应用程序的不同入口,对于用户来说,不是所有的组件都是实际的入口,并且有一些是彼此依赖的,但是每一个组件都存在它们自己的实体,并且扮演着特殊的角色---它们都是帮助定义应用程序整体行为的唯一的模块。
应用程序有四种不同类型的组件,每种类型服务一个不同的目的,并且有一个定义组件如何创建和摧毁不同的生命周期。
以下是应用程序组件的四种类型:
Activites
一个Activity代表一个和用户接口的单独屏幕。例如,一个email应用程序可以有一个显示新邮件列表的Activity,一个写邮件的Activity和一个读邮件的Activity。在邮件应用程序中虽然这些Activity一起工作,从而形成统一的用户体验,但是它们是彼此独立的。这样,一个不同的应用程序能够启动这些Activity中的任何一个(如果邮件应用程序允许)。例如,一个相机应用程序为了给用户共享一张图片,可以启动邮件程序中编写新邮件的Activity。
一个活动是作为一个Activity子类实现的,可以在Activities开发者指南中学到更多的内容。
Services
Service是一个运行在后台的组件,用于执行长时操作或执行远程处理工作。Service不提供用户接口。例如,一个Service可能在后台播放音乐而用户却在使用另一个不同的应用程序,也可以是一个不带有Activity用户接口的从网络上获取数据的Service。
Content providers
Content provider管理一个共享的应用程序数据集,可以在文件系统(一个SQLite数据库、在网络上、或者其他的应用程序能够访问的本地的持久化的存储介质)中保存数据,通过content provider,其他的应用程序能够查询或编辑(如果content provider允许)数据。例如,Android系统提供了一个管理用户通讯录的content
provider。这样,任何带有适当授权的应用程序都能够查询由content provider读/写的数据。
Content provider也可用于读写应用程序的私有数据而不是共享,例如Note Pad程序就是用一个content provider来保存注释。
Content provider是做为ContentProvider的一个子类来实现的,并且必须实现一组标准的使其他应用程序能够执行事务的API。
Broadcast receivers(广播接收器)
Broadcast receivers是一个响应全系统广播通知的组件。系统有很多广播源,例如屏幕关闭的一个广播通知、电池电量不足通知、抓图通知等。应用程序也能启动广播,例如让其他的应用程序知道某些数据已经下载到设备上,并且应用可以有效使用。虽然Broadcast receivers不显示用户接口,但是他们可以创建状态栏通知,提醒用户广播事件发生了。更常见的是,Broadcast
receivers只是作为其他组件的一个网关,并且做很少的工作。例如,它可以启动一个执行某些基于事件来工作的服务。
Broadcast receiver是做为BroadcastReceiver的一个子类来实现的,并且每个广播作为一个Intent对象来分发。
Android系统设计独特一面是任何应用程序都能够启动另外应用程序的组件。例如,如果想要用户使用相机设备拍照,就有可能在一个应用程序中拍照而在另外一个应用程序中使用照片,而不需要在自己的应用程序中开发一个用于拍照的Activity。你不需要合并或事件联接来自相机应用的代码,而是简单启动照相机应用中用于拍照的Activity。当拍照完成,这个事件就返回到你的应用程序以便你能够使用照片。对于用户来说,照相机应用程序就好像你的应用的一部分。
系统启动一个组件时,它会启动那个应用的进程(如果应用未运行),并且初始化组件需要的类。例如,如果你的应用程序启动照相机应用中用于拍照的Activity,这个Activity运行在属于照相机应用的进程中,而不是你自己的进程中。因此,跟大多数其他系统不一样,Android应用程序没有一个单独的进入点(例如,没有main()方法)。
由于系统在一个独立的限制访问其他应用文件的进程中运行每个应用程序,所以应用程序不能直接激活来自其他应用程序的组件。但是,Android系统可以做到,因此要激活另一个应用程序中的一个组件,就必须发一个消息给系统,指定打算启动的特定的组件,然后系统为你激活这个组件。
激活组件
四种类型组件中的三种(Activities、Services、Broadcast receivers)是通过被叫做Intent的异步消息激活的。Intents在运行时绑定彼此独立的组件(可以把它想象成一个来自其他组件的请求一个动作信使),而不关注组件是属于应用自己还是其他的应用程序。
一个Intent就是一个Intent对象,它定义了一个消息,既可以激活指定的组件也可以激活指定的组件类型,一个Intent既可以是明确的也可以是隐含的。
对于Activities和Services,一个Intent定义了要执行的动作(例如:浏览或发送等事情),并且可以指定数据位置(对于正在启动的组件可能需要了解这些信息)。例如,一个Intent可能传达一个请求,要一个Activity显示一张图片或打开一个Web网页。在某些场合,还可以启动一个Activity来接收一个结果,这个Activity也返回Intent中的结果(例如:你可以发出一个Intent,让用户选择一个个人的联系方式,并且要它返回给你,返回的Intent包括一个指向选择的联系方式的位置)。
对于广播接收器,Intent简单的定义了广播的通知(例如,一个指示设备电量不足的广播,只包含了一个已知行为的字符串:电池电量不足.)。
另一个组件类型—Content Provider不是用Intents来激活的,它是在来自一个ContentResolver对象请求目标时被激活。内容解析器使用Content Provider处理所有的直接事务,以便组件不必在用Content Provider来执行事务,而是调用ContentResolver对象上的方法。这样就在内容提供者和组件请求信息之间形成了一个抽象层。
每种类型组件都有独立的方法来进行激活:
1. 通过传递一个Intent给startActivity()或startActivityForResult()(需要Activity返回一个结果时调用这个方法)方法能够启动一个Activity(或者让这个Activity做一些新的事情。)
2. 通过传递一个Intent给startService()方法来启动一个Service(或者给一个运行的Service发送新的指令),也可以通过传递一个Intent给bindService()方法绑定Service
3. 通过传递一个Intent给sendBroadcast()、sendOrderedBroadcast()、sendStickyBroadcast()这样的方法可以启动一个广播;
4. 通过调用ContentResolver上的query()方法,可以执行一个对Content Provider的查询。
关于使用Intents的更多信息,可以看目的和目的过滤器(Intents and Intent Filters)。关于激活特定组件的更多信息在下列文档中也提供了:Activities、Services、BroadcastReceiver和Content Providers。
清单文件
Android系统在启动一个应用程序组件之前,必须通过阅读应用程序的AndroidManifest.xml文件来了解组件的存在情况。应用程序必须在这个文件中声明所有的它用到的组件,这个文件必须在应用项目目录根目录。
清单文件除了声明应用程序组件之外,还做了许多其他的事情,如:
1. 标识应用程序需要的用户权限,如:互联网访问或读取用户联系人的权限等;
2. 声明应用程序需要的最小API级别,也就是要说明应用程序使用的是基于那个级别的Android API;
3. 声明应用程序使用或需要的硬件和软件的功能,例如照相机、蓝牙服务、或多点触摸;
4. 应用程序需要的API库链接(Android框架API除外),如Google地图类库。
声明组件
清单(Manifest)的主要任务是通知系统应用程序的组件构成,例如,清单文件能够像下例那样声明一个Activity:
<?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指定了一个用于显示给用户的字符串标签。
必须使用以下方法声明所有的应用程序组件:
1. <activity>元素用于声明Activities
2. <service>元素用于声明Services
3. <receiver>元素用于声明Broadcast receivers
4. <provider>元素用于声明 Content Providers
在源代码中包含的Activities、Services、和Content
Providers没有在清单文件中声明,对系统就是不可见的,所以就不能运行。但是Broadcast Receivers既可以在清单文件中声明也可以在代码中动态的创建(如BroadcastReceiver对象),然后通过调用registerReceiver()方法在系统中注册。
关于应用程序的清单文件结构的更多内容,可以查看The AndroidManifest.xml File文档。
声明组件的能力
像Activating Components一节中讨论的那样,可以使用一个Intent来启动Activities、Services和Broadcast Receivers。通过明确的指定在Intent中的目标组件的名字(使用组件的类名)就可以做这件事。但是Intent的实际能力在于Intent行为的概念。使用Intent行为,可以简单的描述想要执行的行为的类型(并且可选择型的描述在希望执行行为之上需要的数据),并且允许系统在设备中查找能够执行行为的组件并启动它,如果有多个能够执行被Intent描述的行为的组件,那么用户可以选择使用其中之一。
系统通过比较设备上其他应用程序的清单文件中提供的Intent过滤器收到的Intent来识别组件能够响应的一个Intent。
在应用程序清单中声明一个组件时,能够选择性的包含具有声明组件能力的Intent过滤器,以便能够响应来自其他的应用程序的Intent。通过在组件的声明元素中添加一个<intent-filter>子元素,可以给组件声明一个Intent过滤器。
例如,用一个Activity来写信邮件的邮件应用程序,可能在它的清单中声明一个Intent过滤器,以便进入响应“发送“的Intents(为了发送邮件),然后,应用程序中的一个Activity能够创建一个带有“发送”行为的Intent,在你用startActivity()方法调用Intent是,系统会匹配邮件应用程序的“发送”Activity,并且加载它。
关于创建Intent过滤器的更多信息,可查看Intents and Intent Filters文档。
声明应用成的要求。
Android支持很多设备,并且所有这些设备所提供的功能都不尽相同。为了防止应用程序被安装在那些缺少应用程序所需要的功能的硬件设备上,可以在应用的清单文件中声明设备和软件要求,以便清晰的定义一个应用程序所支持的设备类型的简介。这些声明的大多数都只是信息式的,系统不会读取它们,但是另外的服务(如Android
Market)为了给用户提供过滤服务,会在用户的设备上搜索这些信息。
例如,如果应用程序需要使用照相机和Android2.1中APIs(API Level 7),就应该在应用程序的清单文件中声明这些需求,这样,没有照相机并且Android版本低于2.1的设备就不会动Android Market上安装这个应用程序。
虽然你可以声明你的应用程序使用了照相机,但这不是必须的,如果没有声明,你的应用程序就必须在运行时执行一些检查,判断设备是否有照相机功能,如果照相机功能无效,那么应用程序中一些相关功能就需要禁用。
一下是在设计和开发应用程序时应该重点考虑的一些设备特征:
屏幕大小和分辨率
为了通过屏幕类型区分设备,Android为每个设备定义了两个特征:屏幕大小(屏幕的物理尺寸)和屏幕分辨率(在屏幕上点的物理密度,或dpi---每英寸的点数)。为了简化所有的不同屏幕类型的配置,Android系统把它们归纳到更容易定位的选择组。
屏幕的大小是:小、普通、大、超大;
屏幕的分辨率是:低分辨率、中分辨率、高分辨率和超高分辨率。
默认情况,应用程序会兼容所有的屏幕大小和分辨率,因为Android系统会对你的UI布局和图片资源进行相应的调整。但是,对于确定的屏幕尺寸,你应该创建特定的布局,并且对于确定的分辨率要提供特定的图片,也可以使用布局资源,并且在应用程序的清单中使用<supports-screens>元素明确的声明应用程序支持的屏幕尺寸。
更多信息,请看Supporting Multiple Screens文档。
输入配置
很多设备提供了不同的用户输入机制,如键盘、轨迹球、或五向导航板。如果你的应用程序需要特殊的输入硬件类型,那么就应该在清单的<uses-configuration>元素中声明。但是,应用程序还是很少需要明确的输入配置。
设备功能
在给定的Android设备上有许多硬件和软件功能存在或不存在,如照相机、亮度传感器、蓝牙、OpenGL的某个版本、或触屏的精度等。你不应该假设某个功能在所有的Android设备上都是有效的(标准的Android类库除外),因此你应该用<uses-feature>元素声明应用程序使用的所有功能。
平台版本
不同的Android设备经常运行着不同的Android平台的版本,如Android1.6或Android2.3.每个后续的版本经常会包括以前版本中额外的无效的APIs。为了指明APIs集合的有效性,每个平台版本都指定了一个API级别(例如,Android1.0的API级别是1,Android2.3的API级别是9)。如果你使用任何在1.0版本以后添加的APIs,你应该使用<uses-sdk>元素来声明最小的API级别。
为你的应用程序声明所有这些要求是至关重要的,当你把应用程序发布到Android Market时,Market使用这些声明来过滤在每个设备上有效的应用程序。正因如此,你的应用程序应用只对所有的满足你的应用程序要求的设备有效。
关于Android Market基于这些要求过滤应用程序的更多信息,请看Market Filters文档。
应用程序资源
一个Android应用程序只是由代码组成的,它需要独立与代码之外的资源,如图片、音频文件、以及任何有关应用程序的视觉表现部分等等。例如,你应该使用XML文件来定义用户界面的动画、菜单、样式、颜色、以及Activity的布局等。使用应用程序资源使更新应用程序的各种功能更加容易,而不必编辑代码,通过提高可选的资源集。能够使你针对各种设备配置优化应用程序(例如不同的语言和屏幕尺寸)。
对于在你Android项目中包含的每种资源,SDK编译工具都给定义了一个唯一的数字ID,你能够使用XML文件中定义的这个ID在应用程序的代码或其他的资源中引用这个资源。如,如果应用程序中包含了一个名叫logo.png的图片文件(保存在res/drawable/目录中),SDK工具就会生成一个叫做R.drawable.logo的资源ID,你能够通过这个ID来引用这个图片,并把它插入到用户界面中。
把资源与代码分离的重要原因之一是给不同的设备配置提供可选资源的能力。例如,通过在XML文件中定义UI字符串,你能够把这些字符串翻译成其他的语言,并且把它们保存到一个独立的文件中,然后基于一个语言预选器(你追加的资源目录名,如res/values-fr/目录中保存法语字符串)和用户的语言设置,Android系统就会在UI中使用适当的语言字符串。
Android支持很多不同选择资源的预选器,预选器是一个包含在资源目录名中的简短的字符串,以便定义设备配置应该使用哪些资源。如,根据设备屏幕的取向和尺寸,针对Activity应该创建不同的布局,当设备屏幕纵向高的时候,你可能想要按钮垂直布局,而当屏幕横向宽的时候,你可能想要按钮水平布局。要改名依赖取向的布局,你可以定义两个不同的布局并且用每个布局的目录名作为相应的预选器,然后系统就会根据当前屏幕的取向,自动的使用相应的布局了。
关于应用程序中能够包含的资源种类以及如何为不同设备配置创建可选资源,请看Application Resource开发指南。