Android 技术演进与核心架构
本系列为小说《逆袭西二旗》的技术讲解,用于详细说明剧情里涉及的开发细节。

自 2008 年 9 月 23 日发布以来,Android 经历了巨大的演进。多年来,Android SDK 和整个生态发生了显著变化,新工具和解决方案层出不穷,比如 Android Architecture Components(AAC)、Jetpack 系列库,以及 Jetpack Compose。然而,尽管技术不断革新,Android 底层的一些核心机制——如 Intent、Window 和基础 View 架构——却始终保持高度稳定。
技术栈差异与面试准备策略
不同公司在构建 Android 应用时采用的技术栈和开发方式各不相同。因此,要想找到心仪的 Android 方面工作,必须根据职位描述中的最低要求,以及目标公司实际使用的技术,有针对性地准备面试。了解他们的技术需求,能让你更高效地聚焦重点。
面试题的难度因公司和面试官而异。与其死记硬背答案,不如深入理解底层概念,并多加实践应用。本系列中的问题旨在为你准备 Android 技术面试提供参考,并非涵盖所有可能考题的“标准答案手册”。
考察侧重点与评分标准
有些公司会侧重考察 Android 系统底层运作和架构,以评估你对 Android 基础的理解;另一些则更关注高层 API 或流行库的使用,看你能否快速将其整合进产品中。不同公司的评分标准也大相径庭,这意味着很多问题并没有唯一“完美”的答案。该系列提供的回答只是起点,鼓励你在此基础上进一步拓展和深挖。
本系列无意覆盖 Android 开发这一广阔领域中的所有可能问题,而是希望为你打下扎实基础,助你高效准备,并根据目标岗位的具体要求调整学习方向。需要特别说明的是,本系列并未深入探讨高级第三方库或底层硬件相关功能(如 Camera API、Bluetooth 等)。如果这些内容与你的职业目标相关,你需要额外查阅资料,自主补充知识。
Android 分层是什么
Android 是一个开源操作系统,主要面向智能手机和平板等移动设备。它由 Google 开发并维护,基于 Linux 内核,提供了一个强大而灵活的平台,能够适配各种硬件配置和设备。
面试问题
Android 的平台架构由多个层级组成,包括 Linux 内核、Android 运行时(ART)以及硬件抽象层(HAL)。你能解释一下这些组件是如何协同工作,以确保应用顺利运行并与硬件交互的吗?
关键特性
- 开源且高度可定制:Android 是开源的(即 Android 开源项目,AOSP),开发者和厂商可以根据自身需求自由修改和定制系统。这种灵活性推动了它在各类设备上的广泛应用与创新,包括智能穿戴设备、电视乃至物联网(IoT)设备。
- 基于 SDK 的应用开发:Android 应用主要使用 Java 或 Kotlin 编写,并配合 Android 软件开发工具包(SDK)。开发者可通过 Android Studio 等工具,在平台上完成应用的设计、开发与调试。
- 丰富的应用生态:Google Play 商店是 Android 官方的应用分发平台,提供数百万款涵盖游戏、生产力工具等各类别的应用。开发者也可通过第三方应用商店或直接提供 APK 文件进行独立分发。
- 多任务处理与资源管理:Android 支持多任务运行,用户可同时开启多个应用。系统采用托管式内存管理和高效的垃圾回收机制,在不同设备上优化性能表现。
- 广泛的硬件兼容性:Android 覆盖从入门级到高端旗舰的各类设备,对不同屏幕尺寸、分辨率及硬件配置均有出色的适配能力。
Android 系统架构
Android 平台架构采用模块化分层设计,由多个组件构成:
相信 Android 开发再熟悉不过这张图了!
那我们从下往上依次说明这些层:
Android 系统架构深度解析
Android 系统采用分层架构设计,每一层都承担着特定的职责,共同协作以提供稳定且高效的移动操作系统体验。理解这些层级及其相互关系,是进行高性能 Android 开发的基础。
底层核心:Linux 内核与电源管理
Linux 内核构成了 Android 操作系统的基石。它负责硬件抽象,确保软件与硬件之间的无缝交互。其核心职责包括内存与进程管理、安全策略执行,以及管理 Wi-Fi、蓝牙、显示等硬件组件的驱动程序。通过内核级的资源调度,Android 能够高效地处理多任务并发与硬件访问请求。
在 电源管理方面,Linux 内核中的电源子系统负责直接控制 CPU、设备和整机的休眠、唤醒与功耗状态。它为上层省电机制提供了关键的硬件级支持,确保设备在保持性能的同时,最大限度地延长电池续航时间。
中间层:硬件抽象层与运行时环境
硬件抽象层(HAL)提供了一组标准接口,将 Android 的 Java API 框架与设备硬件连接起来。它由多个库模块组成,每个模块针对特定硬件(如摄像头或蓝牙)进行定制。当框架 API 请求访问硬件时,Android 系统会动态加载对应的 HAL 模块来完成请求,从而实现了上层应用与底层硬件的解耦。
Android 运行时(ART)与核心库构成了应用执行的基石。ART 负责执行由 Kotlin 或 Java 编译而来的字节码,支持“提前编译”(AOT)和“即时编译”(JIT),以优化应用启动速度与运行性能。同时,核心库提供了数据结构、文件操作、多线程等关键功能的 API,为应用开发构建了完整的运行环境,确保开发者能够便捷地调用系统级服务。
上层支撑:原生库与应用框架
原生 C/C++ 库包含一系列用 C 和 C++ 编写的原生库,用于支撑关键功能。例如,OpenGL 负责图形渲染,SQLite 提供数据库操作能力,WebKit 用于网页内容展示。这些库既被 Android 框架内部使用,也可被应用直接调用,以处理对性能要求较高的任务,如复杂图形处理或大数据量存储。
Android 框架(API)位于原生库之上,提供了面向开发者的高层服务和 API。这包括 ActivityManager、NotificationManager、ContentProvider 等核心组件,让开发者能够高效构建 Android 应用,并充分利用系统能力,如界面绘制、数据持久化和网络通信等。
顶层:应用程序
最上层是所有面向用户的应用,包括系统自带应用(如通讯录、设置)和基于 Android SDK 开发的第三方应用。这些应用依赖底层各模块,将功能顺畅地呈现给用户,完成从用户交互到硬件响应的完整闭环。每一层都向上提供服务,向下依赖下层,共同构成了 Android 强大而灵活的生态系统。
Intent 是什么
Intent 是对即将执行操作的一种抽象描述。它作为一种消息对象,让 Activity、Service 和 BroadcastReceiver 能够彼此通信。Intent 通常用于启动一个 Activity、发送广播或启动一个 Service,还能在组件之间传递数据,是 Android 基于组件架构的核心机制之一。
Android 中主要有两种 Intent:显式 Intent 和隐式 Intent。
面试问题
显式 Intent 和隐式 Intent 的核心区别是什么?各自适用于哪些场景?
Android 系统如何决定由哪个应用来处理一个隐式 Intent?如果找不到合适的应用,又会发生什么?
显式 Intent
定义:显式 Intent 通过直接指定组件名称,明确指出要启动的具体组件(Activity 或 Service)。
使用场景:当你明确知道目标组件时使用显式 Intent,例如启动自己应用内的某个特定 Activity。
这种方式具有极高的确定性,系统会直接根据指定的类名或包名找到对应的组件并启动它。由于不需要系统进行匹配解析,其执行效率相对较高,且不易受到其他应用安装或卸载的影响。在应用内部进行页面跳转或服务调用时,显式 Intent 是最常用的方式,因为它能确保操作精确指向开发者预期的目标组件,避免了因系统匹配规则变化导致的意外行为。
例如,在 Activity A 中启动 Activity B,如果 B 是应用内部的一个固定页面,直接使用显式 Intent 指定 B 的 Class 对象即可。这种方式不仅代码意图清晰,而且便于静态分析工具进行依赖检查,有助于提高代码的可维护性和健壮性。此外,在启动 Service 时,若需确保特定的后台任务由特定的 Service 处理,显式 Intent 也是最佳选择,它能避免系统因匹配到多个符合条件的 Service 而产生不可预知的行为。
隐式 Intent
定义:隐式 Activity 不直接指定组件名称,而是通过指定操作(Action)、数据(Data)和类别(Category)等属性,让系统来寻找最合适的组件来处理该请求。
使用场景:当你不确定具体由哪个组件处理,或者希望调用其他应用的功能时使用隐式 Activity。例如,调用系统相机拍照、打开网页或拨打电话。
隐式 Intent 的核心在于“匹配”。系统会根据 Intent 中声明的 Action、Data 和 Category,去查询所有已注册的组件,找出那些在 AndroidManifest.xml 中声明了匹配 IntentFilter 的组件。如果找到多个匹配的组件,系统会弹出一个选择框让用户决定使用哪个应用;如果只找到一个,则直接启动;若一个都没找到,则会抛出 ActivityNotFoundException 异常。
这种方式极大地增强了应用的灵活性和可扩展性。它允许你的应用与其他应用进行交互,而无需知道对方的具体实现细节。例如,你的应用只需声明“我要查看一张图片”,系统会自动调用任何支持查看图片的应用(如相册、浏览器或第三方看图软件)。这种解耦使得应用生态更加丰富,开发者可以专注于自身功能,而将其他任务委托给系统中已有的优秀组件。
然而,使用隐式 Intent 时需要注意权限和数据格式的正确性。如果 Data 的 URI 格式或 MIME 类型与目标组件声明的不符,可能导致匹配失败。此外,由于目标组件可能来自其他应用,你需要考虑目标应用是否已安装、是否有权限访问相关数据等边界情况,通常建议在使用前进行适当的检查或提供默认处理逻辑,以提升用户体验。
显式 Intent
示例场景:如果你在同一个应用内从一个 Activity 跳转到另一个 Activity,就应该使用显式 Intent。
你可以像下面这样使用显式 Intent:
val intent = Intent(this, TargetActivity::class.java)
startActivity(intent)
隐式 Intent
定义:隐式 Intent 不指定具体组件,而是声明一个通用的操作意图。系统会根据该 Intent 中的动作(action)、类别(category)和数据(data),自动匹配能够处理它的组件。
使用场景:当你希望执行一个可由其他应用或系统组件处理的操作时,隐式 Intent 非常有用,比如打开一个网页链接或分享内容。
示例场景:如果你要通过浏览器打开一个网页,或者把内容分享给其他应用,就应该使用隐式 Intent。此时,系统会决定由哪个应用来处理这个请求。
你可以像下面这样使用隐式 Intent:
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
startActivity(intent)
如果使用隐式 Intent 启动 Activity,但系统中没有应用能处理该 Intent,会抛出 ActivityNotFoundException。
正确做法是,在调用 startActivity() 之前,应使用 PackageManager 检查是否存在可处理的 Activity:
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "Hello")
}
// 检查是否有应用能处理
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent)
} else {
// 提示用户或降级处理
Toast.makeText(this, "没有应用可以处理此操作", Toast.LENGTH_SHORT).show()
}
总结
进阶:什么是 IntentFilter
Android 中的 IntentFilter 用于定义应用组件如何响应特定的 Intent,比如打开一个链接或处理一条广播。它本质上是一种过滤器,用来声明某个 Activity、Service 或 BroadcastReceiver 能够处理哪些类型的 Intent,这些声明写在 AndroidManifest.xml 文件中。
每个 IntentFilter 可以包含 Action、Category 和数据类型,以便精准匹配传入的 Intent。合理地定义 IntentFilter,能让你的应用与其他应用及系统组件无缝协作,从而提升整体功能体验。
当发送一个隐式 Intent 时,Android 系统会根据该 Intent 的属性,与已安装应用的清单文件中声明的 IntentFilter 进行匹配,从而确定应启动哪个组件。如果找到匹配项,系统就会启动对应的组件,并将 Intent 对象传递给它。如果多个组件都能匹配该 Intent,系统会弹出一个选择器对话框,让用户自行决定使用哪个应用来处理该操作。
这种机制的核心在于解耦。发送者无需知道接收者的具体存在,只需描述意图;接收者通过声明自己能处理什么,被动地等待匹配。这种设计极大地增强了系统的扩展性。例如,一个图片分享应用无需硬编码所有支持图片查看的 App 包名,只需声明自己支持图片分享,系统会自动列出所有能处理图片的 App。用户点击后,数据便通过 Intent 传递给目标组件。若未找到匹配项,系统通常会抛出异常或提示无可用应用,因此开发者需做好异常处理。此外,Action 和 Category 的组合使用,可以进一步缩小匹配范围,确保意图被正确的组件接收,避免误操作。这种精细的控制能力,是构建复杂 Android 应用生态的基础。
PendingIntent 又是什么
PendingIntent 详解
PendingIntent 是一种特殊的 Intent,它授权其他应用或系统组件在未来某个时刻,以你应用的名义执行一个预先定义好的 Intent。这种机制特别适用于那些需要在你的应用生命周期之外触发的操作,比如发送通知或与后台服务交互。
面试问题
什么是 PendingIntent?它和普通 Intent 有什么区别?
能否举一个必须使用 PendingIntent 的场景?
核心特性
PendingIntent 本质上是对普通 Intent 的一层封装,能让这个 Intent 在你的应用生命周期结束后依然有效。它把执行 Intent 的权限委托给其他应用或系统服务,并且这些组件会以你应用的身份和权限来执行操作。PendingIntent 可用于 Activity、Service 或 BroadcastReceiver。
它主要有三种形式:
- Activity:启动一个 Activity。
- Service:启动一个 Service。
- Broadcast:发送一条广播。
你可以通过 PendingIntent.getActivity()、PendingIntent.getService() 或 PendingIntent.getBroadcast() 等工厂方法来创建对应的 PendingIntent。
// 1. 创建 Intent 跳转到目标 Activity
val intent = Intent(this, MyActivity::class.java)
// 2. 创建 PendingIntent,用于通知点击后启动 Activity
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT // 更新现有 PendingIntent
)
// 3. 构建通知
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Title")
.setContentText("Content")
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent) // 点击通知时触发
.build()
// 4. 发送通知
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification)
PendingIntent 支持多种标志(flag),用于控制其行为以及与系统或其他组件的交互方式:
FLAG_UPDATE_CURRENT:利用新数据覆盖并更新已存在的PendingIntent实例,确保数据同步。FLAG_CANCEL_CURRENT:在创建新的PendingIntent之前,先取消已存在的实例,避免资源浪费或状态冲突。FLAG_IMMUTABLE:将PendingIntent设为不可变对象,防止接收方在后续流程中修改其内容,保证数据一致性。FLAG_ONE_SHOT:确保该PendingIntent只能使用一次,防止重复触发导致的逻辑错误。
使用场景
- 通知(Notifications):当用户点击通知栏消息时,可触发打开某个
Activity等操作,提升交互体验。 - 闹钟(Alarms):配合
AlarmManager调度定时任务,确保在指定时间准确执行后台逻辑。 - 服务(Services):将耗时任务委托给
ForegroundService或BroadcastReceiver,用于执行后台操作,避免阻塞主线程。
安全事项
始终为 PendingIntent 设置 FLAG_IMMUTABLE,以防止恶意应用篡改其内部的 Intent。这一点从 Android 12(API 级别 31)开始尤为重要——在某些场景下,系统已强制要求必须使用该标志,以增强应用间通信的安全性。
总结
PendingIntent 是 Android 中的一项核心机制,即使你的应用未在前台运行,也能实现与系统组件或其他应用之间的无缝通信。通过合理管理标志(flags)和权限,你可以确保延迟任务的安全、高效执行,同时降低被恶意利用的风险。
Serializable 和 Parcelable 有什么区别
面试问题深度解析
在 Android 开发面试中,Serializable 和 Parcelable 的区别是高频考点。面试官通常关注开发者是否理解底层实现机制及其对应用性能的影响。核心问题在于:为什么在组件间传递数据时,通常更推荐使用 Parcelable?这涉及到对内存分配、GC 压力以及序列化开销的综合考量。
Serializable 机制详解
Java 标准接口:Serializable 是 Java 语言层面提供的标准接口,其核心作用是将对象转换为字节流。这种机制使得对象能够在 Activity 之间进行传递,或者被持久化写入磁盘文件,实现了数据的跨进程或跨生命周期存储。
基于反射的实现:该机制严重依赖 Java 的反射机制。在运行时,系统会动态检查类结构及其字段信息以完成序列化过程。这种动态性虽然提供了灵活性,但也带来了显著的性能损耗。
性能瓶颈:相比 Parcelable,Serializable 的执行效率更低。主要原因在于反射操作本身的开销较大,且在序列化过程中会生成大量临时对象。这些临时对象会迅速增加内存占用,进而触发更频繁的垃圾回收(GC)活动,可能导致应用出现卡顿或掉帧现象。
适用场景:尽管性能较差,但当性能不是关键因素,或者在非 Android 特有的通用代码库中使用时,Serializable 依然是一个可行的选择。它因其简单易用的特性,在文件存储或非高频调用的场景下仍被广泛使用。
Parcelable 机制详解
Android 专属接口:Parcelable 是 Android 系统特有的接口,专为在 Android 组件间实现高性能的进程间通信(IPC)而设计。它要求开发者手动实现序列化逻辑,从而获得对序列化过程的完全控制。
性能优势:Parcelable 比 Serializable 更快且更高效。这是因为 Parcelable 针对 Android 系统进行了深度优化,完全不依赖反射机制。通过手动编写序列化代码,它可以避免创建大量临时对象,从而显著减少垃圾回收的压力,提升应用的整体运行流畅度。
Parcelize 插件与性能优化
适用场景:在 Android 中传递数据时,尤其是涉及 IPC 或在 Activity、Service 之间传输数据且对性能有要求的场景下,推荐使用 Parcelable。
在现代 Android 开发中,kotlin-parcelize 插件通过自动生成实现代码,大幅简化了创建 Parcelable 对象的过程。相比早期需要手动编写的方式,这种方法更加高效。只需在类上添加 @Parcelize 注解,插件便会自动生成所需的 Parcelable 实现。下面是一个示例,展示其工作方式:
import kotlinx.parcelize.Parcelize
import android.os.Parcelable
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int) : Parcelable
通过这样的设置,你不再需要手动重写 writeToParcel 方法或实现 CREATOR,大幅减少了样板代码,提升了可读性。
如果你的类使用了
@Parcelize注解,但其中包含了一个既不是基本类型、又未被@Parcelize标记的属性,就会遇到如下错误:
Type is not directly supported by ‘Parcelize’. Annotate the parameter type with ‘@RawValue’ if you want it to be serialized using ‘writeValue()’.
这是因为Parcelize编译器插件在序列化时会尝试“展开”所有属性,而任何不支持或无法识别的类型都必须显式标记。
为避免这个问题,请确保所有复杂类型或自定义类型要么自身已正确实现Parcelable,要么用@RawValue注解标注,以表明你希望它们通过writeValue()进行手动序列化。
关键区别
总结
在 Android 应用开发中,优先选择 Parcelable,因其在多数场景下性能更优。
- 对于简单场景、非性能敏感操作,或处理与 Android 无关的通用 Java 代码,可使用
Serializable。 - 涉及 Android 特有组件且对性能有要求的场景(如进程间通信 IPC),应优先使用
Parcelable。它针对 Android 的 IPC 机制进行了高度优化,效率显著更高。
进阶:Parcel 与 Parcelable
Parcel 是 Android 中的容器类,用于在 Activity、Service 或 BroadcastReceiver 等不同组件间实现高性能进程间通信。其核心作用是对数据进行序列化(marshaling,即“扁平化”)和反序列化(unmarshaling,即“还原”),从而跨越 Android 的 IPC 边界传递数据。
Parcel 不仅能传输扁平化数据,还可携带指向活跃 IBinder 对象的引用,通过 IPC 机制进行传递。它专为高性能 IPC 传输设计,配合 Parcelable 接口,使对象能高效序列化并在组件间流转。
需注意,Parcel 并非通用序列化工具,严禁用于持久化存储。因其底层实现可能随系统版本变化,使用它可能导致旧数据无法读取。
序列化机制详解
基础序列化能力
该接口提供了丰富的 API,支持读写基本数据类型、数组以及 Parcelable 对象,使对象能够自行序列化并在需要时重建。此外,它还提供了一些优化方法:在处理 Parcelable 对象时,可省略写入类信息,但要求接收方提前知道数据类型,从而提升传输效率。
Android 特有接口
Parcelable 是 Android 特有的接口,用于将对象序列化以便通过 Parcel 传递。实现了 Parcelable 的对象可以被写入 Parcel,并从中恢复,因此非常适合在 Android 组件之间传递复杂数据。
| 特性 | Serializable | Parcelable |
|---|---|---|
| 类型 | 标准Java接口 | Android专用接口 |
| 性能 | 较慢,使用反射 | 更快,针对Android优化 |
| 垃圾创建 | 产生更多垃圾(更多对象) | 产生较少垃圾(高效) |
| 使用场景 | 适用于通用Java使用 | 推荐用于Android,尤其是IPC |
Intent
Intent
Serializable
Parcelable
Activity
Fragment
Parcel







