2026年Android中高级面试题专栏(Android基础篇)

4 阅读10分钟

随着 Android 技术的日益成熟与演进,许多开发者在面对日益庞大的技术栈时容易感到迷茫。但无论上层框架如何变化,夯实的基础与核心原理永远是应对大厂面试和“互联网寒冬”最坚固的护城河

本文是 2026年Android中高级面试题专栏 的第一篇:Android基础篇。

我们剔除了浮于表面的“快餐式”背诵答案,紧扣 Android 基础核心,结合最新的架构演进与底层实现原理,带你一次性通关四大组件、Handler 消息机制、View 体系及 Context 机制。

一、 四大组件与应用架构

Q1: Activity 的生命周期在异常重建场景下是如何工作的?启动模式(LaunchMode)与 Task 栈管理有何深度细节?

核心回答:

  • 异常重建(如屏幕旋转、系统内存不足回收)

    • 系统会触发 onSaveInstanceState(Bundle)(在 onStop 之前),将 View 树的状态(如 EditText 输入内容、RecyclerView 滚动位置)及自定义数据保存到 Bundle 中。
    • 恢复时,系统在 onStart 之后调用 onRestoreInstanceState(Bundle),或者在 onCreate(Bundle) 中直接读取 savedInstanceState
  • 四大启动模式与 Flag 深入

    • standard:每次都新建实例注入当前 Task 栈。
    • singleTop:若目标 Activity 已在栈顶,则复用并回调 onNewIntent(Intent);不在栈顶则新建。
    • singleTask:栈内单例。寻找是否存在对应的 taskAffinity 栈,若存在且包含该 Activity 实例,则将其之上的所有 Activity 清空(Clear-Top 效果)并回调 onNewIntent()
    • singleInstance:全局单例。独占一个新的 Task 栈,且该栈内不允许存在其他 Activity。
    • 常用 Flag 对比FLAG_ACTIVITY_NEW_TASK(通常配合 taskAffinity 建立新栈)、FLAG_ACTIVITY_CLEAR_TOP(销毁目标以上的 Activity)、FLAG_ACTIVITY_SINGLE_TOP(等同于 singleTop 模式)。

Q2: startServicebindService 的生命周期有何区别?混合使用时如何正确销毁?

核心回答:

  • 生命周期差异

    • startServiceonCreate() \rightarrow onStartCommand() \rightarrow 服务后台运行。直到显式调用 stopService()stopSelf() 才触发 onDestroy()。与调用者生命周期无关。
    • bindServiceonCreate() \rightarrow onBind() \rightarrow 返回 IBinder 给客户端。当所有绑定者都调用了 unbindService() 后,系统会自动触发 onUnbind() \rightarrow onDestroy()
  • 混合使用(先 start 后 bind)

    • 服务启动后处于常驻状态;当页面与其绑定时,仅回调 onBind()
    • 正确销毁逻辑:必须解绑(调用 unbindService()停止服务(调用 stopService() 或内部 stopSelf()),这两个条件同时满足时,Service 才会真正执行 onDestroy() 销毁。

Q3: 应用架构模式 MVP、MVVM 与 MVI 有什么本质区别与演进逻辑?

核心回答:

维度MVP (Model-View-Presenter)MVVM (Model-View-ViewModel)MVI (Model-View-Intent)
数据流向双向交互(View \leftrightarrow Presenter)单向数据绑定 / 响应式驱动严格单向数据流(UDF)
状态管理状态分散在 View 和 Presenter 中ViewModel 持有多个 LiveData/Flow 状态唯一可信数据源(Single State)
View 耦合度通过接口强绑定,Presenter 易膨胀通过观察者模式解耦,View 监听数据变化View 仅发送 Intent,订阅 UI State 变更
并发与状态重置无法原生防御多线程状态竞争多个独立状态改变时可能导致 UI 多次刷帧状态不可变(Immutable),复制生成新状态
  • MVI 的核心架构逻辑

    1. User Intent:用户操作(如点击、滑动)封装为 Intent/Event 发送给 Model/ViewModel。
    2. Model/Reducer:接收 Intent,处理业务逻辑并生成新的不可变状态(UIState)。
    3. UI Render:UI 界面订阅唯一的 UIState,根据 State 的变化无副作用地渲染界面。

二、 消息机制与异步通信(Handler 深度专场)

Q1: 深度剖析 Handler 消息机制四大核心组件的设计与线程隔离机制

核心回答:

Handler 机制本质上是基于单线程事件循环的生产者-消费者模型

[ Producer Thread ]                              [ Consumer Thread (Looper) ]
     Handler      ──> MessageQueue (Message) ──>     Looper.loop()
  (sendMessage)           (PriorityQueue)               │ (dispatchMessage)
                                                        ▼
                                                 Handler.handleMessage()
  • Message:消息载体,内部通过单链表结构实现对象池机制(sPool),推荐使用 Message.obtain() 复用以减少 GC 压力。
  • MessageQueue:消息单链表队列,按消息的触发执行时间(when)升序排列。
  • Looper:线程的消息循环器。一个线程通过 Looper.prepare() 只能创建一个 Looper 实例,并存入当前线程的 ThreadLocal 中,保证了 Looper 与线程的绝对隔离与唯一性
  • Handler:消息发送者与处理者。构造时绑定当前线程的 LooperMessageQueue,通过 sendMessage() 将消息入队,并在 Looper 轮询到该消息时回调 handleMessage()

Q2: MessageQueue.next() 中的 Linux epoll 机制是什么?Looper.loop() 死循环为什么不会卡死 CPU?

核心回答:

  • 非阻塞等待与唤醒(epoll 机制)

    MessageQueue 的底层使用了 Linux 的 epoll_wait 机制。当 MessageQueue 中没有消息,或者栈顶消息的执行时间 when 未到时,Native 层的 nativePollOnce() 会让当前线程陷入休眠状态,并释放 CPU 资源。

  • CPU 资源占用

    死循环(for (;;))本身只是保持程序运行状态的一种结构。当没有消息时,线程处于休眠状态,不会占用 CPU 时间片

  • 唤醒逻辑

    当有新消息插入(enqueueMessage())且该消息需要立即执行,或者队列被插入了优先级更高的消息时,Native 层会通过向 eventfd 写入数据唤醒 epoll_wait,使 MessageQueue.next() 继续向下执行。

Q3: 什么是消息屏障(Sync Barrier)与异步消息?IdleHandler 的使用场景是什么?

核心回答:

  • 消息屏障(Sync Barrier)

    • 概念:一种特殊的 Message,其 target == null
    • 作用:当 MessageQueue 检索到消息屏障时,会阻塞后续所有同步消息的执行,优先遍历并提取出队列中的异步消息msg.isAsynchronous() == true)进行分发。
    • 应用场景:Android 的 UI 刷新机制(Choreographer)。在请求 VSYNC 信号时,系统会向 MessageQueue 插入一个消息屏障,确保 UI 绘制渲染的异步消息能够无延迟优先执行,绘制完成后再移除屏障。
  • IdleHandler

    • 概念MessageQueue 提供的内部接口。
    • 触发时机:当 MessageQueue当前没有消息可处理,或者所有消息的执行时间未到(处于空闲等待期)时触发。
    • 应用场景:延迟加载低优先级的任务(如主界面加载完成后的后台预加载、日志上报、动态 View 预创建),既不阻塞首屏渲染,又能高效利用 CPU 空闲时段。

三、 View 体系与用户交互

Q1: 详细说明自定义 View 的三大绘制流程(Measure, Layout, Draw)及 MeasureSpec 机制

核心回答:

1. MeasureSpec 计算机制

MeasureSpec 是由 32 位整型构成的打包值(高 2 位表示 Mode,低 30 位表示 Size)。系统根据父容器的 MeasureSpec 和子 View 自身的 LayoutParams 共同决定子 View 的 MeasureSpec

  • EXACTLY:精确大小(如 100dpmatch_parent)。
  • AT_MOST:最大上限(如 wrap_content),子 View 不能超过父容器给出的 size
  • UNSPECIFIED:不限制大小(多用于 ScrollView 等滑动控件)。

2. 三大流程详解

  • onMeasure(widthMeasureSpec, heightMeasureSpec)

    测量 View 的宽高。如果是 ViewGroup,需要遍历调用子 View 的 measure() 方法,并结合子 View 的测量尺寸确定自身的最终宽高,最后必须调用 setMeasuredDimension() 存入结果。

  • onLayout(changed, l, t, r, b)

    确定 View 的位置。ViewGroup 必须重写此方法,根据自身的测量宽高和子 View 的布局规则,循环调用 child.layout(l, t, r, b) 设定子 View 的四个顶点坐标。

  • onDraw(Canvas canvas)

    绘制 View 画面。依次绘制背景 \rightarrow 保存图层 \rightarrow 绘制自身内容(onDraw\rightarrow 绘制子 View(dispatchDraw\rightarrow 绘制装饰(如滚动条、前景)。

Q2: 经典面试题:Android Touch 事件分发机制的递交链条与滑动冲突解法

核心回答:

1. 事件分发责任链递交

事件处理的核心逻辑由 dispatchTouchEventonInterceptTouchEvent(仅 ViewGroup 有)和 onTouchEvent 三个方法构成:

[ Down Event ]
Activity.dispatchTouchEvent()
  └── PhoneWindow.superDispatchTouchEvent()
        └── DecorView.dispatchTouchEvent()
              └── ViewGroup.dispatchTouchEvent()
                    ├── onInterceptTouchEvent() == true ──> 自己 onTouchEvent()
                    └── onInterceptTouchEvent() == false ──> 子 View.dispatchTouchEvent()
  • 如果子 View 的 onTouchEvent 返回 true,则消费该事件,后续的 MOVEUP 事件将直接交由该子 View 处理。
  • 如果子 View 返回 false,事件将向上回溯,由父容器的 onTouchEvent 进行处理。

2. 滑动冲突两大解决范式

  • 外部拦截法(推荐) :点击事件先经过父容器的 onInterceptTouchEvent。如果父容器需要拦截(如横向滑动),则在 ACTION_MOVE 中返回 true,直接由父容器处理事件。
  • 内部拦截法:父容器不拦截任何事件(ACTION_DOWN 除外);子 View 在 dispatchTouchEvent 中通过 getParent().requestDisallowInterceptTouchEvent(true) 阻止父容器拦截。当子 View 检测到不需要该事件时,将其置为 false 重新交还父容器。

四、 Android 存储与 Context 机制

Q1: SharedPreferences 存在哪些性能与 ANR 隐患?MMKV 的底层原理是什么?

核心回答:

  • SharedPreferences 痛点

    1. 全量加载阻塞:首次读取时会将整个 XML 文件一次性加载到内存,可能导致主线程阻塞。
    2. apply() 的 ANR 隐患apply() 虽然是异步写入磁盘,但它会将写入 Task 加入 QueuedWork 队列。在 Activity 切换(onStop/onPause)或 Service 销毁时,系统会强制等待 QueuedWork 队列中的磁盘写入完成,从而阻塞主线程引发 ANR。
    3. 无法安全支持跨进程MODE_MULTI_PROCESS 极易导致数据丢失和内存不同步。
  • MMKV 底层优化原理

    1. mmap(内存映射) :使用 mmap 将文件直接映射到进程的虚拟内存空间,读写操作直接在内存中完成,由操作系统内核负责同步回磁盘,彻底避免了频繁的 I/O 写入和阻塞。
    2. Protobuf 序列化:采用 Protocol Buffers 编码,数据体积更小,仅在尾部追加修改增量,无需每次全量写入。
    3. 多进程并发安全:通过 Posix 文件锁(flock)实现跨进程高可靠的读写互斥。

Q2: Application Context、Activity Context 与 Service Context 的继承关系与应用场景有何不同?

核心回答:

1. Context 继承链架构

                    Context (abstract)
                       ▲
                       │
                 ContextWrapper
          ┌────────────┼────────────┐
          │            │            │
  Application       Service   ContextThemeWrapper
                                    ▲
                                    │
                                 Activity
  • ContextImplContext 的真正实现类。ContextWrapper 内部持有 mBase(即 ContextImpl),采用装饰模式代理所有方法。

2. 使用场景差异与内存泄漏避坑

Context 类型弹出 Dialog启动 Activity加载 Layout / View注册 BroadcastReceiver
Activity允许允许(无需 Flag)允许(带主题)允许
Application拒绝(崩溃)需要 FLAG_ACTIVITY_NEW_TASK不推荐(丢失主题样式)允许
Service拒绝(崩溃)需要 FLAG_ACTIVITY_NEW_TASK不推荐(丢失主题样式)允许
  • 防泄漏准则:单例对象、静态变量、长生命周期的异步回调中,严禁持有 Activity Context,必须使用 context.applicationContext。仅在涉及 UI 渲染、Dialog 弹出、显示 Theme 相关的场景下使用 Activity Context。

专栏目录导航

最后,祝愿每一位正在备战面试的 Android 开发者:

  • 能够在反复的梳理与思考中打破瓶颈,将知识真正融会贯通;
  • 在接下来的每一场面试中发挥出色、对答如流,展现出自己最出色的技术硬实力;
  • 拨云见日,顺利斩获心仪的大厂与中高级职位 Offer,在移动开发的技术道路上披荆斩棘、一路高歌!