1.1 Application Fundamentals - 应用原理
Android应用是使用Java编程语言编写的。Android SDK工具把代码、资源和数据文件编译为一个Android包,这是一个有.apk后缀的压缩文件。一个单独的.apk文件里包含所有的代码,这被当作一个应用,而且这个.apk文件是Android设备用来安装应用的文件。
一旦在设备上安装了应用,那么每个应用都在自已安全的沙盒里运行:
1. Android操作系统是一个多用户的Linux操作系统,在这个系统里,每个应用都是一个不同的用户。
2. 默认地,系统把每一个应用都标识为一个独一无二的Linux用户ID(这个ID仅仅可以被系统所使用,对于应用程序来说,它是不可见的)。系统为每个应用的所有文件设置权限,因此,只有和应用用户ID相匹配的应用才可以读取应用的文件。
3. 每个进程都有自已的虚拟机(VM),因此,每个应用的代码都独立地运行着。
4. 默认地,每个应用都在它自已的Linux进程里运行。当应用的任何一个组件需要被执行时,Android系统都会运行这个进程,然后完全停下不再需要的进程,或者恢复其它应用占有的内存。
在这种方式中,Android系统实现了最小特权原则。也就是说,每个应用默认都只能访问它完成工作所必须的组件,不会访问其它的。这样做就创建了一个非常安全的环境,在这样的环境里,如果一个应用不给分配权限的话,它就不能访问系统的任何内容。
虽然如此,但是,对于一个程序,把数据共享给其它应用,或者从系统服务中获取东东,有下面几种方法:
1. 可以让两个应用共享同一个Linux 用户ID,在这种情况下,这两个应用可以获取另外一个的文件夹。为了保护系统资源,有相同用户ID的程序可以运行在同一个Linux进程中,共享着相同的虚拟机(应用程序也应该有着同样的证书签名)
2. 应用程序也可以通过请求权限的方式来获取设备的数据,比如联系人信息,短信,存储设备(SD card),照相机,蓝牙,等等。所有应用程序的权限在安装时候会展示给用户,用户同意后才能安装。
关于Android应用程序如何在系统中存在的基本知识,本文档的其余部分向你介绍:
1. 定义了应用程序的核心框架组件。
2. 申明了应用程序用到的组件和所需的设备元素的mainfest文件。
3. 独立于应用代码的资源,可以让你的应用极大的优化它在各种配置设备的表现
应用组件
对于一个Android应用来说,应用组件是构建应用所必须的模块。每一个组件都是一个不同的点,系统可以通过这些点进入你的应用。对于用户来说,不是所有的组件都是真正的入口点,但是这些组件之间都有着相互的依赖性-每个组件都是独一无二的模块,这些模块有助于定义应用的整体行为。
有四种不同类型的应用组件。每一种类型的组件都为一个明确的意图服务,也都有明确的生命周期,生命周期定义了如何创建和销毁它。
下面就是应用组件的四种类型:
Activity
一个Activity代表了用户界面的一个单独的屏幕。例如,邮件相关的应用或许应该有一个Activity来展示新邮件的列表,另外一个Activity来写邮件,再一个Activity来读取邮件。虽然在邮件应用中这些Activity一起工作,形成了一个完整的用户体验,但是,每一个部分又是独立工作的。例如,另外一个应用可以启动上面这些Activity中的任何一个Activity(如果邮件应用允许的话)。例如,照相应用可以可以启动邮件应用里的写新邮件的应用,这样做对用户来说,就可以分享照片了。
一个activity类就是一个实现了Activity 的Java类。在Activities开发者指导里,你可以获取更多的知识。
服务
服务是运行在后台,执行耗时较长的操作,或者甚至执行远程进程的操作。服务不提供用户界面。例如,当用户来到其它应用时,服务会负责在后台播放音乐,或者说,服务不会锁住用户和activity的交互,从网络上获取数据。比如一个activity,可以启动一个服务,并可以让它运行或者邦定到这个activity,以便与其进行交互操作。
一个服务是作为Service子类来实现的,在Services开发指南中,你能学到更多关于它的使用。
Content providers - 内容提供
内容提供管理共享的应用数据集。你可以把数据存储在系统中,SQLite数据库中,内页中,或者其它任何你可以从中获取数据的本地持久性存储。通过内容提供,其它的应用可以查询数据,甚至修改数据(如果内容提供允许的话)。例如,Android系统提供了管理用户联系人数据的内容提供。这样的话,任何具有合适权限的应用都可以内容提供(例如ContactsContract.Data)来对一个指定的联系人进行读或写的操作。
内容提供对于操作你应用中没有共享的私有数据也是有用的。例如,Note Pad示例程序就使用了内容提供来保存笔记。
内容提供类是ContentProvider的子类,必须实现一系列标准的APIs,以让其它的应用能执行交换操作。在Content Providers开发者指导中,你可以学到更多的知识。
Broadcast receivers - 广播接收器
广播接收器是一个响应系统范围广播公告(通知)的组件。许多广播信息都来源于系统,比如,通知屏幕关闭的广播,系统电量低的广播,或者是拍了一张照片的广播。应用也可以创建广播,例如,在下载完某些数据时发送一条广播,让其它应用知道数据下载完成,可以使用了。尽管广播接收器不显示一个用户界面,但是广播接收器可以创建一个状态栏通知,来通知用户。但更多情况下,一个广播接收者只是一个其他组件想要做极小量事件的一个"gateway”(途径)。举例,它可能发起一个服务,去执行关于某个事件的一些工作。
一个广播接收者,是当作BroadcastReceiver子类被实现的。每个广播接收者都是从Intent对象衍生出来的。更多信息,请参考BroadcastReceiver。
Android系统设计的一个独一无二的方面是任何应用可以启动另外一个应用的组件。例如,如果你想让用户使用设备的照相机进行拍照操作,拍照功能很有可能是另外一个应用的功能,但是你的应用需要使用它,那你就不需要再在你的应用中开发拍照的功能了。你只需要调用拍照应用来进行拍照就可以了。拍照完成时,拍摄的照片会返回给你的应用,这样,你就可以对照片进行处理了。对于用户来说,拍照功能好像就是你应用的功能一样。
当系统调用一个组件时,系统会开启组件所在应用的进程(这个应用可能没有运行)并且实例化组件所需的类。例如,如果你的应用调用拍照程序拍照的功能,那么系统就会启动照相的进程,这个进程属于拍照应用,不属于你的应用。因此,和其它系统里的应用不一样,Android应用不仅仅只有一个入口点(例如,没有main()函数)。
因为系统的每一个应用,在一个有文件权限的单独的系统里运行,因此,其它应用不能访问,你的应用也不能直接激活其它应用的组件。虽然如此,但是,Android系统也能激活其它应用的组件,你必须给系统发送一个意图来启动一个指定的组件。然后,系统就会为你启动这个组件。
Activating components - 激活组件
四个组件类型中的三个:activity,service,broadcast receiver,是被叫做intent的异步消息激活的。在运行时,关于使用intent的更多信息,详情请参见Intents and Intent Filters章节。把一个单独的组件和其它组件相互绑定,而不管这个组件是否属于你的应用(你可以把它们想像为一个消息,一个用于请求其它组件的动作)。
一个Intent由一个Intent对象创建,在intent里,包含了一个激活组件的消息,这个组件要么是一个指定的组件,要么是指定类型的组件。可以是显式的,也可以是隐式的。
对于activity和service来说,intent里定义了执行的动作(例如,转到一个视图或是发送一些消息等),也可能定义了数据在哪个URI上执行(这些中的一些,是组件启动时就需要知道的)。例如,一个intent可能是传递一个activity要浏览图片或是打开网页的请求。在某些情况下,你可以启动一个activity来接收这个结果,在这种情况下,activity会使用intent来返回结果(比如,你可以指示一个intent,让用户取一个人的联系方式,并返回给你,返回的intent中会包含一个指向选定联系方式的URI)。
对于广播接收器来说,intent仅仅只是定义了被广播的公告的内容(设备电量低的广播仅仅只是包含了一个已知的字符串“battery is low”)。
对于内容提供组件来说,它不会被intent激活,它会被内容解释者(ContentResolver)所请求的目标所激活的。内容解释者,处理所有与内容提供者的直接交换。所以组件不需要执行与提供者交换,而是调用ContentResolver对象方法。为了安全起见,组件请求信息与内容提供者之间有一个抽象层。
下面是分别激活这几种类型组件的方法:
1. 你可以通过给startActivity()或startActivityForResult()(如果你想让启动的activity返回一个结果)方法传递intent参数来启动一个activity(或是做一件新的事情)。
2. 你可以通过给startService()方法传递一个intent来启动一个service(或者给一个正在进行的service添加一些新的说明)。或者你可以通过给bindService()方法传递一个intent参数来绑定到service上。
3. 你可以通过给sendBroadcast()、sendOrderedBroadcast()、sendStickyBroadcast()方法传递一个intent来实例化一个广播。
4. 你可以通过在一个ContentResolver上调用query()方法来在一个内容提供上执行一个查询。
关于使用intent的更多信息,详情请参见Intents and Intent Filters章节。四种类型的组件也相应的提供了文档:Activities、 Services、 BroadcastReceiver 、Content Providers。
Minfest文件
在Android系统启动一个应用组件之前,系统必须通过读取应用的AndroidMainfest.xml。在这个文件中,你必须申明你在应用中所使用的所有的组件,该文件必须位于项目文件的根目录下。
为了申明应用的组件,mainfest文件做了一系列的事情,例如:
1. 标识程序所需的任何的权限,例如访问网络的权限,读取用户联系人的权限。
2. 申明应用运行时需要的最小的API Level,应用会基于这个API运行的。
3. 使用应用用到的或是需要的软硬件需求,例如照相机,蓝牙,或多点触摸屏。
4. 应用需要链接的API库(而不是Android框架API),比如Google Maps library。
5. 其它。
申明组件
mainfest文件最基础的任务就是通知系统应用有哪些组件。例如,mainfest可以按照下面展示的方式来申明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属性指定了用户可见的标签文本。
你必须使用下面的方式来申明应用的所有组件:
1. <activity> - activity组件
2. <service> - service组件
3. <receiver> - broadcast receiver组件
4. <provider> - content provider组件
在你项目中包含了,但是没有在mainfest文件里申明的activity、service和content provider组件,对于系统来说是不可见的,当然,也是不能用的。虽然如此,但是,broadcast receiver要么在mainfest里申明,要么使用代码动态生成(使用BroadcastReceiver),然后通过调用registerReceiver()方法在系统里进行注册,这都是可以的。
关于如何为应用构建mainfest的更多知识,请参见The AndroidManifest.xml File文档。
申明组件功能
正如上面激活组件段落描述的那样,你可以使用一个intent来启动activity、service和broadcast receiver。你也可以在intent中显式的指定目标组件(使用组件类名)。然而,intent真正的强大之处大于它的动作-即intent action。利用intent的动作,你只须简单的描述你要执行的action类型(做为可选操作,你可以给添加执行动作相关的数据),并且允许系统在设备上找到一个组件,这样就可以执行那个动作并启动它。如果有多个组件可以执行intent指定的action,那么用户可以选择执行哪一个。
系统指定响应intent组件的方式是通过比较设备上其它应用的mainfest文件里提供的可接收的intent的过滤器后决定的。
当你在你应用的mainfest里申明了一个组件时,作为可选操作,你可以包括intent filters(意图过滤器)来指定组件的功能,这样的话,该组件就能响应来自其它组件的intent。你可以通过给申明组件的元素标签里添加<intent-filter>标签来给你的组件申明一个/组意图过滤器。
例如,包含写邮件页面的邮件应用或者会在mainfest里申明一个意图过滤器来响应发送的intent(为了能"send"邮件)。然后在你的应用中可创建一个有"send"动作的intent(ACTION_SEND),当你使用startActivity()方法来执行intent时,系统会匹配到邮件应用里的"send" activity并执行它。
关于使用intent的更多信息,详情请参见Intents and Intent Filters章节。
申明应用的需求
有很多的Android设备,但是它们中不是所有都提供同样的元素和能力。为了阻止你的应用安装在缺少你应用所需元素的设备上,通过在你的manifest文件中声明软件硬件要求,明确的指出你的应用支持的硬件类型是非常重要的。大多数的申明只是信息的描述,系统也不会读取它们,但是外部的设备,比如Google商店,会读取它们,这样做的目的是,当用户在它们的设备上搜索应用时,可以提供过滤。例如,如果你的应用需要照相机,而且使用的是从Android(API Level7)开始引入的APIs,那么,你应该在你的mainfest文件里申明这些需求。这样做了,没有照相机、或者低于2.1版本的设备就不能从Google商店里安装你的应用了。
然而,你也可以申明你的应用使用了照相机,但是并不依赖于它。在这种情况下,当设备没有照相机并且照相功能被使用时,就需要你在应用运行时做检查,并做提示。
当你在设计和开发应用时,你应该考虑一些重要的设备特性:
1. 屏幕尺寸和密度。
为了通过屏幕类型来进行设备的分类,Android为每个设备定义了两个特性:屏幕尺寸(屏幕的物理尺寸)和屏幕密度(在屏上的像素的物理密度,或者dpi--每英寸的点数)。为了简化屏幕配置的所有不同类型,Android系统把它们分成可选的组,以便更容易定位。
屏幕大小:小,正常,大和极大
屏幕密度:低密度,中密度,高密度,和极高密度
默认情况下,你的应用是兼容所有屏幕尺寸和密度的,因为Android系统对此做了适当的调整,以使得它适合你的UI布局和图像资源
然而,你应为某个屏幕尺寸创建特殊的布局,并为某些密度提供特定的图像,使用可选的资源,并在你的manifest文件中用<supports-screens> 元素声明,以明确指出你的应用支持的屏幕尺寸.
更多信息,参考Supporting Multiple Screens文档。
2. 输入配置
许多设备为用提供了一个不同类型输入装置,比如,硬件键盘,轨迹球,five-way导航pad.如果你的应用必须要一个特别的输入硬件,那么你应在你的应用中使用<uses-configuration>元素声明.但时,应用必须要一个特别的输入配置的情况是极少的。
3. 设备特性
在一个装有Android的设备中,有许多软硬件特性,有可能有,或有可能没有。比如照相机,光敏器件,蓝牙,或某个版本的OpenGL,或者触模屏的精度。你应该从不假设,在所有的装有Android的设备中某个特点是可用的(除了标准的Android库),所以你应该用 <uses-feature>元素声明你的应用支持的特征.
4. 平台版本
不同的Android设备,经常运行不同的Android平台版本,比如Android1.6或者2.3。每一个成功的版本通常包括在前一个版本中不可用的API。为了指出,那些APIs集是可用的,每个平台版本指定了一个API Level(比如, Android 1.0 is API Level 1 and Android 2.3 is API Level 9)。如果你使用的APIs是在1.0版之后,加入到平台的,你应该用<uses-sdk>元素,声明最小API级别,这样就指出了那些API将被采用。
为你的应用声明所有必要性的要求非常重要。因为,当你把你的应用发布到Android市场,市场将用这些声明信息来过滤出,哪些应用在每个设备是可用的。 同样,你的应用应该只能在满足所有你应用需求的设备上才可用。
更多关于Android市场如何基于这些需求过滤的,请看Filters on Google Play文档
应用资源
一个Android应用不仅仅由代码组成,它还需要从代码中分离出的资源,比如图片,音频文件,及与应用可显图像任何其他相关的。比如,你应该定义动画,菜单,风格,颜色,和用XML文件定义活动的布局。使用应用资源,能让你的应用在不修改任何代码的情况下容易的升级各种特性---并且通过提供一套可选取的资源--能优化你的应用在各种配置不同的设备中的表现(比如不同的语言和屏幕尺寸)。对于每个包含在你的Android工程中的资源,SDK将其定义成一个唯一的整型ID,这样你就可以在你的代码中或在XML文件中定义的其他资源中引用它。如果你的应用包括一个图片名字是logo.png(保存在res/drawable/目录 ),SDK工具将生成一个资源ID命名成R.drawable.logo,你可以用它来引用图片,并插入你的用户界面中
提供与代码分开的资源的一个很重要的方面是,使得你能为不同的配置的设备提供可选资源.比如,在XML中定义UI字串,你可以把字串翻译成各种不同的语言并保存在不同的文件中.然后,以基于语言限定词,你可以追加资源目录名(比如res/values-fr/ 用语法语资源),和用户语言设置,Android系会将相应的资源应用到你的UI中。
Android在可选资源上,支持许多不同的qualifiers (限定词)。限定词是一个包括在你的目录名中的一个简短的字串,是为了定义那些资源将用在某些配置的设备上。再如,由于设备的屏幕的方向和尺寸不同,你通常需要为你的活动定义不同的布局.比如,若设备的屏幕是竖向(高),你可能要一个带有重直button 的布局,当屏幕是横向的(宽),按钮应是水平对齐的。要根据方向来改变布局,你要定义两个不同的布局,并在布局的目录名中使用相应的限定词(qualifier)。然后,系统将自动根据当前的设备朝向来应用相应的布局.
要详细了解,你的应用中能包含的各种资源,及如何为各种配置的设备创建可选资源,请看Application Resources开发指南