不卖课,纯干货!Android分层你知多少?

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

自 2008 年 9 月 23 日发布以来,Android 经历了巨大的演进。多年来,Android SDK 和整个生态发生了显著变化,新工具和解决方案层出不穷,比如 Android Architecture Components(AAC)、Jetpack 系列库,以及 Jetpack Compose。然而,尽管技术不断革新,Android 底层的一些核心机制------如 IntentWindow 和基础 View 架构------却始终保持高度稳定。

不同公司在构建 Android 应用时采用的技术栈和开发方式各不相同。因此,要想找到心仪的 Android 方面工作,必须根据职位描述中的最低要求,以及目标公司实际使用的技术,有针对性地准备面试。了解他们的技术需求,能让你更高效地聚焦重点。

面试题的难度因公司和面试官而异。与其死记硬背答案,不如深入理解底层概念,并多加实践应用。本系列中的问题旨在为你准备 Android 技术面试提供参考,并非涵盖所有可能考题的"标准答案手册"。

有些公司会侧重考察 Android 系统底层运作和架构,以评估你对 Android 基础的理解;另一些则更关注高层 API 或流行库的使用,看你能否快速将其整合进产品中。不同公司的评分标准也大相径庭,这意味着很多问题并没有唯一"完美"的答案。该系列提供的回答只是起点,鼓励你在此基础上进一步拓展和深挖。

本系列无意覆盖 Android 开发这一广阔领域中的所有可能问题,而是希望为你打下扎实基础,助你高效准备,并根据目标岗位的具体要求调整学习方向。需要特别说明的是,本系列并未深入探讨高级第三方库或底层硬件相关功能(如 Camera API、Bluetooth 等)。如果这些内容与你的职业目标相关,你需要额外查阅资料,自主补充知识。

祝你在 Android 面试准备中一切顺利!

Android 分层是什么

Android 是一个开源操作系统,主要面向智能手机和平板等移动设备。它由 Google 开发并维护,基于 Linux 内核,提供了一个强大而灵活的平台,能够适配各种硬件配置和设备。

面试问题

Android 的平台架构由多个层级组成,包括 Linux 内核、Android 运行时(ART)以及硬件抽象层(HAL)。你能解释一下这些组件是如何协同工作,以确保应用顺利运行并与硬件交互的吗?

关键特性

  1. 开源且高度可定制:Android 是开源的(即 Android 开源项目,AOSP),开发者和厂商可以根据自身需求自由修改和定制系统。这种灵活性推动了它在各类设备上的广泛应用与创新,包括智能穿戴设备、电视乃至物联网(IoT)设备。
  2. 基于 SDK 的应用开发:Android 应用主要使用 Java 或 Kotlin 编写,并配合 Android 软件开发工具包(SDK)。开发者可通过 Android Studio 等工具,在平台上完成应用的设计、开发与调试。
  3. 丰富的应用生态:Google Play 商店是 Android 官方的应用分发平台,提供数百万款涵盖游戏、生产力工具等各类别的应用。开发者也可通过第三方应用商店或直接提供 APK 文件进行独立分发。
  4. 多任务处理与资源管理:Android 支持多任务运行,用户可同时开启多个应用。系统采用托管式内存管理和高效的垃圾回收机制,在不同设备上优化性能表现。
  5. 广泛的硬件兼容性:Android 覆盖从入门级到高端旗舰的各类设备,对不同屏幕尺寸、分辨率及硬件配置均有出色的适配能力。

Android 系统架构

Android 平台架构采用模块化分层设计,由多个组件构成:

相信 Android 开发再熟悉不过这张图了!

那我们从下往上依次说明这些层:

  • 电源管理:Linux 内核中的电源子系统,负责直接控制 CPU、设备和整机的休眠、唤醒与功耗状态,为上层省电机制提供硬件级支持。
  • Linux 内核:Linux 内核构成了 Android 操作系统的基础,负责硬件抽象,确保软件与硬件之间的无缝交互。其核心职责包括内存与进程管理、安全策略执行,以及管理 Wi-Fi、蓝牙、显示等硬件组件的驱动程序。
  • 硬件抽象层(HAL) :硬件抽象层(HAL)提供了一组标准接口,将 Android 的 Java API 框架与设备硬件连接起来。它由多个库模块组成,每个模块针对特定硬件(如摄像头或蓝牙)进行定制。当框架 API 请求访问硬件时,Android 系统会动态加载对应的 HAL 模块来完成请求。
  • Android 运行时(ART)与核心库:Android 运行时(ART)负责执行由 Kotlin 或 Java 编译而来的字节码。ART 支持"提前编译"(AOT)和"即时编译"(JIT),以优化应用性能。核心库则提供了数据结构、文件操作、多线程等关键功能的 API,为应用开发构建了完整的运行环境。
  • 原生 C/C++ 库 :Android 包含一系列用 C 和 C++ 编写的原生库,用于支撑关键功能。例如,OpenGL 负责图形渲染,SQLite 提供数据库操作能力,WebKit 用于网页内容展示。这些库既被 Android 框架内部使用,也可被应用直接调用,以处理对性能要求较高的任务。
  • Android 框架(API) :应用框架层提供了面向开发者的高层服务和 API,包括 ActivityManagerNotificationManagerContentProvider 等,让开发者能够高效构建 Android 应用,并充分利用系统能力。
  • 应用程序:最上层是所有面向用户的应用,包括系统自带应用(如通讯录、设置)和基于 Android SDK 开发的第三方应用。这些应用依赖底层各模块,将功能顺畅地交付给用户。

总结

Android 是全球使用最广泛的移动操作系统,占据着绝对的市场份额。它不仅推动创新,还为开发者提供了触达数十亿用户的机会。凭借其高度的适应性和开源特性,Android 得以在各种市场中蓬勃发展,并成为众多非手机设备的基石。

Intent 是什么

Intent 是对即将执行操作的一种抽象描述。它作为一种消息对象,让 ActivityServiceBroadcastReceiver 能够彼此通信。Intent 通常用于启动一个 Activity、发送广播或启动一个 Service,还能在组件之间传递数据,是 Android 基于组件架构的核心机制之一。

Android 中主要有两种 Intent:显式 Intent 和隐式 Intent

面试问题

显式 Intent 和隐式 Intent 的核心区别是什么?各自适用于哪些场景?

Android 系统如何决定由哪个应用来处理一个隐式 Intent?如果找不到合适的应用,又会发生什么?

显式 Intent

定义 :显式 Intent 通过直接指定组件名称,明确指出要启动的具体组件(ActivityService)。

使用场景 :当你明确知道目标组件时使用显式 Intent,例如启动自己应用内的某个特定 Activity

示例场景 :如果你在同一个应用内从一个 Activity 跳转到另一个 Activity,就应该使用显式 Intent

你可以像下面这样使用显式 Intent

Kotlin 复制代码
val intent = Intent(this, TargetActivity::class.java)
startActivity(intent)

隐式 Intent

定义 :隐式 Intent 不指定具体组件,而是声明一个通用的操作意图。系统会根据该 Intent 中的动作(action)、类别(category)和数据(data),自动匹配能够处理它的组件。

使用场景 :当你希望执行一个可由其他应用或系统组件处理的操作时,隐式 Intent 非常有用,比如打开一个网页链接或分享内容。

示例场景 :如果你要通过浏览器打开一个网页,或者把内容分享给其他应用,就应该使用隐式 Intent。此时,系统会决定由哪个应用来处理这个请求。

你可以像下面这样使用隐式 Intent

Kotlin 复制代码
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
startActivity(intent)

如果使用隐式 Intent 启动 Activity,但系统中没有应用能处理该 Intent,会抛出 ActivityNotFoundException

正确做法是,在调用 startActivity() 之前,应使用 PackageManager 检查是否存在可处理的 Activity

Kotlin 复制代码
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()
}

总结

显式 Intent 用于应用内部导航,目标组件是明确已知的。

而隐式 Intent 则用于那些可能由外部应用或其他组件处理的操作,无需直接指定目标。它更像一种向系统提需求的工作方式,这让 Android 生态更具灵活性,也让应用之间能无缝协作。

进阶:什么是 IntentFilter

Android 中的 IntentFilter 用于定义应用组件如何响应特定的 Intent,比如打开一个链接或处理一条广播。它本质上是一种过滤器,用来声明某个 ActivityServiceBroadcastReceiver 能够处理哪些类型的 Intent,这些声明写在 AndroidManifest.xml 文件中。

每个 IntentFilter 可以包含 ActionCategory 和数据类型,以便精准匹配传入的 Intent。合理地定义 IntentFilter,能让你的应用与其他应用及系统组件无缝协作,从而提升整体功能体验。

当发送一个隐式 Intent 时,Android 系统会根据该 Intent 的属性,与已安装应用的清单文件中声明的 IntentFilter 进行匹配,从而确定应启动哪个组件。如果找到匹配项,系统就会启动对应的组件,并将 Intent 对象传递给它。如果多个组件都能匹配该 Intent,系统会弹出一个选择器对话框,让用户自行决定使用哪个应用来处理该操作。

PendingIntent 又是什么

PendingIntent 是一种特殊的 Intent,它授权其他应用或系统组件在未来某个时刻,以你应用的名义执行一个预先定义好的 Intent。这种机制特别适用于那些需要在你的应用生命周期之外触发的操作,比如发送通知或与后台服务交互。

面试问题

什么是 PendingIntent?它和普通 Intent 有什么区别?

能否举一个必须使用 PendingIntent 的场景?

核心特性

PendingIntent 本质上是对普通 Intent 的一层封装,能让这个 Intent 在你的应用生命周期结束后依然有效。它把执行 Intent 的权限委托给其他应用或系统服务,并且这些组件会以你应用的身份和权限来执行操作。PendingIntent 可用于 ActivityServiceBroadcastReceiver

它主要有三种形式:

  • Activity:启动一个 Activity。
  • Service:启动一个 Service。
  • Broadcast:发送一条广播。

你可以通过 PendingIntent.getActivity()PendingIntent.getService()PendingIntent.getBroadcast() 等工厂方法来创建对应的 PendingIntent

Kotlin 复制代码
// 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 只能使用一次。

使用场景

  1. 通知(Notifications) :用户点击通知时,可触发打开某个 Activity 等操作。
  2. 闹钟(Alarms) :配合 AlarmManager 调度定时任务。
  3. 服务(Services) :将任务委托给 ForegroundServiceBroadcastReceiver,用于执行后台操作。

安全事项

始终为 PendingIntent 设置 FLAG_IMMUTABLE,以防止恶意应用篡改其内部的 Intent。这一点从 Android 12(API 级别 31)开始尤为重要------在某些场景下,系统已强制要求必须使用该标志。

总结

PendingIntent 是 Android 中的一项核心机制,即使你的应用未在前台运行,也能实现与系统组件或其他应用之间的无缝通信。通过合理管理标志(flags)和权限,你可以确保延迟任务的安全、高效执行。

Serializable 和 Parcelable 有什么区别

在 Android 中,SerializableParcelable 都是用来在不同组件(如 ActivityFragment)之间传递数据的机制,但它们在性能和实现方式上有明显区别。

面试问题

在 Android 中,SerializableParcelable 有哪些关键区别?为什么在组件间传递数据时通常更推荐使用 Parcelable

Serializable

Java 标准接口Serializable 是 Java 提供的标准接口,用于将对象转换为字节流,以便在 Activity 之间传递或写入磁盘。

基于反射:它依赖 Java 反射机制,在运行时动态检查类及其字段来完成序列化。

性能 :相比 ParcelableSerializable 更慢,因为反射本身开销较大。此外,序列化过程中会产生大量临时对象,增加内存负担。

适用场景 :当性能不是关键因素,或在非 Android 特有的代码库中使用时,Serializable 是一个可行的选择。

Parcelable

Android 专属接口Parcelable 是 Android 特有的接口,专为在 Android 组件间实现高性能的进程间通信(IPC)而设计。

性能ParcelableSerializable 更快,因为它针对 Android 做了优化,不依赖反射机制,并且通过避免创建大量临时对象来减少垃圾回收的压力。

适用场景 :在 Android 中传递数据时,尤其是涉及 IPC 或在 Activity、Service 之间传输数据且对性能有要求的场景下,推荐使用 Parcelable

在现代 Android 开发中,kotlin-parcelize 插件通过自动生成实现代码,大幅简化了创建 Parcelable 对象的过程。相比早期需要手动编写的方式,这种方法更加高效。只需在类上添加 @Parcelize 注解,插件便会自动生成所需的 Parcelable 实现。下面是一个示例,展示其工作方式:

Kotlin 复制代码
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() 进行手动序列化。

关键区别

特性 Serializable Parcelable
类型 标准Java接口 Android专用接口
性能 较慢,使用反射 更快,针对Android优化
垃圾创建 产生更多垃圾(更多对象) 产生较少垃圾(高效)
使用场景 适用于通用Java使用 推荐用于Android,尤其是IPC

总结

一般来说,在 Android 应用中,优先选择 Parcelable,因为它在大多数使用场景下性能更优。

  • 在简单场景、非性能敏感的操作,或处理与 Android 无关的通用 Java 代码时,可以使用 Serializable
  • 而在涉及 Android 特有组件且对性能有要求的场景下(例如进程间通信 IPC),应优先使用 Parcelable,因为它针对 Android 的 IPC 机制做了高度优化,效率高得多。

进阶:Parcel 与 Parcelable

Parcel 是 Android 中的一个容器类,用于在应用的不同组件(如 ActivityServiceBroadcastReceiver)之间实现高性能的进程间通信。它的核心作用是对数据进行序列化(marshaling,即"扁平化")和反序列化(unmarshaling,即"还原"),以便跨越 Android 的 IPC 边界传递。

Parcel 不仅可以传输扁平化的数据,还能携带指向活跃 IBinder 对象的引用,通过 IPC 机制进行传递。它专为高性能 IPC 传输而设计,配合 Parcelable 接口,能让对象高效地序列化并在组件间流转。

需要注意的是,Parcel 并非通用的序列化工具,也不应用于持久化存储------因为其底层实现可能随系统版本变化,导致旧数据无法读取。

Parcel 提供了丰富的 API,支持读写基本数据类型、数组以及 Parcelable 对象,使对象能够自行序列化并在需要时重建。此外,它还提供了一些优化方法:在处理 Parcelable 对象时,可省略写入类信息,但要求接收方提前知道数据类型,从而提升传输效率。

Parcelable 是 Android 特有的接口,用于将对象序列化以便通过 Parcel 传递。实现了 Parcelable 的对象可以被写入 Parcel,并从中恢复,因此非常适合在 Android 组件之间传递复杂数据。

相关推荐
前端小王呀1 小时前
自定义图表相关配置
android·前端·javascript
zh_xuan1 小时前
本人遇过的常见安卓面试题(持续更新)
android·面试题
2501_915918411 小时前
uniapp iOS 打包和上架流程,一次跨端项目的工程化交付记录
android·ios·小程序·https·uni-app·iphone·webview
2501_916008891 小时前
HTTPS 双向认证抓包指南,TLS 握手分析、mTLS 排查方法与多工具协同方案
android·网络协议·http·小程序·https·uni-app·iphone
天庭鸡腿哥1 小时前
输入鸡和马,解suo至尊版!
android·windows·visual studio·everything
豆豆豆大王1 小时前
Android 初学者入门:Fragment 与 ListView 基础概念与使用方法
android
Jomurphys1 小时前
设计模式 - 责任链模式 Chain of Responsibility Pattern
android·设计模式·责任链模式
暗碳2 小时前
安卓usb摄像头/采集卡专用软件
android
李少兄2 小时前
在 Linux 中精准查找名为 `xxx` 的文件或目录路径
android·linux·adb