# Android 原生面试题精选
共 250 道,覆盖语言基础、系统原理、
UI、并发、架构、性能、安全、测试与发布。每题都有代码示例、30 秒速记和两组追问。
# 1 Java 与 JVM 基础
# 1. 泛型擦除与协变边界 的核心机制是什么,工程中应该如何使用?
⚡ 30 秒速记
- 核心结论:先确定 泛型擦除与协变边界 的状态所有者、调用线程和生命周期,再讨论
API。 - 运行机制:从
Java与JVM基础 的入口追踪输入、调度、状态变化和最终可观察结果。 - 工程验证:用最小代码、正常/失败测试和性能工具共同验证,不凭经验猜测。
- 失败边界:重点检查并发竞态、对象销毁、系统回收、版本差异和资源上限。
泛型擦除与协变边界在工程中不能脱离真实运行环境使用,关键是明确输入类型、状态归属、执行上下文和生命周期。 Android 会把声明式或命令式代码转换成系统可调度的工作,一旦跨越语言、线程或进程边界,就要处理序列化、排队和错误传播。实际使用时,公共输入要校验,长期任务要支持取消,UI 更新要回到正确上下文。还应通过正常与失败测试确认页面离开、任务取消或系统回收后的清理行为。
讨论 泛型擦除与协变边界 时,不能只背类名或回调顺序。首先要确认对象由应用、框架还是操作系统创建,状态保存在哪里,代码在哪个线程或执行器运行,以及进程被回收、页面离开或任务取消后谁负责清理。只有这四个问题明确,API 的使用才不会脱离真实运行环境。
Android 的运行时会把声明式或命令式代码转换为系统可调度的工作。Java 与 JVM 基础 中的更新可能跨越语言、线程、进程、渲染或持久化边界;每越过一次边界,都要考虑序列化、排队、取消、错误传播和资源所有权。面试时应把正常路径画完整,再主动补一个系统回收、非法输入、超时或高负载分支。
下面的最小示例展示与“泛型擦除与协变边界 的核心机制是什么,工程中应该如何使用?”同类的生产代码骨架。重点观察输入类型、状态封装和释放位置;示例输出应通过测试断言或平台分析工具确认。
// 泛型擦除与协变边界 的核心机制是什么,工程中应该如何使用?
class UserViewModel(private val repo: UserRepository) : ViewModel() {
val state = flow { emit(repo.load()) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), UiState.Loading)
}
代码示例刻意省略了产品 UI 和后端细节,但保留了关键不变量:公共输入必须校验,长期任务必须可取消,UI 更新必须回到正确执行上下文,订阅、线程、文件和系统句柄必须随所有者释放。生产实现还要补充结构化错误、日志关联 ID、系统版本分支及正常/失败测试。
💬 面试官追问
-
数据层把
Flow<UiState<User>>强转成更宽泛的类型交给三个页面复用,编译通过后某个页面运行时才暴露非法数据;你如何判断这不是单纯的页面判空问题?编译通过不能替代跨边界的输入契约,尤其当类型信息、序列化结果或业务状态在运行时才确定。应在公共入口校验实际输入,并保持状态封装和结构化错误,不让页面靠强转兜底;否则错误会从数据层延迟到订阅端,定位成本更高。
-
在
UserViewModel中,仓库返回多种用户子类型,UI团队要求一个只读协变接口,数据团队还要保留写入能力;你会怎样拆分边界?读取与写入应拆成不同契约,只读消费者接收受控的状态流,写入能力留在明确的所有者内部。
ViewModel负责封装状态并使用viewModelScope管理任务,不应把可变容器直接暴露给页面;接口增多会提高建模成本,但能减少不安全强转和越权修改。 -
同一套泛型状态从应用内调用扩展到跨进程传递,原有类型参数在本地足够表达约束;现在还需要补哪些运行时防线?
跨进程后应把序列化格式、类型标识、非法输入和版本不兼容视为显式失败边界。接收端必须验证数据后再构造领域状态,并记录关联
ID以串起发送、传输和恢复;仅依赖编译期泛型无法覆盖进程外数据,额外校验会增加代码和转换成本。 -
线上仅在页面快速重建时出现一次类型转换异常,同时旧的
Flow仍在发射数据;你会怎样区分类型边界错误与生命周期竞态?先记录订阅实例、请求
ID、实际输入类型、页面销毁和每次状态发射,确认异常值来自当前任务还是已失效任务。再分别注入非法输入、任务取消和页面重建进行复现;如果取消后异常消失,生命周期竞态更可疑,但仍需保留入口类型校验。 -
代码评审中,一方主张用宽泛泛型和运行时分派减少接口数量,另一方要求为每类状态建立封闭模型;你会用什么标准做选择?
边界稳定、输入可完全校验且错误可结构化传播时,宽泛接口可以降低重复;状态分支复杂或跨越序列化边界时,显式模型更易测试和恢复。选择应以所有权、失败路径和调用方能力为依据,过度抽象会隐藏非法状态,过度拆分则增加维护成本。