Android 应用程序基础

快速浏览

  • Android应用程序由一个或多个应用组件构成.(活动, 服务, 内容提供者, 广播接收器)
  • 应用程序中的每个组件扮演一个特定的角色,并且每个组件都可以单独激活.(即使是被其它应用程序激活)
  • 清单文件必须声明应用程序包含的所有组件及需求, 比如兼容的Android最低版本以及任意其它所必需的硬件配置
  • 非代码类应用程序资源(图像, 字符串, 布局文件等等) 应该针对不同的设备配置相应的项(比如适用于不同语言的字符串和适用于不同屏幕尺寸的布局)

目录

  1. 应用程序组件
    1. 组件的激活
  2. 清单文件
    1. 声明组件
    2. 声明组件能力
    3. 声明应用程序需求
  3. 应用程序资源

Android 应用程序使用Java作为编程语言. Android SDK 工具将代码、数据以及资源文件编译成为扩展名为.apk的 Android 软件包. 一个apk文件代表一个Android应用程序,Android使用apk文件来安装应用.

安装完成之后, 设备上的每个Android应用生存在自己的安全沙箱里面:

  • Android操作系统是一个多用户的Linux系统, 每个应用程序代表一个不同的用户.
  • 默认情况下,系统为每个应用程序赋予一个唯一的Linux用户ID (这个用户ID只供系统内部使用,应用程序本身并不知晓这个用户ID). 系统使用这个用户ID为应用程序的所有文件设置权限,因此只有这个用户ID才可以访问这些文件.
  • 每个进程都有自己的虚拟机, 所以应用程序之间的代码在物理上是隔离的.
  • 默认情况下,每个应用程序在自己的Linux进程里面运行. Android在应用程序的任意组件运行的时候启动这个进程, 然后当不再需要该应用程序的组件或者当系统需要为其它应用程序腾出内存的时候Android会关闭该进程.

通过这种方式,Android系统实现了最小特权原则. 就是说, 默认情况下, 每个应用程序只对工作所必需的组件拥有访问权.这样就创建了一个非常安全的环境,在这个环境中,每个应用程序都无法访问它未被授权的系统组件.

然而, 通过下面几种方式可以使应用程序之间共享数据,而且可以使应用程序访问系统服务:

  • 可以使两个应用程序共享同一个Linux用户ID, 这样它们就可以访问彼此的文件. 为了节省系统资源, 具备相同用户ID的应用程序可以共享同一个Linux进程和虚拟机 (这些应用程序必须被赋予相同的证书).
  • 应用程序可以申请访问设备数据的权限,比如联系人, SMS 消息,可挂载的存储 (SD card), 相机, 蓝牙等等. 所有的应用程序权限必须在安装时被用户授予.

上述内容介绍了Android应用程序与系统的基础知识. 下面将为您介绍:

  • 定义应用程序的核心框架组件.
  • 用于声明组件和设备需求的清单文件.
  • 资源,独立于应用程序代码,允许应用程序根据不同的设备配置优化其行为.

应用程序组件

应用程序组件是Android应用程序的核心构建块. 每个组件表示一个不同的点, 系统通过这个点与你的应用程序进行交互.并不是所有的组件都是直接面向用户的点,有一些组件之间是具备依赖关系的,但是每个组件都作为一个独立的实体扮演特定的角色—每个组件都是一个独一无二的构建块,帮助你定义应用程序的整体行为.

一共有4种类型的应用程序组件. 每种类型的组件都服务于不同的目的,具备不同的生命周期.

下面就开始介绍这4种类型的应用程序组件:

Activities
一个Activity代表了a single screen with a user interface. 例如, 一个电子邮件应用程序必需有一个 Activity用来显示邮件列表, 一个 Activity用来写邮件,一个 Activity用来读邮件.尽管在电子邮件应用程序中这些Activity共同工作从而形成一种聚合的用户体验, 但是事实上它们是相互独立的. 因此, 一个不同的应用程序可以启动这些Activity中的任意一个 (在电子邮件应用程序允许的情况下). 例如,为了方便用户共享图片,相机应用程序可以启动电子邮件应用程序中用于写邮件的Activity.

Activity被实现为Activity的子类,从Activities开发者指南你可以获取更多关于Activity的信息.

服务
服务是运行于后台,执行长时间/耗时操作或者远程操作的组件. 服务没有用户界面. 例如, 当用户在与另外一个应用程序交互的时候,可能一个播放音乐的服务正在后台运行, 或者它可能用于获取网络数据而不阻塞用户与Activity的交互.其它组件, 比如Activity,可以启动一个服务,也可以绑定到一个服务从而可以与服务进行交互.

服务被实现为Service的子类,从Services开发者指南你可以获取更多关于服务的信息.

内容提供者
内容提供者可以管理应用程序需要共享的数据. 你可以选择将数据存储在文件系统, SQLite 数据库, Web,以及应用程序可以访问到的任意其它持久存储. 借助内容提供者,一个应用程序可以查询甚至修改另外一个应用程序的数据(在内容提供者允许的情况下). 例如, Android系统提供了一个管理联系人信息的内容提供者. 因此, 任何具备相应权限的应用程序都可以通过这个内容提供者查询、修改联系人信息.

内容提供者对于访问应用程序私有数据也是有用的.例如,Note Pad示例应用程序使用内容提供者保存笔记.

内容提供者被实现为ContentProvider的子类,而且必须实现一组标准接口来使其它应用程序执行相应的事务. 从Content Providers开发者指南你可以获取更多关于内容提供者的信息.

广播接收者
广播接收者可以接收系统范围的广播/通知. 许多广播源自系统—例如, 屏幕关闭, 电量不足,或者拍照事件.应用程序也可以发送广播—例如, 通知其它应用程序数据下载完成. 尽管广播接受者并没有用户界面, 但是在接收到广播时,它们可以创建一个状态栏的通知以提醒用户. 不过,更常见的情况是, 广播接收者作为其它组件的门户只做很少量的工作. 例如,它可能仅仅只是在接收到某个广播/通知的时候启动某个服务来处理相应的工作.

广播接受者被实现为BroadcastReceiver的子类,每个广播都是一个Intent对象.BroadcastReceiver开发者指南你可以获取更多关于广播接收者的信息.

Android系统设计的独特之处在于任何应用程序都可以调用其它应用程序的组件.例如, 如果你的应用程序想让用户拍照, 而且其它应用程序已经实现了一个拍照组件,这个时候你就可以直接使用它的拍照组件儿不需要自己重新实现一次. 你不必要将你的应用程序代码链接到那个相机应用程序.相反, 你可以简单的启动相机应用程序的拍照Activity,然后拍照. 拍照完成的时候, 照片会返回到你的应用程序,这时你就可以使用这张照片了.从用户的角度来看,拍照组件似乎是你的应用程序的一部分.

当系统启动一个组件的时候, 它会首先启动组件所属应用程序的进程(如果该进程尚未运行的话) ,然后创建组件类的实例.例如,如果你的应用程序启动了相机应用程序的拍照Activity并且拍照, 拍照Activity其实是运行在相机应用程序的进程里,而不是你的应用程序进程. 因此,和其它系统上应用程序的区别在于, Android 应用程序并没有单一的入口点 (例如,不具备main函数).

由于在Android系统中每个应用程序运行在独立的进程中,并且每个应用程序并不具备访问其它应用程序文件的权限, 所以你的应用程序是不能直接激活其它应用程序的组件的.然而,Android 系统具备激活其它应用程序组件的能力. 因此,为了激活其它应用程序的组件,你必须在你的应用程序中向Android系统发送一个描述你意图的消息,然后Android系统解析这个消息,激活目标组件.

组件的激活

四种组件中的三种—Activity, 服务,以及广播接受者—是通过被称作“意图”的异步消息激活的. “意图”与组件的映射在运行时完成 (你可以将这种映射关系想象为向其它组件发出某种请求的信使), 不论该组件是否属于你的应用程序.

“意图”其实是一个Intent对象, 它定义了激活特定组件或者激活特定类型组件的消息—“意图”可以是明确的,也可以是隐含的.

就Activity和服务来说,“意图”定义了需要执行的动作(例如, “看”或者“发送”某样东西) 并且指定了目标数据的URI (组件激活所需要的参数). 例如, 某个“意图”可能需要请求某个Activity显示一张图片或者打开一个Web页面. 在某些情况下, 你激活某个Activity是为了获取某种结果, 这种情况下,目标Activity会将结果存储在Intent对象中返回(例如, 你可以发送一个让用户选择联系人的“意图”并将选择的联系人返回给你—返回的“意图”包含一个指向联系人信息的URI).

就广播接受者来说, “意图”只是简单的定义了需要广播的通知 (例如, 一条广播可能只是包含一个指示“电量过低”的字符串).

另外一种组件, 内容提供者, 并不是通过“意图”激活的. 内容提供者是在响应ContentResolver的请求时被激活的. 内容解析者负责所有与内容提供者的直接交互. 这样就在内容提供者和请求信息的组件之间添加了一层抽象 (为了安全考虑).

激活每种组件的方法如下:

  • 你可以通过发送一个“意图” 给startActivity()或者startActivityForResult()(当你希望获取Activity的返回结果时)来启动Activity或者给Activity分配新的任务.
  • 你可以通过发送一个“意图” 给startService()来启动一个服务或者给正在运行的服务发送新的指令.你也可以通过发送一个“意图” 给bindService()来绑定到一个服务.
  • 你可以通过发送一个“意图” 给sendBroadcast(),sendOrderedBroadcast(), 或者sendStickyBroadcast()来产生一个广播.
  • 你可以通过调用ContentResolver的query()方法来对内容提供者执行查询操作.

更多关于“意图”的信息, 请参考Intents and Intent Filters文档. 更多关于激活组件的信息请参考如下文档:Activities,Services,BroadcastReceiverandContent Providers.

清单文件

在启动应用程序组件之前, Android系统必须通过读取应用程序清单文件来确定组件是否存在. 应用程序必须在清单文件中声明它的所有组件, 而且清单文件必须位于应用程序工程的根目录.

除了声明组件, 清单文件还有一些其它责任,例如:

  • 声明应用程序需要的权限, 比如访问网络或者访问联系人信息.
  • 声明应用程序兼容的最低API版本.
  • 声明应用程序的硬件和软件需求, 例如相机, 蓝牙服务, 或者多点触控屏幕.
  • 声明应用程序依赖的库(不包括Android框架API), 例如Google Maps library.
  • 更多

声明组件

清单文件的主要功能是告知系统应用程序所拥有的组件. 例如,清单文件可以按照下述方式声明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>元素用于声明Activity
  • <service>元素用于声明服务
  • <receiver>元素用于声明广播接收者
  • <provider>元素用于声明内容提供者

如果源代码包含的Activity、服务或者内容提供者没有在清单文件中声明,那么它们对系统是不可见的,因此,它们永远不会运行. 然而,广播接收者既可以在清单文件中声明也可以在代码中动态创建并通过调用registerReceiver()来注册.

更多关于清单文件的信息, 请参考The AndroidManifest.xml File文档.

声明组件能力

正如上面所讨论的那样,你可以使用“意图”启动Activity、服务和广播接收者. 你可以在“意图”中明确指定目标组件的类名称. 然而, “意图”的强大威力其实是“意图”动作. 使用“意图”动作,你只需要简单的描述需要执行的动作类型 (还可以指定执行动作需要的数据) 并允许系统在设备商找到可以执行这个动作的组件然后启动它. 如果有多个组件可以执行目标动作,则由用户从中选择一个.

系统通过比较“意图”与应用程序清单文件中的“意图过滤器”来确定能够执行“意图”的组件.

当你在清单文件中声明一个组件的时候, 你可以选择包含一个描述该组件能力的“意图过滤器”,这样该组件就能够响应其它应用程序发送的“意图”了. 你通过为为组件声明元素添加一个<intent-filter>子元素就可以为组件添加“意图过滤器”.

例如, 邮件应用程序负责创作邮件的Activity可能会声明一个响应“发送”(目的是发送邮件)"意图"的“意图过滤器”. 这样你的应用程序就可以创建一个具备“发送”动作(ACTION_SEND)的“意图”,然后调用startActivity(),这时系统就会将"发送""意图"映射到邮件应用程序并启动它.

更多关于”意图过滤器“的信息, 请参考Intents and Intent Filters文档.

声明应用程序需求

Android支持的设备多种多样,而且它们在功能和特性上存在差别. 为了避免你的应用程序安装在缺少必需功能的设备上, 在清单文件中清晰的声明应用程序的硬件和软件需求就显得尤为重要. 系统并不读取这部分声明信息, 但是扩展服务例如Android电子市场在搜索应用的时候会读取这些信息并过滤.

例如, 如果你的应用程序基于 Android 2.1 (API Level7)并且需要相机, 你应该在清单文件中声明这些需求. 这样如果Android 版本低于2.1或者没有相机就不能从电子市场安装你的应用程序.

然而, 你也可以声明你的应用程序需要但不必须有相机的支持.这种情况下, 应用程序必须在运行时检测设备是否有相机,如果没有相机则禁用相关功能.

下面是在设计和开发应用程序时需要考虑的一些重要的设备特征:

屏幕尺寸和分辨率
为了按照屏幕类型对设备归类, Android为每种设备定义了两个特征: 屏幕尺寸 (屏幕的物理尺寸) 和屏幕分辨率(屏幕的物理像素分辨率). 为了简化不同类型的屏幕配置, Android系统将这些特征作了概括.

屏幕尺寸: 小, 正常, 大, 特别大.
屏幕分辨率: 低分辨率, 中等分辨率, 高分辨率, 特高分辨率.

默认情况下, 你的应用程序兼容所有屏幕尺寸和分辨率, 因为Android系统会为你的UI布局和图像文件作适当调整. 然而, 你应该根据屏幕尺寸和分辨率提供特定的布局和图像资源, 并且在清单文件的<supports-screens>元素中声明你的应用程序支持的屏幕类型.

更多信息, 请参考Supporting Multiple Screens文档.

输入配置
许多设备提供不同的用户输入机制,比如键盘,跟踪球, 或者 a five-way navigation pad.如果你的应用程序需要特定类型的输入硬件, 你应该在清单文件的 <uses-configuration>元素中声明. 然而, 应用程序依赖特定输入配置的情况很少见.
设备功能
Android设备有可能具备或者不具备某些硬件和软件功能,例如相机,光传感器, 蓝牙, 某个版本的 OpenGL, 或者触摸屏. 你不应当假设所有的Android设备都具备这些功能(Android 标准API除外), 相反,你应当在 <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应用程序的时候, 电子市场会使用这些信息去过滤设备可用的软件. 因此,你的应用程序应当只能使那些满足所有应用程序需求的设备获取.

更多关于Android电子市场过滤应用程序需求的信息, 请参考Market Filters文档.

应用程序资源

Android应用程序不只包含代码—它也需要独立于代码的资源, 例如图像、音频文件以及其它可视化相关的资源. 例如, 你应当使用XML文件定义动画、菜单、风格、颜色以及Activity用户界面的布局. 通过提供多套可选资源,我们可以在不修改代码的情况下很容易的更新应用程序的各种特征,为特定设备提供优化之后的资源(比如不同的语言、屏幕尺寸).

SDK构建工具会为你包含进Android工程的每一个资源项生成一个唯一的整型ID, 你可以使用这个ID在代码或者其它XML资源文件中引用这个资源项. 例如,如果你的应用程序包含一个名为logo.png的图像文件(保存在res/drawable/目录), SDK工具会为它生成一个名为R.drawable.logo的资源ID, 你可以使用这个ID引用这个图像并且将它插入用户界面.

代码与资源隔离的一个重要方面是,你可以为不同的设备配置提供不同的资源集. 例如, 通过将UI字符串定义在XML文件中, 你可以将字符串翻译成其它语言并存储在单独的文件中. 然后根据你附加到资源目录名称上的语言标识符(例如res/values-fr/包含对应的法语字符串) 和用户设置, Android系统会为你的UI选择合适的语言.

Android通过标识符支持多种可选资源. 标识符是包含在资源目录名称中的短字符串,它用于匹配设备配置和资源. 例如, 依赖于设备屏幕方向和尺寸,你应该为Activity定义不同的布局. 当屏幕是纵向的时候, 你也许需要按钮是垂直排列的, 但是当屏幕是横向的时候,按钮应该水平排列. 为了根据屏幕方向改变布局,你应该定义两种不同的布局并为布局文件目录名称应用适当的标识符. 这样系统就会根据设备方向自动应用合适的布局.

更多关于资源类型、资源与设备配置匹配的信息, 请参考Application Resources开发指南.

更多相关文章

  1. Android应用基础浅析
  2. [Android(安卓)API学习]AppWidget
  3. Android面试题(基础部分1)
  4. Android(安卓)退出应用程序问题
  5. Android广播接收器(三)
  6. 为Android(安卓)Application指定版本
  7. TestFlight被苹果收购,将停止对Android的支持
  8. 如何判断是否可以使用某个Intent
  9. Android性能优化---布局优化

随机推荐

  1. 采用XMPP协议实现Android推送
  2. Parcalable接口使用(android传递结构体数
  3. android中的gravity和…
  4. Android高手进阶教程之----Android(安卓)
  5. Android(安卓)的 SQLLite数据库
  6. Android(安卓)AsyncTask完全解析,带你从源
  7. Android菜单的使用Menu
  8. android体系结构
  9. android平台下音频编码之编译LAME库转码P
  10. Android布局 android:gravity 和 android