Android应用组件的全面解析

发表时间: 2023-11-16 14:57

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

共有四种不同的应用组件类型:

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

每种类型都有不同的用途和生命周期,后者会定义如何创建和销毁组件。

Activity

Activity 是与用户交互的入口点。是用户界面的展示和交互的组件。它通常代表应用中的一个屏幕,例如登录界面或设置界面。用户可以通过点击应用图标或其他方式进入特定的活动。

Activity 有助于完成系统和应用程序之间的以下重要交互:

  • Android 系统会追踪用户当前关心的内容(屏幕上显示的内容),以确保系统继续运行托管 Activity 的进程。
    Android系统会跟踪用户当前关注的内容,也就是当前显示在屏幕上的Activity。系统会优先保持托管当前活动(即当前用户正在与之交互的Activity)的进程保持活动状态,以便快速响应用户的操作和保持应用程序的可见性。
    这种机制可以确保当系统资源紧张时,系统不会轻易终止用户正在使用的应用程序。相反,它会尽量保持托管活动的进程处于活动状态,并根据需要回收其他不活跃的进程或释放资源。这样,用户可以保持与当前活动的交互,并获得更好的响应性能。
  • Android系统会了解先前使用的进程包含用户可能返回的内容(已停止的 Activity),从而更优先保留这些进程。
    Android系统通过了解先前使用的进程中可能包含用户可能返回的内容,从而更优先地保留这些进程。这种机制可以加速应用切换和重新加载,并提供流畅的用户体验。
  • Android系统会帮助应用处理终止其进程的情况,以便用户可以返回已恢复其先前状态的 Activity。
  • 提供一种途径,让应用实现彼此之间的用户流,并让系统协调这些用户流。(此处最经典的示例是共享。)

Service

服务是一个通用入口点,用于因各种原因使应用在后台保持运行状态。它是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。服务不提供界面。例如,当用户使用其他应用时,服务可能会在后台播放音乐或通过网络获取数据,但这不会阻断用户与 Activity 的交互。诸如 Activity 等其他组件可以启动服务,使该服务运行或绑定到该服务,以便与其进行交互。事实上,有两种截然不同的语义服务可以告知系统如何管理应用:已启动服务会告知系统使其运行至工作完毕。此类工作可以是在后台同步一些数据,或者在用户离开应用后继续播放音乐。在后台同步数据或播放音乐也代表了两种不同类型的已启动服务,而这些服务可以修改系统处理它们的方式:

  • 音乐播放是用户可直接感知的服务,因此,应用会向用户发送通知,表明其希望成为前台,从而告诉系统此消息;在此情况下,系统明白它应尽全力维持该服务进程运行,因为进程消失会令用户感到不快。
  • 通常,用户不会意识到常规后台服务正处于运行状态,因此系统可以更自由地管理其进程。如果系统需要使用 RAM 来处理用户更迫切关注的内容,则其可能允许终止服务(然后在稍后的某个时刻重启服务)。

绑定服务之所以能运行,原因是某些其他应用(或系统)已表示希望使用该服务。从根本上讲,这是为另一个进程提供 API 的服务。因此,系统会知晓这些进程之间存在依赖关系,所以如果进程 A 绑定到进程 B 中的服务,系统便知道自己需使进程 B(及其服务)为进程 A 保持运行状态。此外,如果进程 A 是用户关心的内容,系统随即也知道将进程 B 视为用户关心的内容。由于存在灵活性(无论好坏),服务已成为非常有用的构建块,并且可实现各种高级系统概念。动态壁纸、通知侦听器、屏幕保护程序、输入方法、无障碍功能服务以及众多其他核心系统功能均可构建为在其运行时由应用实现、系统绑定的服务。

广播接收器

借助广播接收器组件,系统能够在常规用户流之外向应用传递事件,从而允许应用响应系统范围内的广播通知。由于广播接收器是另一个明确定义的应用入口,因此系统甚至可以向当前未运行的应用传递广播。例如,应用可通过调度提醒来发布通知,以告知用户即将发生的事件。而且,通过将该提醒传递给应用的广播接收器,应用在提醒响起之前即无需继续运行。许多广播均由系统发起,例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可发起广播,例如,通知其他应用某些数据已下载至设备,并且可供其使用。尽管广播接收器不会显示界面,但其可以创建状态栏通知,在发生广播事件时提醒用户。但广播接收器更常见的用途只是作为通向其他组件的通道,旨在执行极少量的工作。

内容提供程序

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

例如,Android 系统可提供管理用户联系人信息的内容提供程序。因此,任何拥有适当权限的应用均可查询内容提供程序(如 ContactsContract.Data),以读取和写入特定人员的相关信息。

我们很容易将内容提供程序看作数据库上的抽象,因为其内置的大量 API 和支持时常适用于这一情况。但从系统设计的角度看,二者的核心目的不同。对系统而言,内容提供程序是应用的入口点,用于发布由 URI 架构识别的已命名数据项。因此,应用可以决定如何将其包含的数据映射到 URI 命名空间,进而将这些 URI 分发给其他实体。反之,这些实体也可使用分发的 URI 来访问数据。在管理应用的过程中,系统可以执行以下特殊操作:

  • 分配 URI 无需应用保持运行状态,因此 URI 可在其所属的应用退出后继续保留。当系统必须从相应的 URI 检索应用数据时,系统只需确保所属应用仍处于运行状态。

内容提供程序作为 ContentProvider 的子类实现,并且其必须实现一组标准 API,以便其他应用能够执行事务。

Android 系统设计的独特之处在于,任何应用都可启动其他应用的组件。例如,当您想让用户使用设备相机拍摄照片时,另一个应用可能也可执行该操作,因而您的应用便可使用该应用,而非自行产生一个 Activity 来拍摄照片。您无需加入甚至链接到该相机应用的代码。只需启动拍摄照片的相机应用中的 Activity 即可。完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,这就如同相机是您应用的一部分。

当系统启动某个组件时,它会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。因此,与大多数其他系统上的应用不同,Android 应用并没有单个入口点(即没有 main() 函数)。

由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件,但 Android 系统可以。如要启动其他应用中的组件,请向系统传递一条消息,说明启动特定组件的 Intent。系统随后便会为您启动该组件。

启动组件

在四种组件类型中,有三种(Activity、服务和广播接收器)均通过异步消息 Intent 进行启动。Intent 会在运行时对各个组件进行互相绑定。您可以将 Intent 视为从其他组件(无论该组件是属于您的应用还是其他应用)请求操作的信使。

您需使用 Intent 对象创建 Intent,该对象通过定义消息来启动特定组件(显式 Intent)或特定的组件类型(隐式 Intent)。

  1. 显式Intent: 当你知道要启动的组件的确切类名时,可以使用显式Intent。这意味着你明确指定了要启动的目标组件。
Intent explicitIntent = new Intent(CurrentActivity.this, TargetActivity.class);startActivity(explicitIntent);

在这个例子中,TargetActivity 是你要启动的特定组件。

  1. 隐式Intent: 当你希望启动某个类型的组件,而不考虑具体的类名时,可以使用隐式Intent。你描述了要执行的动作或者要处理的数据,并让系统找到可以响应该动作或数据的组件。
Intent implicitIntent = new Intent("com.example.ACTION_VIEW");startActivity(implicitIntent);

对于 Activity 和服务,Intent 会定义要执行的操作(例如,查看发送某内容),并且可指定待操作数据的 URI,以及正在启动的组件可能需要了解的信息。例如,Intent 可能会传达对 Activity 的请求,以便显示图像或打开网页。在某些情况下,您可以通过启动 Activity 来接收结果,这样 Activity 还会返回 Intent 中的结果。例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您。返回 Intent 包含指向所选联系人的 URI。

  • 启动 Activity 并接收结果示例:

在某些情况下,你可以通过启动 Activity 来接收结果。这意味着你可以发起一个 Intent 请求,启动另一个 Activity 来执行某项操作,并在操作完成后返回结果。例如,你可以发出一个 Intent,让用户选择某个联系人,然后接收包含所选联系人的信息的返回 Intent。

// 启动 Activity 并期望结果startActivityForResult(intent, requestCode);

然后,在目标 Activity 中,当操作完成时,将结果设置为返回 Intent 并调用 setResult() 方法。

// 在目标 Activity 中设置结果并关闭 ActivityIntent resultIntent = new Intent();resultIntent.putExtra("selectedContact", selectedContact);setResult(Activity.RESULT_OK, resultIntent);finish();

最后,在调用 startActivityForResult() 的地方,你可以在回调方法中获取结果。

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {        // 处理返回的数据        String selectedContact = data.getStringExtra("selectedContact");        // ...    }}

总的来说,这段内容说明了在 Android 中使用 Intent 来定义和执行操作,以及在某些情况下通过启动 Activity 来接收操作的结果。这为实现组件之间的灵活通信提供了一种便捷的方式。

对于广播接收器,Intent 只会定义待广播的通知。例如,指示设备电池电量不足的广播只包含指示“电池电量不足”的已知操作字符串。

与 Activity、服务和广播接收器不同,内容提供程序并非由 Intent 启动。相反,它们会在成为 ContentResolver 的请求目标时启动。内容解析程序会通过内容提供程序处理所有直接事务,因此通过提供程序执行事务的组件便无需执行事务,而是改为在 ContentResolver 对象上调用方法。这会在内容提供程序与请求信息的组件之间留出一个抽象层(以确保安全)。

每种组件都有不同的启动方法:

  • 如要启动 Activity,您可以向 startActivity()startActivityForResult() 传递 Intent(当您想让 Activity 返回结果时),或者为其安排新任务。
  • 在 Android 5.0(API 级别 21)及更高版本中,您可以使用 JobScheduler 类来调度操作。对于早期 Android 版本,您可以通过向 startService() 传递 Intent 来启动服务(或对执行中的服务下达新指令)。您也可通过向将 bindService() 传递 Intent 来绑定到该服务。
  • 您可以通过向 sendBroadcast()sendOrderedBroadcast()sendStickyBroadcast() 等方法传递 Intent 来发起广播。
  • 您可以通过在 ContentResolver 上调用 query(),对内容提供程序执行查询。