android 面试题

Android 面试题大全(详细完整版)

本文档涵盖 Java 基础、Kotlin、Android 四大组件、UI、多线程、网络、存储、性能优化、架构、Jetpack、开源框架、设计模式、算法、系统原理等方向,每题附详细答案。

目录

Java 基础
Kotlin
Android 四大组件
UI 与 View 体系
多线程与异步
网络编程
数据存储
性能优化
架构设计
Jetpack 组件
开源框架源码
设计模式
数据结构与算法
Android 系统原理
Android 11/12/13/14 新特性

项目与场景题

一、Java 基础

  1. 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
  1. String、StringBuilder、StringBuffer 的区别?

特性 String StringBuilder StringBuffer

可变性 不可变 可变 可变

线程安全 安全(不可变) 不安全 安全(synchronized)

性能 低(拼接产生新对象) 高 中

应用场景 少量字符串 单线程拼接 多线程拼接

  1. HashMap 原理?JDK 1.8 有哪些优化?

底层:数组 + 链表(拉链法)。

JDK 1.8:链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树;≤ 6 时退化链表。

默认初始容量 16,负载因子 0.75,扩容翻倍。

key 为 null 时存在 index 0。

扩容时 1.8 不会重新 hash,而是用 (e.hash & oldCap) == 0 判断位置。

  1. HashMap 和 HashTable、ConcurrentHashMap 的区别?

对比 HashMap HashTable ConcurrentHashMap

线程安全 否 是 是

null key 允许 1 个 不允许 不允许

性能 高 低(全锁) 高(分段锁/CAS)

继承 AbstractMap Dictionary AbstractMap

JDK 1.8 的 ConcurrentHashMap 改为 CAS + synchronized 锁单个桶节点。

  1. ArrayList 和 LinkedList 区别?

ArrayList:数组实现,查询 O(1),增删平均 O(n),尾部增删 O(1) 均摊。

LinkedList:双向链表,查询 O(n),增删 O(1)(已知节点)。

ArrayList 扩容 1.5 倍。

  1. Java 异常体系?

Throwable

├── Error(不可恢复:OOM、StackOverflow)

└── Exception

├── 受检异常(IOException、SQLException)

└── RuntimeException(非受检:NPE、ClassCastException)

  1. try-catch-finally 中 finally 一定执行吗?

不一定:

在 try 之前就 return 或 System.exit()。

守护线程被强制终止。

JVM 崩溃。

如果 try 中有 return,finally 会在 return 之前执行,且 finally 中的 return 会覆盖 try 的 return。

  1. Java 中的四种引用类型?

强引用(StrongReference):普通赋值,GC 不回收。

软引用(SoftReference):内存不足时回收,适合做缓存。

弱引用(WeakReference):下一次 GC 必回收,用于 WeakHashMap、Handler 修复内存泄漏。

虚引用(PhantomReference):跟踪对象被回收,配合 ReferenceQueue。

  1. Java 泛型的类型擦除?

Java 泛型只在编译期检查类型,编译后擦除为原始类型(Object 或上界)。运行时无法获取泛型类型,所以 new T() 是非法的。可通过反射获取父类 ParameterizedType 拿到真实泛型。

  1. Java 反射的原理与性能?

反射通过 Class 对象在运行期获取字段、方法、构造器并调用。

原理:JVM 为每个加载的类生成 Class,反射调用走 Method.invoke,存在 MethodAccessor 优化(前 15 次 NativeMethodAccessorImpl,之后生成字节码)。

性能:比直接调用慢数倍,可通过缓存 Method 对象、setAccessible(true) 优化。

  1. Java 类加载机制(双亲委派模型)?

BootstrapClassLoader → ExtClassLoader/PlatformClassLoader → AppClassLoader → 自定义 ClassLoader

流程:先委派父加载器加载,父加载不了再自己加载。

作用:避免重复加载、保证核心类安全(防止用户覆盖 String)。

打破方式:重写 loadClass(Tomcat、SPI、热修复)。

  1. JVM 内存区域划分?

线程私有:程序计数器、虚拟机栈、本地方法栈。

线程共享:堆(新生代 Eden+S0+S1、老年代)、方法区/元空间。

JDK 1.8 后永久代被元空间(Metaspace,使用本地内存)取代。

  1. GC 算法有哪些?

标记-清除:产生碎片。

复制:新生代用,无碎片但浪费空间。

标记-整理:老年代用,无碎片但慢。

分代收集:新生代复制,老年代标记整理。

判断对象死亡:可达性分析(GC Roots)。

  1. volatile 关键字的作用?

保证可见性:强制从主内存读,写后立即刷回主内存。

禁止指令重排序:通过内存屏障。

不保证原子性(如 i++ 仍需 synchronized/AtomicInteger)。

  1. synchronized 的原理与锁升级?

原理:基于 Monitor(对象头 MarkWord 指向 Monitor)。

锁升级:无锁 → 偏向锁 → 轻量级锁(自旋)→ 重量级锁。

JDK 1.6 后引入偏向锁,JDK 15 已废弃偏向锁。

  1. synchronized 和 Lock 的区别?

项 synchronized Lock(ReentrantLock)

释放 自动 手动 unlock

响应中断 否 是

公平锁 否 可选

条件变量 单一 wait/notify 多 Condition

锁状态 不可查 可查/可中断

二、Kotlin

  1. Kotlin 与 Java 相比的优势?

简洁(data class、空安全、类型推断、扩展函数)。

互操作性好(与 Java 100% 兼容)。

协程替代回调,简化异步。

顶层函数、属性、默认参数。

  1. Kotlin 空安全机制?

var a: String = "x" 非空,var b: String? = null 可空。

访问可空类型需 ?.、!!、?:。

编译期检查,运行时仍可能 NPE(Java 互操作、!!、初始化未完成)。

  1. Kotlin 扩展函数原理?

扩展函数在编译期生成静态方法,接收者作为第一个参数传入,本质是语法糖,不会修改原类。所以:

不能被覆写(无动态分派)。

与成员函数同名时,成员函数优先。

  1. data class 与普通 class 区别?

自动生成 getter/setter、equals、hashCode、toString、copy、componentN(解构)。

限制:主构造器至少 1 参数、可继承、不能被 abstract/open/sealed/inner。

  1. Kotlin 伴生对象(companion object)?

Kotlin

class Foo {

companion object {

const val TAG = "Foo"

fun bar() {}

}

}

伴生对象是单例的,类似 Java 静态。

在字节码层面生成内部 Companion 类,可通过 @JvmStatic 生成真正静态方法。

  1. lateinit 与 lazy 的区别?

项 lateinit var by lazy

类型 var val

初始化 后续赋值 第一次访问时初始化

线程安全 否 默认 synchronized

可空 不可空类型 任意

检查 ::x.isInitialized 不可

  1. Kotlin 协程为什么轻量?

协程基于线程池(Dispatchers)+ 挂起函数,挂起时不阻塞线程而是把状态保存到 Continuation,由调度器恢复,单线程可跑数万协程。协程并非真正的"系统线程",而是用户态调度任务。

  1. suspend 函数原理?

编译期 CPS 变换:suspend fun foo() → fun foo(continuation: Continuation)。挂起时调用 suspendCoroutine 保存状态,恢复时 Continuation.resumeWith 调用回填栈。

  1. Kotlin 密封类(sealed class)?

限制子类必须在同一文件/同一模块。

用于 when 表达式可穷举分支,新增子类编译器报错未覆盖。

常用于状态机、网络结果封装。

  1. Kotlin 作用域函数(let/run/with/apply/also)?

函数 接收者 返回值 常用

let it lambda 结果 null 检查

run this lambda 结果 计算返回

with this lambda 结果 调用多个方法

apply this 对象本身 初始化

also it 对象本身 链式副作用

  1. Kotlin 集合中 List 和 MutableList 区别?

List<out T> 只读视图(协变),MutableList<T> 可变。

listOf 返回只读 List,mutableListOf 返回可变。

Java 集合互操作时需注意只读 List 在 Java 端仍可变。

三、Android 四大组件

  1. Activity 的生命周期?

onCreate → onStart → onResume → onPause → onStop → onDestroy

异常重建:onSaveInstanceState( onStop 之前)→ 重建 onRestoreInstanceState(onStart 之后)。

弹出透明 Activity:旧 Activity 走到 onPause 不走 onStop。

  1. Activity 四种启动模式?

模式 说明

standard 每次新建实例

singleTop 栈顶复用,onNewIntent

singleTask 单实例任务栈,清掉其上 Activity

singleInstance 独立任务栈,全局唯一

  1. Activity 启动流程?

ContextImpl.startActivity → Instrumentation.execStartActivity → AMS.startActivity → 校验权限、创建 ActivityRecord、Pause 当前 Activity → ApplicationThread.scheduleTransaction → ActivityThread.H → LaunchActivityItem → performLaunchActivity(创建 Activity、调用 onCreate)。

  1. Activity 与 Fragment 生命周期关系?

Activity.onCreate → Fragment.onAttach → onCreate → onCreateView → onViewCreated → onActivityCreated → onStart → onResume

Fragment 多出:onAttach、onCreateView、onViewCreated、onDestroyView、onDetach。

v1.3 后 onActivityCreated 已废弃,建议用 ViewTreeLifecycleOwner。

  1. Service 的两种启动方式?

startService:独立运行,需 stopSelf/stopService 停止,onCreate→onStartCommand→onDestroy。

bindService:与绑定者共存亡,onCreate→onBind→onUnbind→onDestroy,可通信。

混合启动:典型音乐播放器,start 启动后 bind 控制界面。

  1. BroadcastReceiver 有序广播与普通广播?

普通广播:异步并发派发,无顺序。

有序广播:按优先级派发,可拦截、传递数据,abortBroadcast 终止。

注册方式:静态注册(AndroidManifest,8.0 后受限)与动态注册(registerReceiver,需在合适的时机解注册)。

  1. ContentProvider 初始化时机?

ContentProvider 在 Application.onCreate 之前被创建(在 attachBaseContext 之后),所以不能在 ContentProvider 中依赖 Application 初始化的组件。ContentProvider 的 onCreate 在 Application 的 onCreate 之前调用,

启动顺序:Application 构造 → attachBaseContext → installContentProviders → Application.onCreate。

  1. Intent 显式与隐式区别?

显式:指定 ComponentName,跳转目标明确。

隐式:通过 action/category/data 匹配 IntentFilter,常用跨应用启动。

隐式启动 Service 在 5.0 后被禁止(必须显式)。

  1. Activity、Window、View 的关系?

Activity 持有 PhoneWindow(Window 抽象)。

PhoneWindow 持有 DecorView(顶层 View)。

DecorView 包含 SystemView(状态栏导航栏)和 ContentView(setContentView 实际设置的位置)。

WindowManager 添加 DecorView 到屏幕(WindowManagerImpl → WindowManagerGlobal → ViewRootImpl)。

  1. Application 启动流程?

ActivityThread.main → 创建 ActivityThread → attach → 创建 Application → 调用 attachBaseContext → onCreate。Application 的 attachBaseContext 在 ContentProvider 之前,onCreate 在所有 ContentProvider 之后。

四、UI 与 View 体系

  1. View 的绘制流程?

三大过程:measure、layout、draw。

measure:确定 View 宽高,根 View 的 MeasureSpec 由窗口尺寸决定。

layout:确定 View 在父容器中位置。

draw:绘制背景、内容、子 View、装饰(滚动条等)。

入口:ViewRootImpl.performTraversals → performMeasure → performLayout → performDraw。

  1. MeasureSpec 三种模式?

模式 含义 对应

EXACTLY 确定大小 match_parent、具体值

AT_MOST 最大值 wrap_content

UNSPECIFIED 不限制 系统内部(ScrollView)

  1. 事件分发机制?

三个方法:

dispatchTouchEvent:分发事件。

onInterceptTouchEvent:拦截(ViewGroup 才有)。

onTouchEvent:消费事件。

流程:Activity → ViewGroup → View。返回 true 表示消费,super 走默认。

ACTION_DOWN 决定消费者,后续事件默认派给消费者,除非父级拦截。

滑动冲突:父级在 onInterceptTouchEvent 拦截,子级 requestDisallowInterceptTouchEvent 反拦截。

  1. 滑动冲突解决思路?

外部拦截法:父 View 的 onInterceptTouchEvent 判断何时拦截(推荐)。

内部拦截法:子 View 调用 parent.requestDisallowInterceptTouchEvent(true) 配合父 onInterceptTouchEvent。

  1. RecyclerView 与 ListView 区别?

RecyclerView 强制 ViewHolder,复用更彻底。

LayoutManager 灵活(线性/网格/瀑布流)。

ItemDecoration 自定义分割线,ItemAnimator 增删动画。

局部刷新 notifyItemChanged、DiffUtil 增量更新。

ListView 的 setEmptyView、header/footer 在 RecyclerView 中需自定义。

  1. RecyclerView 缓存机制?

四级缓存:

Scrap:mAttachedScrap(屏幕内,不改 position)、mChangedScrap(已变化的)。

Cache:mCachedViews(默认 2,屏幕外,按 position 直接命中)。

ViewCacheExtension:自定义。

RecycledViewPool:按 viewType 持有,多个 RecyclerView 可共享,存无绑定数据的 ViewHolder。

  1. ConstraintLayout 优势?

减少布局嵌套层级,扁平化提升性能。

约束(Constraint)定义位置关系,Chain 链式约束。

Bias 比例、Guideline 辅助线、Barrier 屏障。

  1. 自定义 View 步骤?

继承 View/ViewGroup 重写 onMeasure(必要处理 wrap_content)。

onDraw 绘制(Canvas、Paint)。

处理 padding(继承 ViewGroup 还需在 onLayout 处理子 View)。

自定义属性 attrs.xml + obtainStyledAttributes。

内存防止泄漏(Handler、动画在 onDetachedFromWindow 回收)。

  1. invalidate() 和 requestLayout() 区别?

invalidate:仅触发 onDraw(UI 刷新),不重新测量布局。

requestLayout:触发 measure、layout(不一定 draw),尺寸/位置变化时用。

postInvalidate:可在子线程调用(实际 post 到主线程)。

  1. WebView 性能优化?

预创建 WebView 池复用。

独立进程(防内存泄漏、OOM)。

DNS 预解析、预加载关键资源。

开启缓存(DOM、H5 APP Cache)。

Service Worker、离线包。

五、多线程与异步

  1. Android 中多线程方式有哪些?

Thread / Runnable

Handler

AsyncTask(已废弃)

HandlerThread(自带 Looper 的子线程)

IntentService(已废弃,用 JobIntentService/WorkManager)

线程池 Executor

协程 Coroutines

RxJava

  1. 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 创建。

  1. Handler 内存泄漏原因与解决?

原因:非静态内部类 Handler 持有外部 Activity,延迟 Message 持有 Handler,未移除导致 Activity 无法回收。

解决:静态内部类 + 弱引用;onDestroy 移除消息 removeCallbacksAndMessages(null)。

  1. MessageQueue.next() 如何阻塞?

nativePollOnce 通过 Linux epoll 机制监听 wake 管道文件描述符。无消息或消息未到时间时阻塞;其他线程通过 nativeWake 写管道唤醒。

  1. IdleHandler 的作用?

在 MessageQueue 空闲时执行的任务,如预加载、日志上报。Looper.myQueue().addIdleHandler,注意要在主线程添加。

  1. HandlerThread 与 IntentService?

HandlerThread:自带 Looper 的子线程,串行执行任务。

IntentService:继承 Service 内置 HandlerThread,任务执行完自动 stopSelf。Android 11 已废弃,推荐 WorkManager。

  1. 线程池核心参数与工作流程?

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。

  1. AsyncTask 为什么废弃?

默认串行执行(API 11 后),并行需 executeOnExecutor。

容易内存泄漏、配置变化重建问题、与生命周期不绑定。

推荐协程或 RxJava 替代。

  1. 协程 Dispatchers 的区别?

Dispatchers.Main:主线程,UI 操作。

Dispatchers.IO:线程池(64 上限),网络/IO。

Dispatchers.Default:CPU 密集(核数)。

Dispatchers.Unconfined:不指定,继承调用方线程。

  1. 协程 launch 和 async 区别?

launch:启动无返回值协程,返回 Job,fire-and-forget,异常默认向上抛。

async:返回 Deferred<t>,await 拿结果,异常靠 await 抛出。需 await 后才执行 await 抛异常。</t>

  1. 协程 Structured Concurrency(结构化并发)?

子协程的作用域由父 Job 管理,父取消则子取消,父等所有子完成才结束。避免协程泄漏,比 RxJava 的 dispose 更优雅。

六、网络编程

  1. HTTP 与 HTTPS 区别?

HTTP 明文,端口 80;HTTPS = HTTP + TLS/SSL,端口 443。

HTTPS 加密通信 + 身份验证(证书)+ 完整性校验,防止中间人。

  1. HTTPS 握手流程?

Client Hello:客户端发送 TLS 版本、随机数 1、加密套件。

Server Hello:服务端选 TLS 版本、随机数 2、加密套件。

服务端发送证书。

客户端校验证书(CA 签名链)。

客户端生成 pre-master secret,用公钥加密发送。

双方用 随机数1 + 随机数2 + pre-master 生成会话密钥。

后续用对称加密通信。

TLS 1.3 简化为 1 RTT,支持 0-RTT。

  1. TCP 三次握手与四次挥手?

三次握手:SYN → SYN+ACK → ACK,建立连接。第三步防止已失效的连接请求到达服务端造成资源浪费。

四次挥手:FIN → ACK → FIN → ACK,关闭连接。服务端收到 FIN 后可能还有数据,先 ACK 再单独发 FIN。

  1. TCP 为什么是可靠传输?

校验和、序列号、确认应答、超时重传、流量控制(滑动窗口)、拥塞控制(慢启动、拥塞避免、快重传、快恢复)。

  1. OkHttp 原理?

核心类:OkHttpClient、Request、Response、Interceptor 链。

责任链模式:RetryAndFollowUpInterceptor → BridgeInterceptor(加头)→ CacheInterceptor → ConnectInterceptor(连接池)→ CallServerInterceptor(写请求读响应)。

连接池:ConcurrentDeque<realconnection>,5 分钟 + 5 个连接复用策略。</realconnection>

异步:Dispatcher 用线程池,maxRequests=64,maxRequestsPerHost=5。

  1. Retrofit 原理?

通过动态代理(Proxy.newProxyInstance)拦截接口方法。

解析注解(GET/POST/Path/Query/Body)构造 OkHttp Request。

CallAdapter 转换 Call → RxJava Flowable / 协程 Deferred / LiveData。

ConverterFactory 处理 JSON 序列化(GsonConverterFactory)。

  1. 网络请求优化?

请求合并、减少请求次数。

gzip 压缩、图片 WebP。

HTTP/2 多路复用、Connection 复用。

DNS 预解析、HttpDNS 防劫持。

弱网策略:超时重试、断点续传、增量更新。

  1. WebSocket 与 HTTP 长轮询?

WebSocket:全双工,握手升级后保持长连接。

HTTP 长轮询:客户端发请求服务端 hold 直到有数据,开销大。

适合 IM:WebSocket + 心跳保活,离线消息靠推送。

七、数据存储

  1. SharedPreferences 原理与问题?

原理:xml 文件全量加载到内存(HashMap),commit 同步写回磁盘,apply 异步写回。

问题:apply 可能丢数据、ANR(onPause 等待)、跨进程不安全。

替代:DataStore(Preferences/Kotlin)、MMKV(mmap)。

  1. MMKV 原理?

基于 mmap,将文件映射到内存,写直接落到内存,由 OS 异步刷盘。

protobuf 序列化,append 写入,增量扩容。

性能:写比 SP 快数倍,多进程安全(文件锁)。

  1. SQLite 与 Room 区别?

Room 在 SQLite 之上做封装,编译期检查 SQL。

Entity、Dao、Database 三层。

支持 LiveData/Flow 返回观察式数据,自动迁移。

减少样板代码与运行时错误。

  1. SQLite 优化?

索引(注意索引维护开销)。

事务(BEGIN/COMMIT 批量插入)。

预编译 Statement 复用。

WAL 模式并发读写。

LIMIT 分页、避免 SELECT *。

  1. GreenDao、Realm、ObjectBox、LitePal 对比?

GreenDao:基于 SQLite 的 ORM,性能好,已较少维护。

Realm:自研引擎,跨平台,性能强但体积大。

ObjectBox:NoSQL,性能高,Kotlin 友好。

LitePal:简单 ORM,已不活跃。

推荐新项目用 Room。

  1. 文件存储方式?

内部存储(/data/data/包名),私有,无需权限。

外部存储(/sdcard/Android/data/包名),10.0 后沙盒化。

共享存储:MediaStore 访问图片/音频/视频。

SAF(Storage Access Framework)选择文件。

八、性能优化

  1. 启动优化?

冷启动:进程未创建,从 fork → Application → Activity。

优化:异步初始化(Task 工具/有向无环图调度)、延迟初始化、多进程分担、避免 Application 重逻辑、启动器框架(启动任务图)。

监控:TraceView、Systrace/Perfetto、Debug.startMethodTracing。

阈值:冷启动 < 800ms 较好。

  1. UI 渲染优化?

减少布局层级(ConstraintLayout、merge、include)。

避免 overdraw(背景透明、移除多余背景)。

ViewStub 懒加载。

列表预加载、DiffUtil 增量刷新。

GPU 渲染分析(Profile GPU Rendering)、Systrace 检查掉帧。

  1. 内存优化?

避免静态持有 Activity、Handler 泄漏、单例持有 Context 用 Application。

弱引用缓存大对象(Bitmap)。

onLowMemory / onTrimMemory 释放资源。

LeakCanary 检测 Activity/Fragment 泄漏。

内存峰值控制:Bitmap 采样 inSampleSize、Glide 缓存策略。

  1. 内存泄漏常见场景?

静态变量持有 Activity。

单例持有 Context。

Handler 延迟消息未移除。

内部类(非静态)持有外部。

监听器/广播未注销。

WebView 持有 Activity(用独立进程或 destroy 时移除)。

Cursor/Cursor 未关闭。

  1. ANR 原因与排查?

原因:主线程耗时操作(5s 输入事件超时、10s 广播、20s 服务)。

排查:adb pull /data/anr/traces.txt,分析主线程堆栈。

监控:FileObserver 监听 anr 目录、Watchdog 线程定时向主线程 post 任务看是否回来。

  1. 卡顿排查?

每帧需 16ms(60Hz),超出掉帧。

工具:Systrace/Perfetto、Profile GPU Rendering、Choreographer.FrameCallback 监测帧率。

BlockCanary 监测主线程消息耗时。

常见原因:主线程 IO、布局复杂、过度绘制、GC 频繁。

  1. Bitmap 优化?

inSampleSize 采样降分辨率。

inPreferredConfig(RGB_565 节省一半内存,ARGB_8888 默认)。

Glide/Fresco 等库做内存+磁盘缓存。

及时 recycle(旧版本,新版本由 GC)。

列表加载用 Glide 的 thumbnail、override(w,h)。

  1. OOM 排查?

通过 LeakCanary 排查泄漏。

Android Profiler 看 Java/Native/Code 堆占用。

MAT 分析 hprof 找支配树(GC Root 到对象路径)。

区分 Java 堆 OOM 与 Native OOM(图库、视频)。

  1. 包体积优化?

代码混淆 R8/ProGuard。

资源压缩 shrinkResources。

图片 WebP、SVG。

so 库裁剪 abiFilters、动态化插件化。

资源混淆 AndResGuard。

Bundle 动态下发。

  1. 电量优化?

网络请求合并、使用 GCM/FCM 推送而非轮询。

限制后台定位频率( fused location )。

JobScheduler/WorkManager 替代常驻后台 Service。

WakeLock 用完即释放。

监控:Battery Historian。

  1. 网络优化?

HttpDNS 防 DNS 劫持。

弱网探测 + 降级策略。

TCP KeepAlive、连接复用。

图片 CDN、WebP、按需尺寸。

离线缓存、预加载。

九、架构设计

  1. MVC、MVP、MVVM 区别?

项 MVC MVP MVVM

Model 数据 数据 数据

View XML+Activity XML+Activity XML+Activity

Controller/Presenter/ViewModel Activity Presenter 接口 ViewModel

通信 View↔Model 直接 Presenter 反向 View 接口 双向绑定

耦合 高 接口隔离 数据驱动

测试 难 易 易

  1. MVVM 数据绑定实现?

DataBinding:XML 中绑定 ViewModel 数据,生成 BR。

LiveData:观察者,生命周期感知,自动更新 UI。

StateFlow/SharedFlow:协程冷/热流,配合 collectLatest。

  1. 单一数据源(SSOT)原则?

状态集中在 ViewModel 持有的 UiState(data class)中,UI 通过不可变流订阅,事件单向流动。避免多处状态不一致。

  1. MVI 架构?

Model-View-Intent:

Intent:用户意图(密封类)。

reduce(intent):纯函数生成新 State。

View 渲染 State。

单向数据流(UDF)、状态不可变、易测试。

  1. Clean Architecture 分层?

Presentation(UI/ViewModel)→ Domain(UseCase/Repository 接口)→ Data(Repository 实现、DataSource)。

依赖方向:外层依赖内层,内层不感知外层。

优势:可替换数据源、易测试、跨平台。

  1. 组件化与插件化区别?

组件化(ARouter/WMMrouter):编译期打包各业务模块为独立 Library,运行期同一进程,跳转解耦。

插件化(VirtualApk/RePlugin/DroidPlugin):未安装 APK 动态加载运行,可热插拔。

区别:组件化是工程组织,插件化是运行时动态加载未安装代码。

  1. ARouter 路由原理?

编译期 APT 扫描 @Route 注解,生成映射表(按 group 分组)。

运行期首次调用 init 加载映射表(按需分组加载)。

跳转:navigation() 查表 → 包装 Postcard → 调起 Activity / 跨模块 Service。

  1. 多模块通信方案?

路由:ARouter/WMRouter 跳转 Activity。

服务暴露:接口下沉到 Base,模块提供实现,注册到 ServiceLoader。

事件总线:EventBus/LiveData/Flow 跨模块通知。

数据共享:公共数据模块或 Repository。

十、Jetpack 组件

  1. Lifecycle 原理?

LifecycleOwner(Activity/Fragment)持 LifecycleRegistry。

LifecycleObserver 通过注解 @OnLifecycleEvent 或 DefaultLifecycleObserver 回调。

FragmentActivity 在 Activity 内部 ReportFragment 派发生命周期事件(兼容旧版)。

  1. LiveData 为什么能感知生命周期?

LifecycleBoundObserver 包装 Observer 并绑定 LifecycleOwner。

在 onStart 后开始派发,onStop 后暂停,onDestroy 自动 remove。

active 状态(STARTED/RESUMED)才会回调 onChanged,避免后台更新浪费。

  1. LiveData 粘性事件问题?

LiveData 最新值会分发给新 Observer,导致事件型数据出现"粘性"。解决:

SingleLiveEvent(不严谨)。

EventWrapper(一次性消费标记)。

改用 SharedFlow/Channel 处理事件。

  1. ViewModel 为什么能保存配置变化数据?

ViewModel 存在 ViewModelStore(Activity 持有)。

配置变化时 ActivityThread 调用 retainNonConfigurationInstances 保存 ViewModelStore,重建后取出。

Activity 销毁(非配置变化)时调用 ViewModel.onCleared。

  1. Room 编译期做了什么?

APT 处理 @Entity/@Dao/@Database 注解。

生成 XxxDao_Impl,实现增删改查(基于 SupportSQLiteStatement)。

校验 SQL、返回类型(Flow/LiveData)。

  1. Navigation 组件?

NavGraph(XML 描述目的地)+ NavHostFragment(容器)。

NavController 管理回退栈,safe args 生成类型安全跳转。

适合单 Activity 多 Fragment 架构,集成深层链接。

  1. WorkManager 的特点?

兼容到 API 23,自动选择 JobScheduler/AlarmManager+BroadcastReceiver。

约束(网络/电量/存储)、可重复/单次、链式任务。

应用退到后台仍能执行,系统重启后可恢复。

适合:上传日志、同步数据、定时刷新。

  1. DataStore 与 SP 区别?

DataStore 基于 Flow + 协程,异步、避免 ANR。

Preferences DataStore 无类型;Proto DataStore 强类型(protobuf schema)。

不支持全量同步读取,需 suspend/Flow 访问。

  1. Compose 与 View 的关系?

Compose 是声明式 UI 框架,独立绘制体系,最终走 AndroidComposeView(一个 View)。

@Composable 函数经 Compose 编译器插入 group 操作,记录到 SlotTable。

重组:State 变化触发 Recompose,按粒度更新。

可与 View 互嵌(AndroidView / ComposeView)。

  1. Hilt 与 Dagger 区别?

Hilt 基于 Dagger,提供标准组件(Application/Activity/Fragment/View/Service)。

简化 @Component 配置,预定义 SingletonComponent/ActivityComponent 等。

@HiltAndroidApp 入口,@AndroidEntryPoint 注入。

ViewModel 通过 @HiltViewModel + @Inject 构造器自动绑定。

十一、开源框架源码

  1. Glide 缓存机制?

内存缓存:LruResourceCache(活动资源 ActiveResources 强引用正在用的 + LRU 弱引用回收的)。

磁盘缓存:DiskLruCache,分原始数据/转换后。

加载流程:先活动资源 → 内存 → 磁盘 → 网络/资源。

生命周期:通过 SupportRequestManagerFragment 监听,onStop 暂停。

  1. Glide 三级缓存?

活动资源 ActiveResources(正在显示)。

内存缓存 LRU。

磁盘缓存。 (再后才是网络/文件源)

  1. OkHttp 拦截器链?

Java

Response response = new RealCall(...).execute();

// 同步入 getResponseWithInterceptorChain

顺序:

RetryAndFollowUpInterceptor:重试、重定向。

BridgeInterceptor:补全请求头、解压 gzip。

CacheInterceptor:磁盘缓存命中判断。

ConnectInterceptor:从连接池获取或新建 RealConnection。

CallServerInterceptor:通过 Okio 写入请求读出响应。

  1. EventBus 原理与不足?

register:反射扫描订阅方法,按 eventType 收集 Subscriber。

post:根据 eventType 在 toPostThread 找到所有订阅者,按 ThreadMode 切换线程调用。

不足:编译期不检查、运行时性能、难以追踪、内存泄漏风险。

替代:LiveEventBus、Flow、SharedFlow、RxJava。

  1. RxJava 调度器原理?

subscribeOn 决定上游执行线程,多次调用只有第一个生效。

observeOn 决定下游线程,可多次切换。

内部通过 Scheduler.Worker 调度,本质上也是线程池。

Lift 操作符嵌套调用.onNext 形成洋葱链。

  1. LeakCanary 原理?

注册 ActivityLifecycleCallbacks,onDestroyed 时用弱引用持有 Activity + 关联 KeyedWeakReference 到 RetainedManager。

触发 GC 后 5 秒检查弱引用是否被回收(检查 ReferenceQueue)。

未回收的对象 dump heap(profiler)。

用 Shark(原 HAHA)解析 hprof,找引用链,输出泄漏。

  1. Coil 与 Glide 对比?

Coil 基于 Kotlin + 协程,依赖更小,API 友好。

同样三级缓存,Coil 用 OkHttp + 真实协程调度,Glide 用线程池。

Compose 一等公民,Coil 适配更好。

十二、设计模式

  1. 单例模式与 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 不是原子操作)。

静态内部类更优雅,懒加载且线程安全。

  1. 工厂模式与抽象工厂?

简单工厂:一个方法根据参数返回不同实例。

工厂方法:每类产品对应一个工厂子类。

抽象工厂:创建一系列相关产品(如 Android/iOS UI 工厂)。

Android 中 BitmapFactory 根据 opts 创建不同 Bitmap。

  1. 建造者模式(Builder)?

将复杂对象的构造与表示分离。AlertDialog、Notification.Builder、Retrofit.Builder、OkHttpClient.Builder 均用此模式。

  1. 观察者模式应用?

LiveData、RxJava、EventBus、ContentObserver。

Subject 持有 Observer 集合,状态变化时通知所有观察者。

  1. 责任链模式?

OkHttp 拦截器、Handler 事件分发(DecorView → ViewGroup → View)、Android 事件分发本身。

  1. 适配器模式与装饰器模式?

适配器:转换接口(如 ArrayAdapter 将 List 适配为 AdapterView)。

装饰器:增强功能而不修改原类(如 ContextWrapper 包裹 Context)。

  1. 代理模式(静态/动态)?

Retrofit 接口走 JDK 动态代理(Proxy.newProxyInstance)。

AIDL 走 Stub 代理跨进程调用。

Binder 的客户端代理 BinderProxy。

  1. 策略模式?

定义一组算法封装互换。如 RecyclerView.LayoutManager、OkHttp 缓存策略、计费方案。

  1. 模板方法模式?

AbstractActivity 定义流程骨架,子类实现步骤。如 AsyncTask 的 doInbackground;Android Activity 的 onCreate 是抽象给子类实现的模板。

  1. 外观模式(Facade)?

提供高层接口简化子系统。Context 就是一组系统服务的门面;Glide.with(...).load(...).into(...)。

十三、数据结构与算法

  1. ArrayList 扩容与 LinkedList 插入复杂度?

ArrayList 扩容 1.5 倍,扩容需 Arrays.copyOf。

ArrayList 尾部 add 均摊 O(1),中间 add O(n)。

LinkedList 任意位置 add O(n)(需遍历定位),头/尾 O(1)。

  1. HashMap 默认参数?

初始容量 16,负载因子 0.75。

链表转红黑树阈值 8,退化阈值 6(需 ≥64 容量才转树)。

容量为 2 的幂,扩容 2 倍。

  1. 常见排序算法复杂度?

算法 平均 最坏 空间 稳定

冒泡 n² n² 1 稳定

选择 n² n² 1 不稳

插入 n² n² 1 稳定

快排 nlogn n² logn 不稳

归并 nlogn nlogn n 稳定

堆排 nlogn nlogn 1 不稳

  1. 二分查找?

有序数组,每次比较中点,复杂度 O(logn)。注意 mid = left + (right-left)/2 防溢出。

  1. 双指针与滑动窗口?

双指针:有序数组两数之和、链表快慢指针找中点/环。

滑动窗口:最长无重复子串、最小覆盖子串,O(n)。

  1. LRU 实现?

HashMap + 双向链表。

访问时移到头;新插入放头;超容删尾。

LinkedHashMap(accessOrder=true)即可实现 LRU,Glide 内存缓存即是。

  1. 二叉树遍历?

前序、中序、后序递归/迭代(栈)。

层序(BFS,队列)。

中序 + 前序可唯一确定二叉树。

  1. DFS 与 BFS?

DFS 递归或栈,路径搜索、拓扑排序。

BFS 队列,最短路径(无权图)。

  1. 经典算法题?

反转链表、合并有序链表、判断环、LRU、二叉树最近公共祖先、最长回文子串、最长递增子序列、背包问题、滑动窗口最大值、岛屿数量。

  1. LeetCode 高频 Top 5?

两数之和

反转链表

最长无重复子串

合并两个有序链表

二叉树层序遍历

十四、Android 系统原理

  1. Android 系统启动流程?

BootLoader → Kernel → init → fork zygote → SystemServer → 启动 AMS/PMS/WMS 等系统服务 → 启动 Launcher。

init 读 init.rc,启动 servicemanager、zygote、surfaceflinger 等。

  1. Zygote 启动与应用启动?

Zygote 预加载常用类、资源、ART,提供 fork 创建应用进程的能力。

AMS 通过 Socket 通知 Zygote fork 新进程。

新进程执行 ActivityThread.main,最终调起 Activity。

  1. Binder 通信原理?

基于 mmap 共享内存 + 一次拷贝。

服务端注册到 servicemanager(binder driver),客户端通过 servicemanager 查找拿到代理。

IBinder.transact 调用,onTransact 处理。

AIDL 生成 Stub(服务端)和 Proxy(客户端)。

  1. 为什么 Binder 只拷贝一次?

进程间 mmap 同一块物理内存(内核缓冲区与用户空间映射同一物理页)。

客户端 copy_from_user 写入共享内存,服务端直接读到,省一次 copy_to_user。

  1. AMS / WMS / PMS 职责?

AMS:Activity 栈、进程调度、Service/Broadcast 管理。

WMS:窗口管理、Z 序、输入事件分发、SurfaceFlinger 通信。

PMS:APK 安装、权限、组件清单、包信息查询。

  1. APK 安装流程?

PackageInstaller 触发安装。

PMS 复制 APK 到 /data/app。

解析 AndroidManifest,分配 uid。

校验签名、合并权限。

dex2oat 编译(ART)。

写入 /data/data/包名 创建数据目录。

  1. 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)。

  1. ART 与 Dalvik 区别?

Dalvik 解释执行 + JIT,每次启动重新编译。

ART:AOT(安装时编译)+ JIT(运行时)+ Profile-guided(PGO,7.0+)。

ART 性能更好但安装体积大,后期 PGO 兼顾。

  1. Android 进程保活方案(已逐步失效)?

1 像素 Activity。

前台 Service(startForeground,5.0 后需通知)。

双进程互拉。

JobScheduler/WorkManager 唤醒。

国内厂商接入推送 SDK(华为/小米/OPPO/vivo)。

Android 8+ 后台限制越来越严,保活几乎不可能,正确做法是用 FCM/厂商推送。

  1. 进程优先级(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。

十六、项目与场景题

  1. 一个图片加载框架需要考虑哪些?

三级缓存、生命周期、占位图、错误图、过渡动画、变换(圆角/模糊)、磁盘策略、内存峰值、OOM 防御、列表滑动暂停加载、长图采样。

  1. 列表卡顿如何优化?

复用 ViewHolder、DiffUtil 增量刷新。

异步加载/预加载、固定高度避免 measure。

Glide 配合 RecyclerView 预取(prefetch)。

减少 onBindViewHolder 中逻辑,子线程预计算。

限制图片尺寸、避免大图加载。

  1. 设计一个 IM 即时通讯客户端?

协议:TCP 长连接 + 自定义二进制协议,心跳保活。

消息:本地数据库存历史,分页加载,已读回执。

离线推送:FCM + 厂商推送通道。

多端同步:按消息 ID + 序号去重。

加密:TLS + 端到端可选。

UI:分页、消息合并、富媒体。

  1. 设计一个 App 启动框架?

启动任务抽象为 Task,声明依赖关系。

DAG 拓扑排序、按线程(主/IO/CPU)分发。

异步 + await 主线程依赖、依赖图延迟初始化。

监控每个 Task 耗时、上报。

按进程区分(main/process)启动。

  1. 长连接断线重连策略?

指数退避 + 随机抖动,避免雪崩。

主动心跳超时判定断开。

网络变化(NetworkCallback)触发重连。

重连期间切回 HTTP 轮询兜底。

  1. 一张图片从网络到显示的完整流程?

用户触发请求(ImageView)。

缓存查活动/内存/磁盘。

命中直接显示,未命中走网络。

OkHttp 发请求,CDN 回源。

服务端返回字节流,OkHttp 写入磁盘缓存。

解码为 Bitmap(inSampleSize 采样)。

转换(圆角等),存入内存缓存。

主线程 setImageDrawable 显示。

  1. 如何设计一个本地缓存?

LRU 内存缓存(LruCache)。

DiskLruCache 二级缓存,key 用 url md5。

过期策略(时间 + 大小)、缓存淘汰。

异步存取、防并发(锁)。

序列化方式(Kotlinx Serialization / Protobuf)。

  1. 多进程通信有哪些方式?

Binder(AIDL/Messenger)、ContentProvider、Socket、文件、SharedPreferences(不安全)、MemoryFile/共享内存、信号量。

大数据传输用 MemoryFile + Binder 句柄传递。

  1. 如何保证后台任务必执行?

WorkManager(约束 + 持久化),系统重启自动恢复。

前台 Service 提高优先级。

JobScheduler 在 API 23+。

应用被杀则配合厂商推送唤醒。

  1. 你做过最有挑战的项目?

(开放题,建议结构化回答)

背景 + 我负责什么 + 难点 + 解决方案 + 结果(量化数据)。

用 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、包体五大方向各准备一个案例。

简历真实:不要堆砌,每个项目都能讲细节

相关推荐
蜡台6 小时前
Jetpack Compose 稳定性、重组优化(Stable / @NonRestartableComposable)
android·kotlin·compose·jepack
JMchen1239 小时前
Jetpack 内核实战(十):实战(下)——编辑页、全局设置与内存泄漏排查
kotlin·内存泄漏·android 实战·datastore 设置·jetpack 项目优化·编辑页开发
雨白1 天前
Kotlin 泛型基础:“万能魔法盒”的诞生
kotlin
小小测试开发1 天前
RAG评测:把忠实度、检索命中率、噪声敏感度做成可回归的流水线
人工智能·数据挖掘·回归·kotlin
撩得Android一次心动1 天前
Kotlin 语言【知识点整理3】
java·开发语言·笔记·学习·kotlin
Kapaseker1 天前
我常用的 5 个 Kotlin 优化小技巧
android·kotlin
名剑走天下1 天前
Kotlin_LiveData
开发语言·kotlin
JMchen1232 天前
Jetpack 内核实战(九):实战(上)——记事本首页列表 + 搜索功能开发
kotlin·android 实战·记事本 app 开发·recyclerview 优化·jetpack 综合实战
pengyu2 天前
【Kotlin 协程修仙录 · 炼虚境 · 初阶】 | 虚空造物:Channel 基础与协程间通信的管道艺术
android·前端·kotlin