应用程序基础知识

Android 应用可以使用 Kotlin、Java 编程语言和 C++ 语言编写。Android SDK 工具会将您的代码与任何数据和资源文件一起编译成 APK 或 Android 应用包。

Android 包,这是一个后缀为.apk 的归档文件,包含运行时所需的 Android 应用内容,并且它是 Android 设备用于安装应用的文件。

Android 应用包,这是一个后缀为.aab 的归档文件,包含 Android 应用项目的全部内容,包括一些运行时不需要的其他元数据。AAB 是一种发布格式,无法安装在 Android 设备上。它将 APK 生成和签名推迟到稍后阶段。

例如,当通过 Google Play 分发您的应用时,Google Play 的服务器会生成优化的 APK,这些 APK 仅包含特定设备请求安装应用所需的资源和代码。

每个 Android 应用都存在于其自己的安全沙盒中,受到以下 Android 安全功能的保护

  • Android 操作系统是一个多用户 Linux 系统,其中每个应用都是一个不同的用户。
  • 默认情况下,系统为每个应用分配一个唯一的 Linux 用户 ID,该 ID 仅供系统使用,应用未知。系统设置应用中所有文件的权限,以便只有分配给该应用的用户 ID 才能访问它们。
  • 每个进程都有自己的虚拟机 (VM),因此应用的代码与其他应用隔离运行。
  • 默认情况下,每个应用都在其自己的 Linux 进程中运行。当需要执行应用的任何组件时,Android 系统会启动该进程,并在不再需要或系统必须为其他应用回收内存时关闭该进程。

Android 系统实现了最小权限原则。也就是说,默认情况下,每个应用只能访问其工作所需的部分,而不能访问更多内容。这创建了一个非常安全的环境,应用无法访问未被授予权限的系统部分。

但是,应用可以通过多种方式与其他应用共享数据,并访问系统服务。

  • 可以安排两个应用共享同一个 Linux 用户 ID,在这种情况下,它们能够访问彼此的文件。为了节省系统资源,具有相同用户 ID 的应用还可以安排在同一个 Linux 进程中运行并共享同一个虚拟机。这些应用还必须使用相同的证书进行签名。
  • 应用可以请求权限以访问设备数据,例如设备的位置、摄像头和蓝牙连接。用户必须明确授予这些权限。有关权限的更多信息,请参阅Android 上的权限

本文档的其余部分介绍以下概念

  • 定义应用的核心框架组件。
  • 清单文件,您可以在其中声明组件以及应用所需的设备功能。
  • 与应用代码分开的资源,这些资源使您的应用能够优雅地优化其行为以适应各种设备配置。

应用组件

应用组件是 Android 应用的基本构建块。每个组件都是一个入口点,系统或用户可以通过它进入您的应用。某些组件依赖于其他组件。

有四种类型的应用组件

  • 活动
  • 服务
  • 广播接收器
  • 内容提供程序

每种类型都具有不同的用途,并具有不同的生命周期,该生命周期定义了如何创建和销毁组件。以下部分描述了四种类型的应用组件。

活动
活动是与用户交互的入口点。它表示具有用户界面的单个屏幕。例如,电子邮件应用可能有一个活动显示新电子邮件列表,另一个活动用于撰写电子邮件,另一个活动用于阅读电子邮件。尽管这些活动协同工作以在电子邮件应用中形成连贯的用户体验,但每个活动都独立于其他活动。

如果电子邮件应用允许,其他应用可以启动这些活动中的任何一个。例如,相机应用可能会启动电子邮件应用中的活动以撰写新电子邮件,让用户分享图片。

活动促进系统和应用之间以下关键交互

  • 跟踪用户当前关注的内容(即屏幕上的内容),以便系统继续运行托管该活动的进程。
  • 了解先前使用的进程中包含用户可能返回的已停止活动,并更优先地处理这些进程以使其保持可用。
  • 帮助应用处理其进程被终止的情况,以便用户可以返回到其先前状态已恢复的活动。
  • 为应用提供一种方法,使它们能够彼此之间实现用户流程,并使系统能够协调这些流程。这方面的主要示例是共享。

您可以将活动实现为Activity类的子类。有关Activity类的更多信息,请参阅活动简介

服务
服务是用于出于各种原因使应用在后台保持运行的通用入口点。它是在后台运行以执行长时间运行的操作或为远程进程执行工作的组件。服务不提供用户界面。

例如,服务可以在用户位于其他应用中时在后台播放音乐,或者可以在不阻止用户与活动交互的情况下通过网络获取数据。另一个组件(例如活动)可以启动服务并使其运行或绑定到它以与其交互。

有两种类型的服务可以告诉系统如何管理应用:启动的服务和绑定服务。

启动的服务告诉系统在工作完成之前保持运行。这可能是为了在后台同步一些数据,或者在用户离开应用后继续播放音乐。在后台同步数据或播放音乐代表不同类型的启动服务,系统会以不同的方式处理它们。

  • 音乐播放是用户直接感知到的,应用通过指示其希望位于前台并显示通知以告知用户其正在运行来将其传达给系统。在这种情况下,系统会优先考虑保持该服务的进程运行,因为如果其消失,用户会体验不佳。
  • 常规后台服务不是用户直接感知到的,因此系统在管理其进程方面拥有更多自由。如果它需要 RAM 来处理对用户更重要的内容,它可能会将其终止,并在稍后重新启动服务。

绑定服务运行是因为其他一些应用(或系统)已表示其希望使用该服务。绑定服务向另一个进程提供 API,系统知道这些进程之间存在依赖关系。因此,如果进程 A 绑定到进程 B 中的服务,则系统知道它需要保持进程 B 及其服务为 A 运行。此外,如果进程 A 是用户关心的内容,则系统知道将进程 B 视为用户也关心的内容。

由于服务的灵活性,它们对于各种更高级别的系统概念很有用。动态壁纸、通知侦听器、屏幕保护程序、输入法、辅助功能服务以及许多其他核心系统功能都构建为服务,应用在运行时会实现并绑定到这些服务。

服务实现为Service的子类。有关Service类的更多信息,请参阅服务概述

注意:如果您的应用的目标是 Android 5.0(API 级别 21)或更高版本,请使用JobScheduler类来安排操作。JobScheduler 的优势在于可以通过优化作业调度来减少功耗并与Doze API 配合使用来节省电池电量。有关如何使用此类的更多信息,请参阅JobScheduler参考文档。

广播接收器
广播接收器是一个组件,它允许系统在常规用户流程之外向应用传递事件,以便应用可以响应系统范围内的广播公告。由于广播接收器是另一个定义明确的应用入口点,因此系统即使在应用当前未运行时也可以传递广播。

因此,例如,应用可以安排警报以发布通知,以告知用户即将发生的事件。由于警报传递到应用中的BroadcastReceiver,因此应用无需在警报响起之前一直保持运行。

许多广播源于系统,例如宣布屏幕关闭、电池电量不足或捕获图片的广播。应用也可以启动广播,例如让其他应用知道某些数据已下载到设备上,并可供它们使用。

尽管广播接收器不显示用户界面,但它们可以创建状态栏通知以在发生广播事件时提醒用户。但是,更常见的情况是,广播接收器只是一个网关,用于连接到其他组件,并且旨在执行极少量的操作。

例如,广播接收器可能会安排JobService使用JobScheduler根据事件执行某些操作。广播接收器通常涉及应用之间的交互,因此在设置它们时务必注意安全隐患。

广播接收器实现为BroadcastReceiver的子类,每个广播都作为Intent对象传递。有关更多信息,请参阅BroadcastReceiver类。

内容提供程序
内容提供程序管理一组共享的应用数据,您可以将其存储在文件系统、SQLite 数据库、网络或应用可以访问的任何其他持久存储位置中。通过内容提供程序,其他应用可以查询或修改数据(如果内容提供程序允许)。

例如,Android 系统提供了一个内容提供程序来管理用户的联系人信息。任何具有适当权限的应用都可以查询内容提供程序,例如使用ContactsContract.Data读取和写入有关特定人员的信息。

很容易将内容提供程序视为数据库的抽象,因为为此常见情况内置了许多 API 和支持。但是,从系统设计的角度来看,它们具有与数据库不同的核心目的。

对于系统而言,内容提供程序是应用中发布命名数据项的入口点,这些数据项由 URI 方案标识。因此,应用可以决定如何将其包含的数据映射到 URI 命名空间,并将这些 URI 分发给其他实体,这些实体随后可以使用它们来访问数据。系统在管理应用时可以通过此方法执行一些特定操作

  • 分配 URI 不需要应用保持运行,因此 URI 可以在其所属应用退出后继续存在。系统只需要确保在从相应的 URI 检索应用的数据时,其所属应用仍在运行即可。
  • 这些 URI 还提供了一个重要的细粒度安全模型。例如,应用可以将其拥有的图像的 URI 放置到剪贴板中,但将其内容提供程序锁定,以便其他应用无法自由访问它。当第二个应用尝试访问剪贴板上的该 URI 时,系统可以让该应用使用临时URI 权限授予访问数据,以便它仅访问该 URI 后面的数据,而不是第二个应用中的其他任何内容。

内容提供程序也可用于读取和写入对应用私有的且未共享的数据。

内容提供程序实现为ContentProvider的子类,并且必须实现一组标准的 API,使其他应用能够执行事务。有关更多信息,请参阅内容提供程序开发者指南。

Android 系统设计的一个独特方面是,任何应用都可以启动另一个应用的组件。例如,如果您希望用户使用设备摄像头捕获照片,可能会有另一个应用可以执行此操作,并且您的应用可以使用它,而不是自己开发一个捕获照片的活动。您无需合并或甚至链接来自相机应用的代码。相反,您可以启动相机应用中捕获照片的活动。完成后,照片甚至会返回到您的应用,以便您可以使用它。对用户而言,似乎相机实际上是您的应用的一部分。

当系统启动组件时,它会启动该应用的进程(如果尚未运行),并实例化组件所需的类。例如,如果您的应用启动相机应用中捕获照片的活动,则该活动将在属于相机应用的进程中运行,而不是在您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android 应用没有单个入口点:没有main()函数。

由于系统在单独的进程中运行每个应用,并且文件权限限制了对其他应用的访问,因此您的应用无法直接激活其他应用中的组件。但是,Android 系统可以。要激活另一个应用中的组件,您可以向系统传递一条消息,该消息指定您启动特定组件的 *意图*。然后,系统会为您激活该组件。

激活组件

名为 *意图* 的异步消息激活四种组件类型中的三种:活动、服务和广播接收器。意图在运行时将各个组件绑定在一起。您可以将它们视为请求其他组件执行操作的消息传递者,无论该组件属于您的应用还是其他应用。

意图使用 Intent 对象创建,该对象定义一条消息以激活特定组件(*显式* 意图)或特定类型的组件(*隐式* 意图)。

对于活动和服务,意图定义要执行的操作,例如 *查看* 或 *发送* 某些内容,并且可能会指定要操作的数据的 URI,以及启动的组件可能需要知道的其他内容。

例如,意图可能传达请求活动显示图像或打开网页的请求。在某些情况下,您可以启动活动以接收结果,在这种情况下,活动也会在 Intent 中返回结果。您还可以发出意图让用户选择个人联系人并将其返回给您。返回的意图包含指向所选联系人的 URI。

对于广播接收器,意图定义广播公告。例如,指示设备电量不足的广播仅包含一个已知的动作字符串,指示 *电量不足*。

与活动、服务和广播接收器不同,内容提供程序在 ContentResolver 的请求目标时被激活。内容解析器处理与内容提供程序的所有直接事务,执行与提供程序事务的组件调用 ContentResolver 对象上的方法。出于安全原因,这在内容提供程序和请求信息的组件之间留下了抽象层。

激活每种类型的组件都有单独的方法

  • 您可以启动活动或为其提供新的操作内容,方法是将 Intent 传递给 startActivity(),或者当您希望活动返回结果时,传递给 startActivityForResult()
  • 在 Android 5.0(API 级别 21)及更高版本中,您可以使用 JobScheduler 类来安排操作。对于较早的 Android 版本,您可以启动服务或向正在进行的服务提供新指令,方法是将 Intent 传递给 startService()。您可以通过将 Intent 传递给 bindService() 来绑定到该服务。
  • 您可以通过将 Intent 传递给诸如 sendBroadcast()sendOrderedBroadcast() 之类的方法来启动广播。
  • 您可以通过在 ContentResolver 上调用 query() 来对内容提供程序执行查询。

有关使用意图的更多信息,请参阅 意图和意图过滤器 文档。以下文档提供了有关激活特定组件的更多信息:活动简介服务概述BroadcastReceiver内容提供程序

清单文件

在 Android 系统启动应用组件之前,系统必须通过读取应用的 *清单文件* AndroidManifest.xml 来知道该组件是否存在。您的应用在此文件中声明了其所有组件,该文件位于应用项目目录的根目录。

除了声明应用的组件之外,清单还执行许多操作,例如以下操作

  • 识别应用所需的任何用户权限,例如互联网访问或对用户联系人的读取访问权限。
  • 根据应用使用的 API 声明应用所需的最低 API 级别
  • 声明应用使用或需要的硬件和软件功能,例如相机、蓝牙服务或多点触控屏幕。
  • 声明应用需要链接到的 API 库(Android 框架 API 除外),例如 Google Maps 库

声明组件

清单的主要任务是通知系统应用的组件。例如,清单文件可以如下声明活动

<?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 属性指定用作活动的用户可见标签的字符串。

您必须使用以下元素声明所有应用组件

您在源代码中包含但在清单中未声明的活动、服务和内容提供程序对系统不可见,因此永远无法运行。但是,广播接收器可以在清单中声明,也可以在代码中动态创建为 BroadcastReceiver 对象,并通过调用 registerReceiver() 向系统注册。

有关如何为您的应用构建清单文件的更多信息,请参阅 应用清单概述

声明组件功能

激活组件 部分所述,您可以使用 Intent 启动活动、服务和广播接收器。您可以通过在意图中显式命名目标组件(使用组件类名)来执行此操作。您还可以使用隐式意图,它描述要执行的操作类型,以及可选地,您要对其执行操作的数据。隐式意图允许系统查找设备上可以执行该操作并启动它的组件。如果有多个组件可以执行意图描述的操作,则用户选择使用哪个组件。

注意:如果使用意图启动 Service,请确保您的应用安全,方法是使用 显式 意图。使用隐式意图启动服务是一种安全隐患,因为您无法确定哪个服务响应意图,并且用户也无法看到哪个服务启动。从 Android 5.0(API 级别 21)开始,如果您使用隐式意图调用 bindService(),系统会抛出异常。不要为您的服务声明意图过滤器。

系统通过将接收到的意图与设备上其他应用的清单文件中提供的 *意图过滤器* 进行比较来识别可以响应意图的组件。

在应用的清单中声明活动时,您可以选择包含意图过滤器来声明活动的的功能,以便它可以响应来自其他应用的意图。您可以通过添加 <intent-filter> 元素作为组件声明元素的子元素来执行此操作。

例如,如果您构建了一个带有用于撰写新电子邮件的活动的电子邮件应用,则可以声明一个意图过滤器以响应“发送”意图以发送新电子邮件,如下例所示

<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(),则系统可能会启动您的活动,以便用户可以起草并发送电子邮件。

有关创建意图过滤器的更多信息,请参阅 意图和意图过滤器 文档。

声明应用需求

有各种各样的设备由 Android 驱动,但并非所有设备都提供相同的特性和功能。为了防止您的应用安装在缺少应用所需的特性设备上,务必在清单文件中声明设备和软件需求,以明确定义应用支持的设备类型的配置文件。

大多数这些声明仅供参考。系统不会读取它们,但 Google Play 等外部服务会读取它们,以便在用户从其设备搜索应用时提供过滤。

例如,假设您的应用需要相机并使用 Android 8.0(API 级别 26)中引入的 API。您必须声明这些需求。minSdkVersiontargetSdkVersion 的值在应用模块的 build.gradle 文件中设置

android {
  ...
  defaultConfig {
    ...
    minSdkVersion 26
    targetSdkVersion 29
  }
}

注意:不要在清单文件中直接设置 minSdkVersiontargetSdkVersion,因为它们在构建过程中会被 Gradle 覆盖。有关更多信息,请参阅 指定 API 级别需求

您在应用的清单文件中声明相机功能

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    ...
</manifest>

通过这些示例中显示的声明,没有 相机或 Android 版本低于 8.0 的设备无法从 Google Play 安装您的应用。但是,您也可以声明您的应用使用相机,但不需要 它。为此,您可以将 required 属性设置为 false,在运行时检查设备是否具有相机,并在需要时禁用任何相机功能。

有关如何管理应用与不同设备的兼容性的更多信息,请参阅 设备兼容性概述

应用资源

Android 应用不仅仅由代码组成。它需要与源代码分离的资源,例如图像、音频文件以及与应用视觉呈现相关的任何内容。例如,您可以使用 XML 文件定义动画、菜单、样式、颜色和活动用户界面的布局。

使用应用资源可以轻松更新应用的各种特性,而无需修改代码。提供替代资源集可以让您为各种设备配置(例如不同的语言和屏幕尺寸)优化应用。

对于您在 Android 项目中包含的每个资源,SDK 构建工具都会定义一个唯一的整数 ID,您可以使用该 ID 从您的应用代码或从 XML 中定义的其他资源中引用该资源。例如,如果您的应用包含一个名为 logo.png 的图像文件(保存在 res/drawable/ 目录中),则 SDK 工具会生成一个名为 R.drawable.logo 的资源 ID。此 ID 映射到一个特定于应用的整数,您可以使用该整数引用该图像并将其插入到您的用户界面中。

将资源与源代码分开提供的最重要方面之一是能够为不同的设备配置提供替代资源。

例如,通过在 XML 中定义 UI 字符串,您可以将字符串翻译成其他语言并将这些字符串保存在单独的文件中。然后,Android 会根据您附加到资源目录名称的语言 *限定符*(例如,法语字符串值的 res/values-fr/)和用户的语言设置,将相应的语言字符串应用到您的 UI。

Android 支持许多用于替代资源的限定符。限定符是一个短字符串,您将其包含在资源目录的名称中以定义这些资源用于的设备配置。

例如,您可以根据设备的屏幕方向和大小为您的活动创建不同的布局。当设备屏幕处于纵向(高)方向时,您可能希望布局中的按钮垂直排列,但当屏幕处于横向(宽)方向时,您可能希望按钮水平对齐。要根据方向更改布局,您可以定义两个布局并将相应的限定符应用到每个布局的目录名称。然后,系统会根据当前设备方向自动应用相应的布局。

有关您可以包含在应用程序中的不同类型的资源以及如何为不同的设备配置创建替代资源的更多信息,请阅读 应用资源概述。要了解有关最佳实践和设计健壮的生产级应用的更多信息,请参阅 应用架构指南

其他资源

要使用视频和代码教程学习 Android 开发,请参阅 使用 Kotlin 开发 Android 应用 Udacity 课程。

继续阅读关于

意图和意图过滤器
了解如何使用 Intent API 激活应用组件(例如活动和服务),以及如何使您的应用组件可供其他应用使用。
活动简介
了解如何创建 Activity 类的实例,该类在您的应用程序中提供了一个具有用户界面的独立屏幕。
应用资源概述
了解 Android 应用如何构建以将应用资源与应用代码分开,包括如何为特定设备配置提供替代资源。

也值得关注

设备兼容性概述
了解 Android 如何在不同类型的设备上运行,以及如何为每个设备优化您的应用或限制您的应用在不同设备上的可用性。
Android 上的权限
了解 Android 如何使用权限系统限制应用对某些 API 的访问,该系统要求用户同意您的应用才能使用这些 API。