1 绪论
可以使用 Kotlin、Java、C++ 语言来开发 Android 应用。Android SDK 工具将代码以及任何数据和资源文件编译到 APK 或 Android App Bundle。
Android 软件包(即带有.apk
后缀的归档文件),包含运行时所需的 Android 应用的内容。它是用于在 Android 设备上安装应用的文件。
Android App Bundle 是一种带有.aab
后缀的归档文件,其中包含 Android 应用项目的内容,包括一些在运行时不需要的额外的元数据。AAB 是一种发布格式,无法安装在 Android 设备上。
每个 Android 应用都处于各自的安全沙箱中,受到以下 Android 安全功能的保护:
- Android 操作系统是一种多用户 Linux 系统,其中的每个应用都是一个不同的用户。
- 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID,它仅被系统使用,并且应用是不知道该 ID 的。系统为应用中的所有文件设置权限,以便只有分配给该用户 ID 的应用可以访问它们。
- 每个进程都有自己的虚拟机(VM),因此应用的代码运行会和其它应用隔离。
- 默认情况下,每个应用都在自己的 Linux 进程中运行。Android 系统在任何应用组件需要被执行时启动进程,然后在它不再被需要或在系统必须为其它应用恢复内存时关闭进程。
Android 系统实现了最小权限原则。也就是说,默认情况下,每个应用仅可以访问执行其工作的组件。这将创建一个非常安全的环境,其中应用不能访问系统中没有权限的部分。
2 应用组件
应用组件是 Android 应用的基本构建块。每个组件是一个入口点,系统或用户可以通过该入口点进入您的应用。部分组件依赖于其它组件。
应用组件有四种类型:
- Activities
- Services
- Broadcast receivers
- Content providers
每种类型都有不同的用途,并且具有定义组件的创建和销毁方式的独特生命周期。
2.1 Activities
Activity 是与用户交互的入口点。它代表了一个带有用户界面的单一屏幕。例如,一个电子邮件应用可能有一个 activity 显示邮件的列表,一个 activity 用于撰写邮件,以及另一个 activity 用于阅读邮件。虽然这些 activities 协同工作,在电子邮件应用中形成统一的用户体验,但是每个 activity 都相互独立。
其它应用可以启动其中任一应用的 activity(如果应用允许的话)。例如,相机应用可能会启动电子邮件应用中的 activity,用于撰写新邮件,让用户共享图片。
可以将 activity 实现为Activity
类的子类。
2.2 Services
Service 是用于保持处于各种原因在后台运行的通用入口点。它是一个运行在后台的组件,用于执行长时间运行的操作,或为远程进程执行工作。Service 不提供界面。例如,Service 可能在用户使用其它应用时在后台播放音乐,或者它可以在不阻止用于与 Activity 互动的情况下通过网络提取数据。另一个组件可以启动 Service 并运行它,或绑定它并与之交互。
有两种类型的 service 可以指示系统如何管理应用:已启动的服务和绑定服务。
已启动的服务会告知系统使其保持运行状态,直到其工作完成为止。这可能是在后台同步一些数据,或者在用户离开应用后继续播放音乐。在后台同步数据或播放音乐代表了不同类型的已启动的服务,系统处理它们的方式有所不同:
- 音乐播放是用户能直接感知到的内容,应用通过指示它想在前台播放来将其传达给系统,并通知用户它正在运行。在这种情况下,系统会优先考虑保持该服务的进程运行,因为如果它消失了,用户的体验会很糟糕。
- 常规后台服务不是用户直接知道的,因此系统在管理其流程方面有更多的自由。如果它需要RAM来处理用户更直接关心的事情,它可能会让它被杀死,稍后重新启动服务。
绑定服务运行是因为其他应用程序(或系统)表示希望使用该服务。绑定服务向另一个进程提供 API,系统知道这些进程之间存在依赖关系。因此,如果进程 A 绑定到进程 B 中的服务,则系统知道它需要保持进程 B 及其服务为 A 运行。此外,如果用户关心进程 A,则它知道将进程 B 视为用户也关心的东西。
由于其灵活性,服务是各种高级系统概念的有用构建块。实时壁纸、通知监听器、屏幕保护程序、输入法、辅助功能服务和许多其他核心系统功能都是作为应用程序实现的服务构建的,系统在运行时会绑定到这些服务。
服务实现为Service
类的子类。
2.3 Broadcast receivers
广播接收器允许系统在常规用户流之外将事件传递给应用程序,以便应用程序可以响应系统范围内的广播公告。因为广播接收器是应用程序中另一个定义明确的入口,所以该系统甚至可以向当前未运行的应用程序发送广播。例如,一个应用程序可以安排一个闹钟来发布通知,告诉用户即将发生的事件。因为警报会传递到应用程序中的BroadcastReceiver
,所以在警报响起之前,应用程序不需要一直运行。
许多广播都来自该系统,比如宣布屏幕关闭、电池电量低或拍摄图片的广播。应用程序还可以启动广播,例如让其他应用程序知道某些数据已下载到设备上,可供他们使用。
虽然广播接收器不显示用户界面,但它们可以创建状态栏通知,以便在广播事件发生时提醒用户。然而,更常见的是,广播接收器只是通往其他组件的网关,其目的是做很少的工作。例如,广播接收器可能会使用JobScheduler
根据事件安排JobService
执行某些工作。广播接收器通常涉及相互交互的应用程序,因此在设置它们时了解其安全影响非常重要。
广播接收器被实现为BroadcastReceiver
的子类,每个广播都作为Intent
对象传递。
2.4 Content providers
Content providers 管理一组共享的应用程序数据,您可以将其存储在文件系统、SQLite数据库、网络或应用程序可以访问的任何其他持久存储位置。如果内容提供商允许,其他应用程序可以通过内容提供商查询或修改数据。例如,Android 系统提供了一个管理用户联系信息的 content provider。任何具有适当权限的应用程序都可以查询 content provider,例如使用ContactsConact.Data
读写特定人的信息。
人们很容易将 content provider 视为数据库上的抽象,因为对于这种常见情况,它们内置了大量 API 和支持。然而,从系统设计的角度来看,它们有不同的核心目的。
对于该系统来说,内容提供者是应用程序的入口点,用于发布由 URI 方案标识的命名数据项。因此,应用程序可以决定如何将其包含的数据映射到 URI 命名空间,将这些 URI 分发给其他实体,这些实体反过来可以使用它们来访问数据。这让系统在管理应用程序时可以做一些特别的事情:
- 分配 URI 并不要求应用程序保持运行,因此 URI 可以在其所属应用程序退出后保留。当系统从相应的 URI 检索应用程序的数据时,它只需要确保所属应用程序仍在运行。
- 这些 URI 还提供了一个重要的细粒度安全模型。例如,一个应用程序可以将它所拥有的图像的 URI 放置在剪贴板上,但将其内容提供商锁定,这样其他应用程序就无法自由访问它。当第二个应用程序尝试访问剪贴板上的该 URI 时,系统可以让该应用程序使用临时 URI 权限授予访问数据,这样它就只能访问该 URI 后面的数据,而不能访问第二个应用中的其他数据。
Content provider 对于读取和写入应用程序专有且不共享的数据也很有用。
Content provider 是作为ContentProvider
的子类实现的,必须实现一组标准的API,使其他应用程序能够执行事务。
Android 系统设计的一个独特之处是,任何应用程序都可以启动另一个应用程序的组件。例如,如果你想让用户用设备摄像头拍摄照片,可能还有另一个应用程序可以做到这一点------你的应用程序可以使用它,而不是自己开发一个活动来拍摄照片。你不需要整合甚至链接到相机应用程序中的代码。相反,您可以在拍摄照片的相机应用程序中开始活动。完成后,照片甚至会返回到您的应用程序中,以便您可以使用它。对用户来说,相机似乎实际上是您应用程序的一部分。
当系统启动一个组件时,如果该应用程序尚未运行,它将启动该应用程序的进程,并实例化该组件所需的类。例如,如果您的应用程序在捕获照片的相机应用程序中启动活动,则该活动将在属于相机应用程序的进程中运行,而不是在应用程序的过程中运行。因此,与大多数其他系统上的应用程序不同,Android 应用程序没有单一的入口点:没有main()
函数。
由于系统在单独的进程中运行每个应用程序,并具有限制访问其他应用程序的文件权限,因此您的应用程序无法直接从另一个应用程序激活组件。然而,Android 系统可以。要激活另一个应用程序中的组件,您需要向系统发送一条消息,指定您启动特定组件的 intent。然后,系统会为您激活该组件。
2.5 激活组件
一个称为 intent 的异步消息会激活三种组件类型:活动、服务和广播接收器。intents 在运行时将各个组件相互绑定。您可以将它们视为向其他组件请求操作的信使,无论该组件属于您的应用程序还是其他应用程序。
Intent 是通过Intent
对象创建的,该对象定义了一条消息来激活特定组件(显式 intent)或特定类型的组件(隐式 intent)。
对于活动和服务,意图定义了要执行的操作,例如查看或发送某些内容,并可能指定要操作的数据的URI,以及正在启动的组件可能需要知道的其他内容。
例如,意图可能会传达对显示图像或打开网页的活动的请求。在某些情况下,您可以启动一个活动来接收结果,在这种情况下,活动也会在Intent
中返回结果。您还可以发出一个意图,让用户选择一个个人联系人并将其返回给您。返回意图包括指向所选联系人的 URI。
对于广播接收器,意图定义了广播公告。例如,指示设备电池电量低的广播仅包括指示电池电量低。
与活动、服务和广播接收器不同,当内容解析器发出请求时,内容提供商会被激活。内容解析器处理与内容提供者的所有直接事务,与提供者执行事务的组件调用ContentResolver
对象的方法。出于安全原因,这在内容提供者和请求信息的组件之间留下了一个抽象层。
激活每种类型的组件都有单独的方法:
- 您可以通过向
startActivity()
传递Intent
来启动一个活动或为其提供新的任务,或者,当您希望活动返回结果时,可以通过startActivityForResult()
来启动活动。 - 您可以使用
JobScheduler
类来安排操作。您可以通过向bindService()
传递Intent
来绑定到服务。 - 您可以通过将
Intent
传递给sendBroadcast()
或sendOrderedBroadcast()
等方法来启动广播。 - 您可以通过在
ContentResolver
上调用query()
来执行对内容提供者的查询。