Android 面试题大全(详细完整版)
本文档涵盖 Java 基础、Kotlin、Android 四大组件、UI、多线程、网络、存储、性能优化、架构、Jetpack、开源框架、设计模式、算法、系统原理等方向,每题附详细答案。
目录
Java 基础
Kotlin
Android 四大组件
UI 与 View 体系
多线程与异步
网络编程
数据存储
性能优化
架构设计
Jetpack 组件
开源框架源码
设计模式
数据结构与算法
Android 系统原理
Android 11/12/13/14 新特性
项目与场景题
一、Java 基础
- Java 中 == 和 equals() 的区别?
== 比较的是引用地址(基本类型比较值,引用类型比较内存地址)。
equals() 默认(Object 中)也是比较地址,但被 String、Integer 等重写后比较的是内容。
java
String a = new String("abc");
String b = "abc";
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
- String、StringBuilder、StringBuffer 的区别?
特性 String StringBuilder StringBuffer
可变性 不可变 可变 可变
线程安全 安全(不可变) 不安全 安全(synchronized)
性能 低(拼接产生新对象) 高 中
应用场景 少量字符串 单线程拼接 多线程拼接
- HashMap 原理?JDK 1.8 有哪些优化?
底层:数组 + 链表(拉链法)。
JDK 1.8:链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树;≤ 6 时退化链表。
默认初始容量 16,负载因子 0.75,扩容翻倍。
key 为 null 时存在 index 0。
扩容时 1.8 不会重新 hash,而是用 (e.hash & oldCap) == 0 判断位置。
- HashMap 和 HashTable、ConcurrentHashMap 的区别?
对比 HashMap HashTable ConcurrentHashMap
线程安全 否 是 是
null key 允许 1 个 不允许 不允许
性能 高 低(全锁) 高(分段锁/CAS)
继承 AbstractMap Dictionary AbstractMap
JDK 1.8 的 ConcurrentHashMap 改为 CAS + synchronized 锁单个桶节点。
- ArrayList 和 LinkedList 区别?
ArrayList:数组实现,查询 O(1),增删平均 O(n),尾部增删 O(1) 均摊。
LinkedList:双向链表,查询 O(n),增删 O(1)(已知节点)。
ArrayList 扩容 1.5 倍。
- Java 异常体系?
Throwable
├── Error(不可恢复:OOM、StackOverflow)
└── Exception
├── 受检异常(IOException、SQLException)
└── RuntimeException(非受检:NPE、ClassCastException)
- try-catch-finally 中 finally 一定执行吗?
不一定:
在 try 之前就 return 或 System.exit()。
守护线程被强制终止。
JVM 崩溃。
如果 try 中有 return,finally 会在 return 之前执行,且 finally 中的 return 会覆盖 try 的 return。
- Java 中的四种引用类型?
强引用(StrongReference):普通赋值,GC 不回收。
软引用(SoftReference):内存不足时回收,适合做缓存。
弱引用(WeakReference):下一次 GC 必回收,用于 WeakHashMap、Handler 修复内存泄漏。
虚引用(PhantomReference):跟踪对象被回收,配合 ReferenceQueue。
- Java 泛型的类型擦除?
Java 泛型只在编译期检查类型,编译后擦除为原始类型(Object 或上界)。运行时无法获取泛型类型,所以 new T() 是非法的。可通过反射获取父类 ParameterizedType 拿到真实泛型。
- Java 反射的原理与性能?
反射通过 Class 对象在运行期获取字段、方法、构造器并调用。
原理:JVM 为每个加载的类生成 Class,反射调用走 Method.invoke,存在 MethodAccessor 优化(前 15 次 NativeMethodAccessorImpl,之后生成字节码)。
性能:比直接调用慢数倍,可通过缓存 Method 对象、setAccessible(true) 优化。
- Java 类加载机制(双亲委派模型)?
BootstrapClassLoader → ExtClassLoader/PlatformClassLoader → AppClassLoader → 自定义 ClassLoader
流程:先委派父加载器加载,父加载不了再自己加载。
作用:避免重复加载、保证核心类安全(防止用户覆盖 String)。
打破方式:重写 loadClass(Tomcat、SPI、热修复)。
- JVM 内存区域划分?
线程私有:程序计数器、虚拟机栈、本地方法栈。
线程共享:堆(新生代 Eden+S0+S1、老年代)、方法区/元空间。
JDK 1.8 后永久代被元空间(Metaspace,使用本地内存)取代。
- GC 算法有哪些?
标记-清除:产生碎片。
复制:新生代用,无碎片但浪费空间。
标记-整理:老年代用,无碎片但慢。
分代收集:新生代复制,老年代标记整理。
判断对象死亡:可达性分析(GC Roots)。
- volatile 关键字的作用?
保证可见性:强制从主内存读,写后立即刷回主内存。
禁止指令重排序:通过内存屏障。
不保证原子性(如 i++ 仍需 synchronized/AtomicInteger)。
- synchronized 的原理与锁升级?
原理:基于 Monitor(对象头 MarkWord 指向 Monitor)。
锁升级:无锁 → 偏向锁 → 轻量级锁(自旋)→ 重量级锁。
JDK 1.6 后引入偏向锁,JDK 15 已废弃偏向锁。
- synchronized 和 Lock 的区别?
项 synchronized Lock(ReentrantLock)
释放 自动 手动 unlock
响应中断 否 是
公平锁 否 可选
条件变量 单一 wait/notify 多 Condition
锁状态 不可查 可查/可中断
二、Kotlin
- Kotlin 与 Java 相比的优势?
简洁(data class、空安全、类型推断、扩展函数)。
互操作性好(与 Java 100% 兼容)。
协程替代回调,简化异步。
顶层函数、属性、默认参数。
- Kotlin 空安全机制?
var a: String = "x" 非空,var b: String? = null 可空。
访问可空类型需 ?.、!!、?:。
编译期检查,运行时仍可能 NPE(Java 互操作、!!、初始化未完成)。
- Kotlin 扩展函数原理?
扩展函数在编译期生成静态方法,接收者作为第一个参数传入,本质是语法糖,不会修改原类。所以:
不能被覆写(无动态分派)。
与成员函数同名时,成员函数优先。
- data class 与普通 class 区别?
自动生成 getter/setter、equals、hashCode、toString、copy、componentN(解构)。
限制:主构造器至少 1 参数、可继承、不能被 abstract/open/sealed/inner。
- Kotlin 伴生对象(companion object)?
Kotlin
class Foo {
companion object {
const val TAG = "Foo"
fun bar() {}
}
}
伴生对象是单例的,类似 Java 静态。
在字节码层面生成内部 Companion 类,可通过 @JvmStatic 生成真正静态方法。
- lateinit 与 lazy 的区别?
项 lateinit var by lazy
类型 var val
初始化 后续赋值 第一次访问时初始化
线程安全 否 默认 synchronized
可空 不可空类型 任意
检查 ::x.isInitialized 不可
- Kotlin 协程为什么轻量?
协程基于线程池(Dispatchers)+ 挂起函数,挂起时不阻塞线程而是把状态保存到 Continuation,由调度器恢复,单线程可跑数万协程。协程并非真正的"系统线程",而是用户态调度任务。
- suspend 函数原理?
编译期 CPS 变换:suspend fun foo() → fun foo(continuation: Continuation)。挂起时调用 suspendCoroutine 保存状态,恢复时 Continuation.resumeWith 调用回填栈。
- Kotlin 密封类(sealed class)?
限制子类必须在同一文件/同一模块。
用于 when 表达式可穷举分支,新增子类编译器报错未覆盖。
常用于状态机、网络结果封装。
- Kotlin 作用域函数(let/run/with/apply/also)?
函数 接收者 返回值 常用
let it lambda 结果 null 检查
run this lambda 结果 计算返回
with this lambda 结果 调用多个方法
apply this 对象本身 初始化
also it 对象本身 链式副作用
- Kotlin 集合中 List 和 MutableList 区别?
List<out T> 只读视图(协变),MutableList<T> 可变。
listOf 返回只读 List,mutableListOf 返回可变。
Java 集合互操作时需注意只读 List 在 Java 端仍可变。
三、Android 四大组件
- Activity 的生命周期?
onCreate → onStart → onResume → onPause → onStop → onDestroy
异常重建:onSaveInstanceState( onStop 之前)→ 重建 onRestoreInstanceState(onStart 之后)。
弹出透明 Activity:旧 Activity 走到 onPause 不走 onStop。
- Activity 四种启动模式?
模式 说明
standard 每次新建实例
singleTop 栈顶复用,onNewIntent
singleTask 单实例任务栈,清掉其上 Activity
singleInstance 独立任务栈,全局唯一
- Activity 启动流程?
ContextImpl.startActivity → Instrumentation.execStartActivity → AMS.startActivity → 校验权限、创建 ActivityRecord、Pause 当前 Activity → ApplicationThread.scheduleTransaction → ActivityThread.H → LaunchActivityItem → performLaunchActivity(创建 Activity、调用 onCreate)。
- Activity 与 Fragment 生命周期关系?
Activity.onCreate → Fragment.onAttach → onCreate → onCreateView → onViewCreated → onActivityCreated → onStart → onResume
Fragment 多出:onAttach、onCreateView、onViewCreated、onDestroyView、onDetach。
v1.3 后 onActivityCreated 已废弃,建议用 ViewTreeLifecycleOwner。
- Service 的两种启动方式?
startService:独立运行,需 stopSelf/stopService 停止,onCreate→onStartCommand→onDestroy。
bindService:与绑定者共存亡,onCreate→onBind→onUnbind→onDestroy,可通信。
混合启动:典型音乐播放器,start 启动后 bind 控制界面。
- BroadcastReceiver 有序广播与普通广播?
普通广播:异步并发派发,无顺序。
有序广播:按优先级派发,可拦截、传递数据,abortBroadcast 终止。
注册方式:静态注册(AndroidManifest,8.0 后受限)与动态注册(registerReceiver,需在合适的时机解注册)。
- ContentProvider 初始化时机?
ContentProvider 在 Application.onCreate 之前被创建(在 attachBaseContext 之后),所以不能在 ContentProvider 中依赖 Application 初始化的组件。ContentProvider 的 onCreate 在 Application 的 onCreate 之前调用,
启动顺序:Application 构造 → attachBaseContext → installContentProviders → Application.onCreate。
- Intent 显式与隐式区别?
显式:指定 ComponentName,跳转目标明确。
隐式:通过 action/category/data 匹配 IntentFilter,常用跨应用启动。
隐式启动 Service 在 5.0 后被禁止(必须显式)。
- Activity、Window、View 的关系?
Activity 持有 PhoneWindow(Window 抽象)。
PhoneWindow 持有 DecorView(顶层 View)。
DecorView 包含 SystemView(状态栏导航栏)和 ContentView(setContentView 实际设置的位置)。
WindowManager 添加 DecorView 到屏幕(WindowManagerImpl → WindowManagerGlobal → ViewRootImpl)。
- Application 启动流程?
ActivityThread.main → 创建 ActivityThread → attach → 创建 Application → 调用 attachBaseContext → onCreate。Application 的 attachBaseContext 在 ContentProvider 之前,onCreate 在所有 ContentProvider 之后。
四、UI 与 View 体系
- View 的绘制流程?
三大过程:measure、layout、draw。
measure:确定 View 宽高,根 View 的 MeasureSpec 由窗口尺寸决定。
layout:确定 View 在父容器中位置。
draw:绘制背景、内容、子 View、装饰(滚动条等)。
入口:ViewRootImpl.performTraversals → performMeasure → performLayout → performDraw。
- MeasureSpec 三种模式?
模式 含义 对应
EXACTLY 确定大小 match_parent、具体值
AT_MOST 最大值 wrap_content
UNSPECIFIED 不限制 系统内部(ScrollView)
- 事件分发机制?
三个方法:
dispatchTouchEvent:分发事件。
onInterceptTouchEvent:拦截(ViewGroup 才有)。
onTouchEvent:消费事件。
流程:Activity → ViewGroup → View。返回 true 表示消费,super 走默认。
ACTION_DOWN 决定消费者,后续事件默认派给消费者,除非父级拦截。
滑动冲突:父级在 onInterceptTouchEvent 拦截,子级 requestDisallowInterceptTouchEvent 反拦截。
- 滑动冲突解决思路?
外部拦截法:父 View 的 onInterceptTouchEvent 判断何时拦截(推荐)。
内部拦截法:子 View 调用 parent.requestDisallowInterceptTouchEvent(true) 配合父 onInterceptTouchEvent。
- RecyclerView 与 ListView 区别?
RecyclerView 强制 ViewHolder,复用更彻底。
LayoutManager 灵活(线性/网格/瀑布流)。
ItemDecoration 自定义分割线,ItemAnimator 增删动画。
局部刷新 notifyItemChanged、DiffUtil 增量更新。
ListView 的 setEmptyView、header/footer 在 RecyclerView 中需自定义。
- RecyclerView 缓存机制?
四级缓存:
Scrap:mAttachedScrap(屏幕内,不改 position)、mChangedScrap(已变化的)。
Cache:mCachedViews(默认 2,屏幕外,按 position 直接命中)。
ViewCacheExtension:自定义。
RecycledViewPool:按 viewType 持有,多个 RecyclerView 可共享,存无绑定数据的 ViewHolder。
- ConstraintLayout 优势?
减少布局嵌套层级,扁平化提升性能。
约束(Constraint)定义位置关系,Chain 链式约束。
Bias 比例、Guideline 辅助线、Barrier 屏障。
- 自定义 View 步骤?
继承 View/ViewGroup 重写 onMeasure(必要处理 wrap_content)。
onDraw 绘制(Canvas、Paint)。
处理 padding(继承 ViewGroup 还需在 onLayout 处理子 View)。
自定义属性 attrs.xml + obtainStyledAttributes。
内存防止泄漏(Handler、动画在 onDetachedFromWindow 回收)。
- invalidate() 和 requestLayout() 区别?
invalidate:仅触发 onDraw(UI 刷新),不重新测量布局。
requestLayout:触发 measure、layout(不一定 draw),尺寸/位置变化时用。
postInvalidate:可在子线程调用(实际 post 到主线程)。
- WebView 性能优化?
预创建 WebView 池复用。
独立进程(防内存泄漏、OOM)。
DNS 预解析、预加载关键资源。
开启缓存(DOM、H5 APP Cache)。
Service Worker、离线包。
五、多线程与异步
- Android 中多线程方式有哪些?
Thread / Runnable
Handler
AsyncTask(已废弃)
HandlerThread(自带 Looper 的子线程)
IntentService(已废弃,用 JobIntentService/WorkManager)
线程池 Executor
协程 Coroutines
RxJava
- Handler 原理?
四要素:Handler、Message、MessageQueue、Looper。
Looper.prepare() 创建 Looper 并存 ThreadLocal,MessageQueue 随之创建。
Looper.loop() 死循环取 Message,调用 msg.target.dispatchMessage。
Handler.sendMessage 入队,MessageQueue 按时间排序,next() 阻塞取消息(epoll 唤醒)。
子线程 Looper 需 prepare + loop,主线程已在 ActivityThread.main 创建。
- Handler 内存泄漏原因与解决?
原因:非静态内部类 Handler 持有外部 Activity,延迟 Message 持有 Handler,未移除导致 Activity 无法回收。
解决:静态内部类 + 弱引用;onDestroy 移除消息 removeCallbacksAndMessages(null)。
- MessageQueue.next() 如何阻塞?
nativePollOnce 通过 Linux epoll 机制监听 wake 管道文件描述符。无消息或消息未到时间时阻塞;其他线程通过 nativeWake 写管道唤醒。
- IdleHandler 的作用?
在 MessageQueue 空闲时执行的任务,如预加载、日志上报。Looper.myQueue().addIdleHandler,注意要在主线程添加。
- HandlerThread 与 IntentService?
HandlerThread:自带 Looper 的子线程,串行执行任务。
IntentService:继承 Service 内置 HandlerThread,任务执行完自动 stopSelf。Android 11 已废弃,推荐 WorkManager。
- 线程池核心参数与工作流程?
Java
ThreadPoolExecutor(int corePoolSize, int maxPoolSize, long keepAlive,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
ThreadFactory factory, RejectedExecutionHandler handler)
流程:core 满 → 队列满 → 扩到 max → 拒绝策略。
拒绝策略:AbortPolicy 抛异常、DiscardPolicy 丢弃、DiscardOldestPolicy 丢老任务、CallerRunsPolicy 调用者执行。
常见池:FixedThreadPool(nThreads 阻塞 LinkedBlockingQueue 无界)、CachedThreadPool(0~Integer.MAX,SynchronousQueue)、SingleThreadExecutor、ScheduledThreadPool。
- AsyncTask 为什么废弃?
默认串行执行(API 11 后),并行需 executeOnExecutor。
容易内存泄漏、配置变化重建问题、与生命周期不绑定。
推荐协程或 RxJava 替代。
- 协程 Dispatchers 的区别?
Dispatchers.Main:主线程,UI 操作。
Dispatchers.IO:线程池(64 上限),网络/IO。
Dispatchers.Default:CPU 密集(核数)。
Dispatchers.Unconfined:不指定,继承调用方线程。
- 协程 launch 和 async 区别?
launch:启动无返回值协程,返回 Job,fire-and-forget,异常默认向上抛。
async:返回 Deferred<t>,await 拿结果,异常靠 await 抛出。需 await 后才执行 await 抛异常。</t>
- 协程 Structured Concurrency(结构化并发)?
子协程的作用域由父 Job 管理,父取消则子取消,父等所有子完成才结束。避免协程泄漏,比 RxJava 的 dispose 更优雅。
六、网络编程
- HTTP 与 HTTPS 区别?
HTTP 明文,端口 80;HTTPS = HTTP + TLS/SSL,端口 443。
HTTPS 加密通信 + 身份验证(证书)+ 完整性校验,防止中间人。
- HTTPS 握手流程?
Client Hello:客户端发送 TLS 版本、随机数 1、加密套件。
Server Hello:服务端选 TLS 版本、随机数 2、加密套件。
服务端发送证书。
客户端校验证书(CA 签名链)。
客户端生成 pre-master secret,用公钥加密发送。
双方用 随机数1 + 随机数2 + pre-master 生成会话密钥。
后续用对称加密通信。
TLS 1.3 简化为 1 RTT,支持 0-RTT。
- TCP 三次握手与四次挥手?
三次握手:SYN → SYN+ACK → ACK,建立连接。第三步防止已失效的连接请求到达服务端造成资源浪费。
四次挥手:FIN → ACK → FIN → ACK,关闭连接。服务端收到 FIN 后可能还有数据,先 ACK 再单独发 FIN。
- TCP 为什么是可靠传输?
校验和、序列号、确认应答、超时重传、流量控制(滑动窗口)、拥塞控制(慢启动、拥塞避免、快重传、快恢复)。
- OkHttp 原理?
核心类:OkHttpClient、Request、Response、Interceptor 链。
责任链模式:RetryAndFollowUpInterceptor → BridgeInterceptor(加头)→ CacheInterceptor → ConnectInterceptor(连接池)→ CallServerInterceptor(写请求读响应)。
连接池:ConcurrentDeque<realconnection>,5 分钟 + 5 个连接复用策略。</realconnection>
异步:Dispatcher 用线程池,maxRequests=64,maxRequestsPerHost=5。
- Retrofit 原理?
通过动态代理(Proxy.newProxyInstance)拦截接口方法。
解析注解(GET/POST/Path/Query/Body)构造 OkHttp Request。
CallAdapter 转换 Call → RxJava Flowable / 协程 Deferred / LiveData。
ConverterFactory 处理 JSON 序列化(GsonConverterFactory)。
- 网络请求优化?
请求合并、减少请求次数。
gzip 压缩、图片 WebP。
HTTP/2 多路复用、Connection 复用。
DNS 预解析、HttpDNS 防劫持。
弱网策略:超时重试、断点续传、增量更新。
- WebSocket 与 HTTP 长轮询?
WebSocket:全双工,握手升级后保持长连接。
HTTP 长轮询:客户端发请求服务端 hold 直到有数据,开销大。
适合 IM:WebSocket + 心跳保活,离线消息靠推送。
七、数据存储
- SharedPreferences 原理与问题?
原理:xml 文件全量加载到内存(HashMap),commit 同步写回磁盘,apply 异步写回。
问题:apply 可能丢数据、ANR(onPause 等待)、跨进程不安全。
替代:DataStore(Preferences/Kotlin)、MMKV(mmap)。
- MMKV 原理?
基于 mmap,将文件映射到内存,写直接落到内存,由 OS 异步刷盘。
protobuf 序列化,append 写入,增量扩容。
性能:写比 SP 快数倍,多进程安全(文件锁)。
- SQLite 与 Room 区别?
Room 在 SQLite 之上做封装,编译期检查 SQL。
Entity、Dao、Database 三层。
支持 LiveData/Flow 返回观察式数据,自动迁移。
减少样板代码与运行时错误。
- SQLite 优化?
索引(注意索引维护开销)。
事务(BEGIN/COMMIT 批量插入)。
预编译 Statement 复用。
WAL 模式并发读写。
LIMIT 分页、避免 SELECT *。
- GreenDao、Realm、ObjectBox、LitePal 对比?
GreenDao:基于 SQLite 的 ORM,性能好,已较少维护。
Realm:自研引擎,跨平台,性能强但体积大。
ObjectBox:NoSQL,性能高,Kotlin 友好。
LitePal:简单 ORM,已不活跃。
推荐新项目用 Room。
- 文件存储方式?
内部存储(/data/data/包名),私有,无需权限。
外部存储(/sdcard/Android/data/包名),10.0 后沙盒化。
共享存储:MediaStore 访问图片/音频/视频。
SAF(Storage Access Framework)选择文件。
八、性能优化
- 启动优化?
冷启动:进程未创建,从 fork → Application → Activity。
优化:异步初始化(Task 工具/有向无环图调度)、延迟初始化、多进程分担、避免 Application 重逻辑、启动器框架(启动任务图)。
监控:TraceView、Systrace/Perfetto、Debug.startMethodTracing。
阈值:冷启动 < 800ms 较好。
- UI 渲染优化?
减少布局层级(ConstraintLayout、merge、include)。
避免 overdraw(背景透明、移除多余背景)。
ViewStub 懒加载。
列表预加载、DiffUtil 增量刷新。
GPU 渲染分析(Profile GPU Rendering)、Systrace 检查掉帧。
- 内存优化?
避免静态持有 Activity、Handler 泄漏、单例持有 Context 用 Application。
弱引用缓存大对象(Bitmap)。
onLowMemory / onTrimMemory 释放资源。
LeakCanary 检测 Activity/Fragment 泄漏。
内存峰值控制:Bitmap 采样 inSampleSize、Glide 缓存策略。
- 内存泄漏常见场景?
静态变量持有 Activity。
单例持有 Context。
Handler 延迟消息未移除。
内部类(非静态)持有外部。
监听器/广播未注销。
WebView 持有 Activity(用独立进程或 destroy 时移除)。
Cursor/Cursor 未关闭。
- ANR 原因与排查?
原因:主线程耗时操作(5s 输入事件超时、10s 广播、20s 服务)。
排查:adb pull /data/anr/traces.txt,分析主线程堆栈。
监控:FileObserver 监听 anr 目录、Watchdog 线程定时向主线程 post 任务看是否回来。
- 卡顿排查?
每帧需 16ms(60Hz),超出掉帧。
工具:Systrace/Perfetto、Profile GPU Rendering、Choreographer.FrameCallback 监测帧率。
BlockCanary 监测主线程消息耗时。
常见原因:主线程 IO、布局复杂、过度绘制、GC 频繁。
- Bitmap 优化?
inSampleSize 采样降分辨率。
inPreferredConfig(RGB_565 节省一半内存,ARGB_8888 默认)。
Glide/Fresco 等库做内存+磁盘缓存。
及时 recycle(旧版本,新版本由 GC)。
列表加载用 Glide 的 thumbnail、override(w,h)。
- OOM 排查?
通过 LeakCanary 排查泄漏。
Android Profiler 看 Java/Native/Code 堆占用。
MAT 分析 hprof 找支配树(GC Root 到对象路径)。
区分 Java 堆 OOM 与 Native OOM(图库、视频)。
- 包体积优化?
代码混淆 R8/ProGuard。
资源压缩 shrinkResources。
图片 WebP、SVG。
so 库裁剪 abiFilters、动态化插件化。
资源混淆 AndResGuard。
Bundle 动态下发。
- 电量优化?
网络请求合并、使用 GCM/FCM 推送而非轮询。
限制后台定位频率( fused location )。
JobScheduler/WorkManager 替代常驻后台 Service。
WakeLock 用完即释放。
监控:Battery Historian。
- 网络优化?
HttpDNS 防 DNS 劫持。
弱网探测 + 降级策略。
TCP KeepAlive、连接复用。
图片 CDN、WebP、按需尺寸。
离线缓存、预加载。
九、架构设计
- MVC、MVP、MVVM 区别?
项 MVC MVP MVVM
Model 数据 数据 数据
View XML+Activity XML+Activity XML+Activity
Controller/Presenter/ViewModel Activity Presenter 接口 ViewModel
通信 View↔Model 直接 Presenter 反向 View 接口 双向绑定
耦合 高 接口隔离 数据驱动
测试 难 易 易
- MVVM 数据绑定实现?
DataBinding:XML 中绑定 ViewModel 数据,生成 BR。
LiveData:观察者,生命周期感知,自动更新 UI。
StateFlow/SharedFlow:协程冷/热流,配合 collectLatest。
- 单一数据源(SSOT)原则?
状态集中在 ViewModel 持有的 UiState(data class)中,UI 通过不可变流订阅,事件单向流动。避免多处状态不一致。
- MVI 架构?
Model-View-Intent:
Intent:用户意图(密封类)。
reduce(intent):纯函数生成新 State。
View 渲染 State。
单向数据流(UDF)、状态不可变、易测试。
- Clean Architecture 分层?
Presentation(UI/ViewModel)→ Domain(UseCase/Repository 接口)→ Data(Repository 实现、DataSource)。
依赖方向:外层依赖内层,内层不感知外层。
优势:可替换数据源、易测试、跨平台。
- 组件化与插件化区别?
组件化(ARouter/WMMrouter):编译期打包各业务模块为独立 Library,运行期同一进程,跳转解耦。
插件化(VirtualApk/RePlugin/DroidPlugin):未安装 APK 动态加载运行,可热插拔。
区别:组件化是工程组织,插件化是运行时动态加载未安装代码。
- ARouter 路由原理?
编译期 APT 扫描 @Route 注解,生成映射表(按 group 分组)。
运行期首次调用 init 加载映射表(按需分组加载)。
跳转:navigation() 查表 → 包装 Postcard → 调起 Activity / 跨模块 Service。
- 多模块通信方案?
路由:ARouter/WMRouter 跳转 Activity。
服务暴露:接口下沉到 Base,模块提供实现,注册到 ServiceLoader。
事件总线:EventBus/LiveData/Flow 跨模块通知。
数据共享:公共数据模块或 Repository。
十、Jetpack 组件
- Lifecycle 原理?
LifecycleOwner(Activity/Fragment)持 LifecycleRegistry。
LifecycleObserver 通过注解 @OnLifecycleEvent 或 DefaultLifecycleObserver 回调。
FragmentActivity 在 Activity 内部 ReportFragment 派发生命周期事件(兼容旧版)。
- LiveData 为什么能感知生命周期?
LifecycleBoundObserver 包装 Observer 并绑定 LifecycleOwner。
在 onStart 后开始派发,onStop 后暂停,onDestroy 自动 remove。
active 状态(STARTED/RESUMED)才会回调 onChanged,避免后台更新浪费。
- LiveData 粘性事件问题?
LiveData 最新值会分发给新 Observer,导致事件型数据出现"粘性"。解决:
SingleLiveEvent(不严谨)。
EventWrapper(一次性消费标记)。
改用 SharedFlow/Channel 处理事件。
- ViewModel 为什么能保存配置变化数据?
ViewModel 存在 ViewModelStore(Activity 持有)。
配置变化时 ActivityThread 调用 retainNonConfigurationInstances 保存 ViewModelStore,重建后取出。
Activity 销毁(非配置变化)时调用 ViewModel.onCleared。
- Room 编译期做了什么?
APT 处理 @Entity/@Dao/@Database 注解。
生成 XxxDao_Impl,实现增删改查(基于 SupportSQLiteStatement)。
校验 SQL、返回类型(Flow/LiveData)。
- Navigation 组件?
NavGraph(XML 描述目的地)+ NavHostFragment(容器)。
NavController 管理回退栈,safe args 生成类型安全跳转。
适合单 Activity 多 Fragment 架构,集成深层链接。
- WorkManager 的特点?
兼容到 API 23,自动选择 JobScheduler/AlarmManager+BroadcastReceiver。
约束(网络/电量/存储)、可重复/单次、链式任务。
应用退到后台仍能执行,系统重启后可恢复。
适合:上传日志、同步数据、定时刷新。
- DataStore 与 SP 区别?
DataStore 基于 Flow + 协程,异步、避免 ANR。
Preferences DataStore 无类型;Proto DataStore 强类型(protobuf schema)。
不支持全量同步读取,需 suspend/Flow 访问。
- Compose 与 View 的关系?
Compose 是声明式 UI 框架,独立绘制体系,最终走 AndroidComposeView(一个 View)。
@Composable 函数经 Compose 编译器插入 group 操作,记录到 SlotTable。
重组:State 变化触发 Recompose,按粒度更新。
可与 View 互嵌(AndroidView / ComposeView)。
- Hilt 与 Dagger 区别?
Hilt 基于 Dagger,提供标准组件(Application/Activity/Fragment/View/Service)。
简化 @Component 配置,预定义 SingletonComponent/ActivityComponent 等。
@HiltAndroidApp 入口,@AndroidEntryPoint 注入。
ViewModel 通过 @HiltViewModel + @Inject 构造器自动绑定。
十一、开源框架源码
- Glide 缓存机制?
内存缓存:LruResourceCache(活动资源 ActiveResources 强引用正在用的 + LRU 弱引用回收的)。
磁盘缓存:DiskLruCache,分原始数据/转换后。
加载流程:先活动资源 → 内存 → 磁盘 → 网络/资源。
生命周期:通过 SupportRequestManagerFragment 监听,onStop 暂停。
- Glide 三级缓存?
活动资源 ActiveResources(正在显示)。
内存缓存 LRU。
磁盘缓存。 (再后才是网络/文件源)
- OkHttp 拦截器链?
Java
Response response = new RealCall(...).execute();
// 同步入 getResponseWithInterceptorChain
顺序:
RetryAndFollowUpInterceptor:重试、重定向。
BridgeInterceptor:补全请求头、解压 gzip。
CacheInterceptor:磁盘缓存命中判断。
ConnectInterceptor:从连接池获取或新建 RealConnection。
CallServerInterceptor:通过 Okio 写入请求读出响应。
- EventBus 原理与不足?
register:反射扫描订阅方法,按 eventType 收集 Subscriber。
post:根据 eventType 在 toPostThread 找到所有订阅者,按 ThreadMode 切换线程调用。
不足:编译期不检查、运行时性能、难以追踪、内存泄漏风险。
替代:LiveEventBus、Flow、SharedFlow、RxJava。
- RxJava 调度器原理?
subscribeOn 决定上游执行线程,多次调用只有第一个生效。
observeOn 决定下游线程,可多次切换。
内部通过 Scheduler.Worker 调度,本质上也是线程池。
Lift 操作符嵌套调用.onNext 形成洋葱链。
- LeakCanary 原理?
注册 ActivityLifecycleCallbacks,onDestroyed 时用弱引用持有 Activity + 关联 KeyedWeakReference 到 RetainedManager。
触发 GC 后 5 秒检查弱引用是否被回收(检查 ReferenceQueue)。
未回收的对象 dump heap(profiler)。
用 Shark(原 HAHA)解析 hprof,找引用链,输出泄漏。
- Coil 与 Glide 对比?
Coil 基于 Kotlin + 协程,依赖更小,API 友好。
同样三级缓存,Coil 用 OkHttp + 真实协程调度,Glide 用线程池。
Compose 一等公民,Coil 适配更好。
十二、设计模式
- 单例模式与 DCL 懒加载?
Java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 防止指令重排序(new 不是原子操作)。
静态内部类更优雅,懒加载且线程安全。
- 工厂模式与抽象工厂?
简单工厂:一个方法根据参数返回不同实例。
工厂方法:每类产品对应一个工厂子类。
抽象工厂:创建一系列相关产品(如 Android/iOS UI 工厂)。
Android 中 BitmapFactory 根据 opts 创建不同 Bitmap。
- 建造者模式(Builder)?
将复杂对象的构造与表示分离。AlertDialog、Notification.Builder、Retrofit.Builder、OkHttpClient.Builder 均用此模式。
- 观察者模式应用?
LiveData、RxJava、EventBus、ContentObserver。
Subject 持有 Observer 集合,状态变化时通知所有观察者。
- 责任链模式?
OkHttp 拦截器、Handler 事件分发(DecorView → ViewGroup → View)、Android 事件分发本身。
- 适配器模式与装饰器模式?
适配器:转换接口(如 ArrayAdapter 将 List 适配为 AdapterView)。
装饰器:增强功能而不修改原类(如 ContextWrapper 包裹 Context)。
- 代理模式(静态/动态)?
Retrofit 接口走 JDK 动态代理(Proxy.newProxyInstance)。
AIDL 走 Stub 代理跨进程调用。
Binder 的客户端代理 BinderProxy。
- 策略模式?
定义一组算法封装互换。如 RecyclerView.LayoutManager、OkHttp 缓存策略、计费方案。
- 模板方法模式?
AbstractActivity 定义流程骨架,子类实现步骤。如 AsyncTask 的 doInbackground;Android Activity 的 onCreate 是抽象给子类实现的模板。
- 外观模式(Facade)?
提供高层接口简化子系统。Context 就是一组系统服务的门面;Glide.with(...).load(...).into(...)。
十三、数据结构与算法
- ArrayList 扩容与 LinkedList 插入复杂度?
ArrayList 扩容 1.5 倍,扩容需 Arrays.copyOf。
ArrayList 尾部 add 均摊 O(1),中间 add O(n)。
LinkedList 任意位置 add O(n)(需遍历定位),头/尾 O(1)。
- HashMap 默认参数?
初始容量 16,负载因子 0.75。
链表转红黑树阈值 8,退化阈值 6(需 ≥64 容量才转树)。
容量为 2 的幂,扩容 2 倍。
- 常见排序算法复杂度?
算法 平均 最坏 空间 稳定
冒泡 n² n² 1 稳定
选择 n² n² 1 不稳
插入 n² n² 1 稳定
快排 nlogn n² logn 不稳
归并 nlogn nlogn n 稳定
堆排 nlogn nlogn 1 不稳
- 二分查找?
有序数组,每次比较中点,复杂度 O(logn)。注意 mid = left + (right-left)/2 防溢出。
- 双指针与滑动窗口?
双指针:有序数组两数之和、链表快慢指针找中点/环。
滑动窗口:最长无重复子串、最小覆盖子串,O(n)。
- LRU 实现?
HashMap + 双向链表。
访问时移到头;新插入放头;超容删尾。
LinkedHashMap(accessOrder=true)即可实现 LRU,Glide 内存缓存即是。
- 二叉树遍历?
前序、中序、后序递归/迭代(栈)。
层序(BFS,队列)。
中序 + 前序可唯一确定二叉树。
- DFS 与 BFS?
DFS 递归或栈,路径搜索、拓扑排序。
BFS 队列,最短路径(无权图)。
- 经典算法题?
反转链表、合并有序链表、判断环、LRU、二叉树最近公共祖先、最长回文子串、最长递增子序列、背包问题、滑动窗口最大值、岛屿数量。
- LeetCode 高频 Top 5?
两数之和
反转链表
最长无重复子串
合并两个有序链表
二叉树层序遍历
十四、Android 系统原理
- Android 系统启动流程?
BootLoader → Kernel → init → fork zygote → SystemServer → 启动 AMS/PMS/WMS 等系统服务 → 启动 Launcher。
init 读 init.rc,启动 servicemanager、zygote、surfaceflinger 等。
- Zygote 启动与应用启动?
Zygote 预加载常用类、资源、ART,提供 fork 创建应用进程的能力。
AMS 通过 Socket 通知 Zygote fork 新进程。
新进程执行 ActivityThread.main,最终调起 Activity。
- Binder 通信原理?
基于 mmap 共享内存 + 一次拷贝。
服务端注册到 servicemanager(binder driver),客户端通过 servicemanager 查找拿到代理。
IBinder.transact 调用,onTransact 处理。
AIDL 生成 Stub(服务端)和 Proxy(客户端)。
- 为什么 Binder 只拷贝一次?
进程间 mmap 同一块物理内存(内核缓冲区与用户空间映射同一物理页)。
客户端 copy_from_user 写入共享内存,服务端直接读到,省一次 copy_to_user。
- AMS / WMS / PMS 职责?
AMS:Activity 栈、进程调度、Service/Broadcast 管理。
WMS:窗口管理、Z 序、输入事件分发、SurfaceFlinger 通信。
PMS:APK 安装、权限、组件清单、包信息查询。
- APK 安装流程?
PackageInstaller 触发安装。
PMS 复制 APK 到 /data/app。
解析 AndroidManifest,分配 uid。
校验签名、合并权限。
dex2oat 编译(ART)。
写入 /data/data/包名 创建数据目录。
- APK 打包流程?
aapt/aapt2 处理资源生成 R.java 和 resources.arsc。
处理 aidl 生成 Java 接口。
javac 编译 Java 为 .class。
d8/r8 将 .class 转 .dex(含混淆、压缩)。
打包 dex + 资源 + native lib 签名为 APK。
zipalign 对齐 + apksigner 签名(v1/v2/v3)。
- ART 与 Dalvik 区别?
Dalvik 解释执行 + JIT,每次启动重新编译。
ART:AOT(安装时编译)+ JIT(运行时)+ Profile-guided(PGO,7.0+)。
ART 性能更好但安装体积大,后期 PGO 兼顾。
- Android 进程保活方案(已逐步失效)?
1 像素 Activity。
前台 Service(startForeground,5.0 后需通知)。
双进程互拉。
JobScheduler/WorkManager 唤醒。
国内厂商接入推送 SDK(华为/小米/OPPO/vivo)。
Android 8+ 后台限制越来越严,保活几乎不可能,正确做法是用 FCM/厂商推送。
- 进程优先级(oom_adj)?
前台 > 可见 > 服务 > 缓存 > 空。
系统 LMK(Low Memory Killer)按 oom_adj 从高到低回收。
十五、Android 11/12/13/14 新特性
Android 11
强制分区存储(targetSdk=30)。
权限单次授权。
后台位置权限独立弹窗。
ApkSignatureScheme v3 签名支持密钥轮换。
引入 5G API。
Android 12
Splash Screen API(启动屏统一)。
AVIF 图片支持。
大屏幕中心(窗口化、兼容模式)。
前台服务通知延后 10s。
近似位置权限(ACCESS_COARSE_LOCATION 必须请求)。
BluetoothAdapter 不再持有蓝牙整体权限。
Android 13
通知运行时权限 POST_NOTIFICATIONS。
细分媒体权限(READ_MEDIA_IMAGES/VIDEO/AUDIO)。
主题应用图标(自适应 + 单色)。
WiFi 运行时权限 NEARBY_WIFI_DEVICES(无需位置)。
预测性返回手势(开发者选项)。
Android 14
预测性返回手势正式版。
闹钟权限 SCHEDULE_EXACT_ALARM 受限(需用户授权)。 -隐式 Intent 限制:必须指定 component 或 action 与包名。
注册 Receiver 需指定 exportFlag(RECEIVER_EXPORTED/NOT_EXPORTED)。
通知前台服务类型必须声明(dataSync、mediaPlayback、location 等)。
最低 targetSdk 23。
十六、项目与场景题
- 一个图片加载框架需要考虑哪些?
三级缓存、生命周期、占位图、错误图、过渡动画、变换(圆角/模糊)、磁盘策略、内存峰值、OOM 防御、列表滑动暂停加载、长图采样。
- 列表卡顿如何优化?
复用 ViewHolder、DiffUtil 增量刷新。
异步加载/预加载、固定高度避免 measure。
Glide 配合 RecyclerView 预取(prefetch)。
减少 onBindViewHolder 中逻辑,子线程预计算。
限制图片尺寸、避免大图加载。
- 设计一个 IM 即时通讯客户端?
协议:TCP 长连接 + 自定义二进制协议,心跳保活。
消息:本地数据库存历史,分页加载,已读回执。
离线推送:FCM + 厂商推送通道。
多端同步:按消息 ID + 序号去重。
加密:TLS + 端到端可选。
UI:分页、消息合并、富媒体。
- 设计一个 App 启动框架?
启动任务抽象为 Task,声明依赖关系。
DAG 拓扑排序、按线程(主/IO/CPU)分发。
异步 + await 主线程依赖、依赖图延迟初始化。
监控每个 Task 耗时、上报。
按进程区分(main/process)启动。
- 长连接断线重连策略?
指数退避 + 随机抖动,避免雪崩。
主动心跳超时判定断开。
网络变化(NetworkCallback)触发重连。
重连期间切回 HTTP 轮询兜底。
- 一张图片从网络到显示的完整流程?
用户触发请求(ImageView)。
缓存查活动/内存/磁盘。
命中直接显示,未命中走网络。
OkHttp 发请求,CDN 回源。
服务端返回字节流,OkHttp 写入磁盘缓存。
解码为 Bitmap(inSampleSize 采样)。
转换(圆角等),存入内存缓存。
主线程 setImageDrawable 显示。
- 如何设计一个本地缓存?
LRU 内存缓存(LruCache)。
DiskLruCache 二级缓存,key 用 url md5。
过期策略(时间 + 大小)、缓存淘汰。
异步存取、防并发(锁)。
序列化方式(Kotlinx Serialization / Protobuf)。
- 多进程通信有哪些方式?
Binder(AIDL/Messenger)、ContentProvider、Socket、文件、SharedPreferences(不安全)、MemoryFile/共享内存、信号量。
大数据传输用 MemoryFile + Binder 句柄传递。
- 如何保证后台任务必执行?
WorkManager(约束 + 持久化),系统重启自动恢复。
前台 Service 提高优先级。
JobScheduler 在 API 23+。
应用被杀则配合厂商推送唤醒。
- 你做过最有挑战的项目?
(开放题,建议结构化回答)
背景 + 我负责什么 + 难点 + 解决方案 + 结果(量化数据)。
用 STAR 法则:Situation、Task、Action、Result。
强调技术深度、协作、量化收益。
附:面试准备建议
八股文 + 项目结合:理论要懂,但务必结合项目场景讲清楚取舍。
画图能力:能徒手画 Handler、Binder、RecyclerView 缓存、HTTPS 流程。
源码能力:挑 2~3 个框架源码读透(如 Glide、OkHttp、ViewModel)。
算法:剑指 Offer + LeetCode Hot 100 刷一遍。
新特性跟进:Android 11~14 + Compose + Kotlin 协程必问。
系统原理:Zygote、Binder、AMS、WMS 至少能讲清流程。
性能优化:启动、内存、卡顿、ANR、包体五大方向各准备一个案例。
简历真实:不要堆砌,每个项目都能讲细节