Android 之 面试八股文
1.Activity生命周期
问题:描述Activity从启动到销毁的完整生命周期方法,并说明
onSaveInstanceState()
的调用时机。参考答案:
onCreate()
→onStart()
→onResume()
(活跃状态) →onPause()
(失去焦点) →onStop()
(完全不可见) →onDestroy()
。onSaveInstanceState()
:在Activity可能被销毁前调用(如屏幕旋转),用于保存临时数据到Bundle。
考察点:生命周期顺序、状态恢复、资源释放时机。
2.Fragment通信方式
问题:列举3种Fragment间通信方式,并对比优缺点。
参考答案:
ViewModel共享:通过同一Activity作用域的ViewModel共享数据(推荐,解耦性强)。
Fragment Result API:使用
setFragmentResult()
传递Bundle(无需直接引用)。接口回调:Fragment定义接口,Activity实现并转发(代码冗余,适合简单场景)。
3.View绘制流程
问题:解释View的
onMeasure()
,onLayout()
,onDraw()
的作用,并说明如何优化自定义View的性能。参考答案:
onMeasure()
:计算View尺寸(需处理MeasureSpec
模式)。onLayout()
:确定子View位置。onDraw()
:绘制内容(避免在此创建对象或耗时操作)。优化:使用
ViewStub
延迟加载、减少布局层级、启用硬件加速。
4.事件分发机制
问题:描述触摸事件从Activity到View的分发流程,并解决嵌套滑动冲突(如ScrollView内嵌RecyclerView)。
参考答案:
流程:
Activity.dispatchTouchEvent()
→ViewGroup.onInterceptTouchEvent()
→View.onTouchEvent()
。冲突解决:重写父容器的
onInterceptTouchEvent()
,根据滑动方向决定是否拦截(如垂直滑动时父容器不拦截)。
5.Handler机制
问题:解释Handler、Looper、MessageQueue的关系,并说明如何避免内存泄漏。
参考答案:
关系:Looper轮询MessageQueue,Handler发送和处理Message。
防泄漏:使用静态内部类 + WeakReference持有Context,或在
onDestroy()
中调用handler.removeCallbacks()
。
Kotlin协程优势
问题:对比协程与RxJava的适用场景,并说明协程如何简化网络请求。
参考答案:
协程:轻量级线程、结构化并发(适合顺序逻辑,如链式请求)。
RxJava:复杂数据流处理(如合并多个API响应)。
示例:
viewModelScope.launch { val data = withContext(Dispatchers.IO) { api.fetchData() }updateUI(data)
}
6.内存泄漏场景与解决
问题:列举3个常见内存泄漏场景(如静态Context),并提供解决方案。
参考答案:
静态Context → 改用
ApplicationContext
或弱引用。未注销广播 → 在
onDestroy()
中调用unregisterReceiver()
。匿名内部类持有外部引用 → 静态内部类 + 弱引用。
7冷启动优化
问题:应用冷启动耗时超过1秒,如何定位和优化?
参考答案:
定位工具:Android Studio Profiler的启动时间分析。
优化方案:
延迟初始化第三方库(使用
App Startup
)。减少主线程任务(用线程池处理IO操作)。
使用SplashScreen API避免白屏。
8.MVVM与MVI对比
问题:解释MVVM和MVI架构的区别,并说明MVI如何解决状态管理问题。
参考答案:
MVVM:数据驱动UI(LiveData + ViewModel),但状态分散。
MVI:单向数据流(ViewState → 事件 → 更新状态),状态集中管理(如用Kotlin Flow实现)。
模块化方案
问题:如何将单模块App拆分为模块化架构?需考虑哪些问题?
参考答案:
步骤:按功能划分模块(登录、支付等),基础库抽离为独立Module。
关键点:
依赖注入解耦(使用Hilt)。
路由框架(如ARouter)解决跨模块跳转。
Gradle配置按需编译。
9.Binder机制
问题:解释Binder在IPC中的一次拷贝原理,并说明其与Socket通信的性能差异。
参考答案:
原理:通过
mmap
内存映射共享内核空间,减少数据拷贝次数。性能:Binder传输速度比Socket快3倍,适合高频调用(如系统服务)。
10.Compose优势
问题:对比Compose与XML布局的渲染性能差异,并说明声明式UI的核心思想。
参考答案:
性能:Compose跳过冗余measure/layout阶段,减少嵌套层级。
声明式:UI = f(State)(状态变化自动触发重组,无需手动更新View)。
11.ANR产生原因
主线程耗时操作
I/O阻塞:主线程执行文件读写、数据库操作或网络请求(Android 4.0+主线程网络请求直接抛异常)。
复杂计算:大数据处理、图像渲染等占用CPU时间过长。
锁竞争:主线程等待子线程释放锁(如死锁或同步锁阻塞)。
组件超时
BroadcastReceiver:
onReceive()
未在限定时间内完成(前台10秒/后台60秒)。Service:生命周期方法(如
onCreate()
)超时(前台20秒/后台200秒)。Input事件:按键或触摸事件5秒内未响应。
系统资源瓶颈
内存不足:频繁GC或内存泄漏拖慢主线程。
CPU抢占:高负载时主线程无法获取CPU资源。
Binder调用阻塞:跨进程通信超时或系统服务响应延迟
12.ANR产生原因解决方案与优化策略
异步处理耗时任务
组件优化
BroadcastReceiver:使用
goAsync()
+ 子线程处理耗时逻辑Service:改用``或
JobIntentService
异步执行任务。
资源与锁管理
减少锁竞争:避免嵌套锁,使用
ReentrantLock
替代synchronized
。数据库优化:批量操作使用事务,减少磁盘I/O
UI与内存优化
布局扁平化:用
ConstraintLayout
减少层级,避免过度绘制。内存缓存:
LruCache
缓存图片等资源,减少重复加载
13.OOM产生的主要原因
内存泄漏(核心原因)
静态引用持有短生命周期对象:静态变量(如单例)持有
Activity
或View
的强引用,导致其无法被回收。未注销监听器/广播:如未在
onDestroy()
中调用unregisterReceiver()
或移除Handler
消息。资源未关闭:
Cursor
、InputStream
、Bitmap
未调用recycle()
或关闭流。
大对象分配与资源处理不当
Bitmap加载过大:高分辨率图片未经压缩直接加载,占用内存远超控件显示需求。
缓存策略不合理:
LruCache
或内存缓存设置过大,或未及时清理失效数据。
频繁创建对象与内存碎片
临时对象泛滥:循环中频繁创建对象(如字符串拼接),引发频繁GC导致卡顿甚至OOM。
栈内存溢出:深度递归或局部变量过多导致栈空间耗尽(默认线程栈仅1MB左右)。
系统限制与进程模型
内存配额限制:每个应用进程有固定上限(中低端设备32-64MB,高端设备192MB+),可通过
ActivityManager.getMemoryClass()
获取。Native内存与Java堆分离:Bitmap通过Native分配内存,占用C层空间,但受总进程内存限制
14.OOM解决办法
根治内存泄漏
场景 | 解决方案 |
---|---|
静态引用Activity | 改用 |
Handler/Runnable泄漏 | 使用静态内部类 + |
单例模式 | 初始化时传入 |
资源未关闭 | 在 |
2. Bitmap优化(核心场景)
采样压缩:通过BitmapFactory.Options.inSampleSize
按需缩放图片
编码格式优化:使用
Bitmap.Config.RGB_565
(占内存为ARGB_8888
的一半)及时回收:非显示状态的Bitmap主动调用
recycle()
并置null
。三级缓存策略:内存缓存(
LruCache
) + 磁盘缓存(DiskLruCache
) + 网络加载,避免重复解码
内存管理与编码规范
对象复用:
ListView/RecyclerView
使用ViewHolder
+convertView
复用视图。避免在
onDraw()
等高频方法中创建对象,减少内存抖动。
数据结构优化:
递归算法改迭代,避免栈溢出。
大对象分块加载(如分页读取文件)。
引用类型选择:
缓存使用
WeakHashMap
或SoftReference
,允许GC在内存紧张时回收对象
15. Android 测量一个imageview的大小,在什么生命周期能测量到?
在 Activity
的 onCreate()
、onStart()
或 onResume()
中直接调用 imageView.getWidth()
或 imageView.getHeight()
通常返回 0,原因如下:
视图未完成布局:此时视图树仅完成初始化,但尚未经过测量(Measure)和布局(Layout)阶段。
布局流程未结束:Android 的视图布局分为三个阶段:
Measure:计算视图大小(
onMeasure()
)。Layout:确定视图位置(
onLayout()
)。Draw:绘制视图内容(
onDraw()
)。只有完成 Layout 阶段后,尺寸才被确定
可获取尺寸的生命周期及方法
onWindowFocusChanged()
:窗口焦点变化时,当 Activity窗口获得焦点(用户可见且可交互)时,视图已完成布局,此时可直接获取尺寸
16.Android jectpack有哪些组件,有哪些作用?
一、Jetpack 的定义与价值
定位:Jetpack 是 Google 推出的标准化开发组件集合,旨在解决四大痛点:
✅ 加速开发:减少样板代码(如数据库操作、生命周期处理)
✅ 兼容性保障:自动适配不同 Android 版本(如
AppCompat
实现 Material Design 兼容)✅ 架构规范:强制推行 MVVM 等现代化架构,提升可维护性
✅ 性能优化:通过生命周期感知机制避免内存泄漏
💡 面试点睛:可类比为“Android 开发的瑞士军刀”,整合了 iOS 的 CocoaPods + UIKit 功能
。
📦 二、核心组件分类与作用
⚙️ 1. 架构组件(Architecture)
组件 | 核心作用 | 典型场景举例 |
---|---|---|
ViewModel | 管理 UI 相关数据,独立于 Activity/Fragment 生命周期(屏幕旋转不丢失数据) | 保存页面状态(如用户输入表单) |
LiveData | 生命周期感知的数据观察者,自动更新 UI 并避免内存泄漏 | 实时展示数据库查询结果 |
Room | SQLite 的 ORM 抽象层,编译时校验 SQL 语句 | 本地缓存用户数据( |
Navigation | 管理 Fragment 导航栈,可视化配置页面跳转关系 | 实现单 Activity 多 Fragment 架构 |
WorkManager | 可靠的后台任务调度(支持设备重启后继续执行) | 定期同步服务器数据 |
🎨 2. UI 组件
组件 | 核心作用 |
---|---|
Compose | 声明式 UI 工具包(未来趋势),实时预览布局 |
RecyclerView | 高效加载大数据集列表(支持差异化更新) |
ConstraintLayout | 扁平化布局减少嵌套,优化渲染性能 |
Paging | 分页加载大数据集(如聊天记录、商品列表) |
⚡️ 3. 行为组件(Behavior)
组件 | 核心作用 |
---|---|
CameraX | 标准化相机开发(兼容不同设备) |
DownloadManager | 系统级文件下载管理(断点续传、通知栏进度) |
Permissions | 简化运行时权限请求流程 |
🧱 4. 基础组件(Foundation)
组件 | 核心作用 |
---|---|
AppCompat | 向后兼容 Material Design 控件 |
Android KTX | Kotlin 扩展函数库(如简化 |
DataStore | 替代 |
⚖️ 三、Jetpack 与传统开发对比
维度 | 传统开发 | Jetpack 开发 |
---|---|---|
代码量 | 高(需手动处理生命周期) | 减少 30%-50% 样板代码 1 |
内存泄漏风险 | 高(需谨慎管理引用) | 低(LiveData/ViewModel 自动清理) |
测试便利性 | 需大量 Mock 环境 | 内置 |
附:面试准备建议
基础巩固:
必刷:Activity/Fragment生命周期、四大组件通信、Handler机制。
进阶重点:
深入View绘制/事件分发、内存优化、协程原理。
架构设计:
准备1-2个模块化/MVVM项目案例,说明技术选型原因。
工具使用:
熟练使用Profiler、LeakCanary、Systrace定位问题