android学习指南(2)-应用程序的基本要素
应用程序的基本原理
Android 应用程序是以Java语言编写的. Android SDK 工具编译代码并与数据及资源文件一起打包到以后缀名为.apk的文件里 . 所有这些在单独的.apk文件下的代码被视为一个应用程序并且该文件是Android设备用以安装应用的安装文件.
一旦在设备中安装后,每个Android应用程序在他们自己的安全沙箱内生存:
- Android操作系统是一个多用户系统,他的每个应用都是不同的用户.
- 默认的,系统会为每个应用程序分配一个唯一的ID(这个ID仅被系统使用,应用程序并不知道) ,系统为应用程序中的所有文件设置了许可限制,只有分配了ID的应用程序才能访问他们.
- 每个进程都有他们自己的虚拟机, 所以一个应用程序与另一个应用程序是在相互隔离下运行的.
- 默认的,每个应用程序运行在他们自己的Linux进程中. Android在应用程序的组件需要执行时开启进程,不再需要或者系统必须要为其他应用程序重新获得内存的时候关闭.
这样,Android系统实现了最少权限的原则,每一个应用程序仅仅在他们需要工作时才能访问其组件。这会创造一个在未得到许可的情况下应用程序不能访问系统的其他部分这样的一个非常安全稳妥的环境.
但是,也有与其他应用程序共享数据以及访问系统服务的方式:
- 安排两个应用程序共享同一个Linux用户ID,让他们能互相访问对方的文件。为了保护系统资源,拥有相同用户ID的应用程序也能运行在相同的Linux进行里以及共享相同的虚拟机 (应用程序也需要签上相同的签名证书).
- 应用程序能够请求访问设备数据的许可,例如联系人,SMS消息,存储卡(SD卡),照相机、蓝牙以及更多。所有应用程序的许可必须在安装的时候由用户授权.
已经讲解了一个Android应用程序如何存在于系统中的基本注意事项.这一篇文章剩余部分将向你介绍:
- 定义应用程序的核心框架组件.
- 为应用程序声明组件以及规定设备特征的mainfest文件.
- 资源文件:与应用程序代码分离,并且以各种配置文件来让应用优美有效地展现其行为.
应用组件
应用组件是Android应用程序的重要组成部分. 每个组件是通过系统进入应用的不同的点. 对用户来说不是所有的组件都是真正的入口点,一些组件需要依赖于其他组件, 但是每一个组件都以他们自己为实体存在并且扮演特定的角色;每一个组件都是帮助定义一个应用程序整体行为的独一无二的组成部分.
有四种不同的应用组件类型. 每种类型以不同的目的,不同的生命周期定义了组件如何创建与销毁.
以下是四种不同类型的应用组件:
Activities一个activity 代表了一个单独的用户界面.例如,一个电子邮件应用程序需要一个activity来显示新邮件的清单,一个activity写邮件,一个activity读邮件. 尽管这些电子邮件应用程序中的activities工作在一起形成了具有粘合性的用户体验,但每个部分却是相互独立的。一个不同的应用程序能启动这些activities中的任何一个(如果邮件应用程序允许). 例如,在写新邮件的activity中启动一个照相机应用的activity是为了让用户分享一张照片.一个activity是实现了Activity
的一个子类,请通过Activities开发指南学习更多相关知识.
一个service是实现了Service
接口的子类,请通过Services开发指南学习更多相关知识.
ContactsContract.Data
)来读取和写入某一个人的信息.Content providers 对那些针对你的应用是私有的且不可共享的数据的读写也非常有用. 例如, 一个简单应用Note Pad 使用content provider进行保存.
一个content provider 是实现了ContentProvider
接口的子类,他必须实现一组标准的API使其他应用程序能够执行其事务. 请通过 Content Providers开发指南 学习更多相关知识.
一个broadcast receiver是实现了BroadcastReceiver
接口的子类,每个broadcast被作为一个Intent
对象被递交. 更多详细信息,请查阅 BroadcastReceiver
类.
Android系统设计的独一无二的一个方面是任何一个应用程序都能访问其他应用程序的组件. 例如,如果你想用户用设备的拍照功能拍摄照片, 可以使用其他有该功能的应用程序的activity,而不需要自己单独开发拍照片的acticity. 你不需要加入甚至是链接照相机应用的代码. 你只需要开启照相机应用的一个拍摄照片的activity. 完成时,返回一张照片到你的应用程序中,你便可以使用了. 对使用者来说,照相机应用看起来就像你应用中的一部分一样.
当系统运行一个组件时,就会为这个应用开启一个进程(如果他还没有运行的话)以及实例化该组件需要的类. 例如,如果你的应用程序开始一个照相机应用中拍摄照片的activity,该activity是在属于照相机应用的进程中运行的而并非在你的应用中. 因此,不想其他系统的应用程序,Android应用程序并没有单独的入口(例如main()函数).
由于系统是在带有文件授权的分开的进程中运行每个应用程序限制了对其他应用程序的访问, 你的应用程序不能直接从另外一个应用程序激活组件. 但并非不能这么做,你必须通过指定一个intent向系统递交一个信息来启动某个组件。然后系统就会为你激活这个组件.
激活组件
四种组件类型中的三种——activities, services, broadcast receivers—被一个叫intent的异步消息所激活.在运行时,intents为其他每个类型绑定单独的组件(你可以把他想象为从其他组件响应动作的一个通信者),无论该组件是否属于你的应用或是其他应用.
一个intent被Intent
对象创建, 他定义了一种消息来激活特定的组件或是某种特定类型的组件—intent可以分别是明确的或是不明确的.
对于activities与services, intent 定义了执行的动作(例如"查看"或"发送"某些信息)以及运行的数据URI(组件需要事先知道才能运行). 例如,一个intent为activity传达显示图片或是打开一个网页的请求. 在某些情况,你可以开始一个activity来得到一个结果,在这种情况下,那个activity会通过Intent
对象返回一个结果集 (例如,你发布一个选择个人联系人的intent,并将结果返回给你—返回的intent包含了你选中的联系人的URI链接).
对于broadcast receivers, intent 简单地定义了即将广播的通知(例如仅包含一个"电量不足"的字符串来指示设备电量过低的通知).
另一个类型的组件content provider并不是通过intent激活的,而是将通过来自于ContentResolver
的一个请求作为目标而激活的. content resolver处理content provider的直接的事务,所以就不需要用content provider去执行事务而是通过调用ContentResolver
对象上的方法. 这交托给content provider与请求信息的组件之间的抽象层来处理(为了安全).
这里有几个激活每个类型组件的方法:
- 你可以通过将
Intent
对象传递给startActivity()
或者startActivityForResult()
(当你需要activity返回一个结果)方法开启一个新的activity(或者做一些新的事情). - 你可以通过将
Intent
对象传递给startService()
方法开启一个service(或是给正在运行的service新的指令). 或者将Intent
对象传递给bindService()
方法绑定一个service. - 你可以通过将
Intent
对象传递给sendBroadcast()
,sendOrderedBroadcast()
, 或者sendStickyBroadcast()
方法新添加一个broadcast. - 你可以使用
ContentResolver
上的方法query()
向content provider执行查询.
关于使用intents的更多信息, 请阅读 Intents and Intent Filters. 以下文章也提供了更多关于激活特定组件的相关内容: Activities, Services, BroadcastReceiver
and Content Providers
Manifest文件
在Android系统启动一个应用组件之前,必须通过读取应用程序的AndroidManifest.xml
("清单" 文件)让系统知道他的存在. 你的应用程序必须在这个文件中声明所有的组件,且该文件处于项目根目录下.
该清单文件除声明应用程序的组件之外还做了一些其他事情,如下:
- 识别应用程序要求的用户权限,例如网络以及用户联系人的访问权.
- 声明了应用程序要求的基于应用程序使用的API的最小API Level.
- 声明应用程序需要的软件及硬件。例如照相机、蓝牙服务或者多点的触摸屏.
- 应用程序需要关联的API库(不同于Android框架API),例如Google Maps library.
- 其他
声明组件
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
属性指明activity的可见标签上的一个字符串.
你必须声明所有的应用程序组件:
<activity>
元素声明activities<service>
元素声明services<receiver>
元素声明broadcast receivers<provider>
元素声明content providers
在你的代码资源中包含了Activities, services, 和 content providers 但是没有在manifest.xml文件中声明对系统而言是不可见的,所以不能运行. 但是,broadcast receivers 可以在manifest.xml中声明或者在代码中动态的创建(例如 BroadcastReceiver
对象)以及通过调用registerReceiver()
向系统注册.
关于更多为应用程序构建mainfest的内容, 请阅读 The AndroidManifest.xml File.
声明组件的功能
像上面 Activating Components所讨论的, 你可以通过在intent中明确命名目标组件(用组件的类名)让 Intent
开启activities, services, 以及 broadcast receivers. 但是,intent真正的能力在于intent action这样的一个概念,用intent actions,你简单地描述了你想执行的动作的类型(并且可选择性的描述该动作之上的数据)以及让系统找到一个能够执行action并且能启动它的组件. 如果找到多个能执行该action的组件,用户自行选择一个.
系统识别能响应intent的组件的方法是比较对照设备上从其他应用程序的manifest文件提供的intent filters中接收到的intent .
当你在应用程序的mainfest文件中声明一个组件时, 你可以选择性的包含用来声明组件功能的intent filters,所以应用程序能够响应来自其他应用程序的intent. 你可以通过添加<intent-filter>
元素作为组件声明的一个子元素来为声明你的应用程序的intent.
例如,一个创建新邮件的邮件客户端应用的activity在intent filter下声明一个确切的入口来响应"发送"的动作(为了发送邮件). 应用程序的activity就能创建一个带有"发送"动作的intent(ACTION_SEND
), 系统就会匹配邮件应用程序的"发送"的activity,并且通过调用startActivity()
方法来执行该intent.
For more about creating intent filters, see the Intents and Intent Filters document.
声明应用程序的要求
有各种各样的以Android驱动的设备,不是所有的设备都提供了相同的特征与功能. 为了防止应用程序安装到设备上以后,缺乏应用程序需要的特征, 在你应用程序的manifest文件中通过声明设备硬件以及软件要求为你的应用程序支持的设备类型清楚地定义一个简况尤为重要. 大多数这些声明仅仅是一个信息性的,系统并不会读取到它,但是一些额外的服务例如Android Market去读取这些信息是为了当用户从中搜索应用的时候提供过滤.
比如,如果你的应用程序需要一个摄像头以及系统版本为Android 2.1 (API Level 7), 你应该将这些作为要求在manifest文件中声明. 那样, 没有摄像头以及版本系统低于Android2.1的设备不能从Android Market中安装应用.
但是,你也能够声明你的应用程序使用摄像头,但不会真正用到他. 如果那样,应用程序在运行的时候必须执行一次检测来确定设备是否有摄像头以及某个摄像功能不可用.
这里有一些重要的设备特征需要在你做设计以及开发应用程序的时候注意:
屏幕大小与分辨率为了用屏幕类型来归类设备, Android 为每种设备定义了两个特征: 屏幕大小 (屏幕的物理尺寸) 与屏幕分辨率 (屏幕像素的物理稠密度,或者每一寸的原点数). 为了简化不同类型的界面配置, Android 系统将他们归纳为了更易于面向的一组类型.屏幕大小: 小的, 普通的, 大的, 特别大的.屏幕分辨率: 低的, 适中的, 高清的, 特别高清的.
默认的,应用程序针对所有的屏幕大小以及分辨率都是兼容的,因为。Android系统对UI界面布局与图片资源都做了适当的调整. 但是,你应该使用可替换的布局资源,manifest文件中确切声明应用程序支持的屏幕大小元素<supports-screens>
来为某些固定大小的屏幕创建专门的布局以及为提供某些固定的分辨率提供专门的图像.
对于更多信息, 请阅读多种屏幕的支持.
输入设备的配置许多设备提供了不同类型的用户输入装置,例如键盘, a 轨迹球, 或者一个有五个方向的导航键. 如果你的应用需要某种类型的输入硬件,你可以通过<uses-configuration>
元素在manifest文件中声明. 但是,一个应用极少需要配置某种固定的输入.设备特征许多硬件与软件特征并不一定存在于一些Android的设备上, 例如摄像头、重力感应、蓝牙、OpenGL的固定版本, 或者精准的触摸屏. 你不应该认为某种固定的特征适用于所有的Android设备(除Android标准库), 所以你应该通过<uses-feature>
元素声明那些应用中使用的特征.平台版本Android设备运行着不同版本的Android平台, 例如Android 1.6 或者 Android 2.3. 每个连续的版本都包含了新增的不能适用于前一个版本的 API. 为了明确一组API的可用性,每个平台版本指定一个API Level (例如, Android 1.0 是API Level 1而 Android 2.3 是API Level 9). 如果你要使用版本1.0以后的增加的API,你应该通过 <uses-sdk>
元素声明最小API Level.为你的应用程序声明所有的要求尤为重要, 因为当你在Android Market上发布你的应用程序时, Market 使用这些声明为每台设备过滤可用的应用. 这样,你的应用程序仅仅在满足其所有要求的设备上可用.
关于Android Market如何基于这些(或者其他)要求过滤应用程序,请阅读Market Filters.
应用程序资源
Android应用程序不仅仅是由代码组成;他需要与代码相分离的资源,例如图片,声音文件以及与应用的展现相关的其他资源. 例如,你可以用xml文件去定义动画,菜单,样式,颜色以及用户界面的布局. 使用应用程序资源可以简单地更新应用的各种特征而不用修改代码;通过提供几组可替换的资源文件可以为应用的各种配置做优化(例如不同语言与不同屏幕大小).
关于Android工程下包含的每个资源, SDK 编译工具生成一个独一无二的整型ID, 你可以通过应用程序代码或是定义在xml中的其他资源来引用这个ID, 如果你的应用包含一个名为logo.png
(saved in the res/drawable/
directory)的图片, SDK 工具就生成一个名为R.drawable.logo
的资源ID, 你可以通过这个ID引用到这个图片以及将他插入到你的用户界面中.
提供与代码相分离的资源的最重要的一个方面是为各种设备的配置提供可替换资源的能力. 例如,通过在xml文件中定义UI大的字符串,你能将这些字符串翻译成其他语言而且分别保存在不同的文件里. 然后,基于将资源目录名称加用户的语言的设置的这样的一个语言修饰(例如法语字符串的目录格式 res/values-fr/
) , Android 系统将合适的语言字符应用到你的UI中.
Android对可替换资源支持多种不同的修饰符. 修饰符是包含资源目录名的简短的字符串,他是为了使用的这些资源定义设备配置. 像另外一个例子, 你应该通常依靠屏幕的方向以及大小为activities创建不同的布局. 例如, 但设备的屏幕是竖直方向, 你可能想要带有按钮的布局是垂直的, 但当你的屏幕是横向的, 按钮应该水平对齐. 为了依赖设备屏幕方向来改变布局, 你要定义两种不同的布局并且应用适当的修饰词到每个布局目录名. 然后,系统便能依靠当前设备的方向自动应用适当的布局.
关于更多应用中包含的不同类型的资源以及如何为各种设备配置创建可替代的资源文件, 请阅读 Application Resources开发指南.
<!-- <h2>Beginner's Path</h2> <p>For a close look at implementing activities—the components your users use to interact with your application—continue with the <b><a href="../../guide/topics/fundamentals/activities.html">Activities</a></b> document.</p> -->