准备Android面试最难的不是学新东西,而是把已经学过的东西讲清楚。很多开发者写代码很熟练,但面对面试官追问原理时就卡壳,比如Handler的MessageQueue为什么不会把主线程卡死、事件分发中onInterceptTouchEvent的返回值到底影响什么。这篇文章把面试中出现频率最高的几个知识板块整理出来,逐个拆解原理,并给出组织答案的思路。

Handler消息机制:被追问最多的考点
Handler几乎是Android面试的必考题,而且面试官往往不会只问表面用法,而是层层深入。标准的问法包括:Handler的工作原理是什么,为什么在子线程创建Handler要先调用Looper.prepare,一个线程可以有几个Looper,post和send的区别是什么。
核心要点是这样的:Handler把消息投递到MessageQueue,Looper以死循环的方式不断从MessageQueue中取出消息,交回给Handler处理。MessageQueue底层是一个按时间排序的单链表结构,enqueueMessage时会根据when字段决定插入位置,保证消息按执行时间先后排列。这里有个高频追问:Looper.loop是死循环,为什么主线程不会卡死?答案是当MessageQueue中没有消息时,会通过Linux的epoll机制阻塞在native层的nativePollOnce上,线程被挂起释放CPU,直到有新消息到来通过nativeWake唤醒。也就是说阻塞发生在底层,而且处理消息之外还要处理页面绘制、输入事件,这些都是靠这个循环驱动的,它恰恰是主线程能工作的前提,而不是负担。
另外要能讲清Message的回收复用机制。Message内部维护了一个静态链表作为消息池,调用recycle后会把消息清空并放回池中,obtain方法优先从池里取,避免频繁创建对象。面试中提到这一点能体现你对内存细节的关注。
四大组件与生命周期:考察对系统行为的理解
Activity的生命周期人人都会背,但面试官更爱问异常场景。典型问题有:从Activity A跳转到B,A的onPause和B的onResume执行顺序如何,屏幕旋转时生命周期怎么走,什么情况下只走onPause不走onStop。
标准的跳转顺序是A的onPause先执行,然后B的onCreate、onStart、onResume,最后才是A的onStop。这个顺序保证了B完全显示之前A不会被停止,如果B是一个透明主题的Activity,A就不会走到onStop,因为A仍然部分可见。屏幕旋转时如果没在manifest中配置configChanges,Activity会销毁重建,完整走一遍onPause、onStop、onSaveInstanceState、onDestroy,再重新onCreate;如果配置了orientation|screenSize,则只会回调onConfigurationChanged。
onSaveInstanceState的调用时机也常被问到。它在API 28之后只在系统可能杀掉Activity时才回调,之前的版本在onStop之前必定回调。数据恢复有两条路径:onRestoreInstanceState在重建时若有保存数据必被调用,且它的Bundle参数一定非空,而onCreate的Bundle参数可能为null,这也是二者的关键区别。
事件分发与View绘制:原理题的重灾区
事件分发的三个核心方法是dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。整个流程从Activity开始,依次经过Window、DecorView,再到ViewGroup逐层向下传递。一个经典结论是:同一个事件序列中,如果一个View一旦开始处理事件(onTouchEvent返回true),后续事件都会直接交给它,不会再走拦截判断。
滑动冲突是这块的高频应用题。比如外部ScrollView嵌套内部RecyclerView,解决方案有外部拦截法和内部拦截法两种。外部拦截法在父容器的onInterceptTouchEvent中判断滑动方向,符合条件才拦截;内部拦截法则是父容器默认不拦截,子View在dispatchTouchEvent中通过requestDisallowInterceptTouchEvent控制父类是否拦截。面试时最好能现场手写其中一种的核心代码。
// 外部拦截法:在父容器中处理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// DOWN事件不能拦截,否则后续事件都收不到
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
// 水平位移大于垂直位移时认为是在做横向滑动,父容器拦截
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
}
mLastX = x;
mLastY = y;
return intercepted;
}View绘制方面,measure、layout、draw三个阶段各自的职责必须能说清:measure确定View的大小,layout确定View的位置,draw负责绘制内容。要理解MeasureSpec的三种模式EXACTLY、AT_MOST、UNSPECIFIED与wrap_content、match_parent的对应关系,以及为什么直接继承View重写onDraw时,wrap_content不生效,原因是wrap_content时MeasureSpec是AT_MOST,默认实现等同于match_parent,需要手动处理。
性能优化与内存泄漏:区分初级和高级的分水岭
性能优化题目最能拉开差距。内存泄漏的定位思路是:用Android Studio的Memory Profiler抓取heap dump,重点排查Activity和Fragment实例是否被意外持有。常见泄漏场景包括静态变量持有Context、非静态内部类Handler持有外部Activity引用、线程未结束、注册的监听器未反注册、单例回调解绑遗漏等。
Handler泄漏是必考场景。由于Message可能长时间留在队列中,它持有Handler,而非静态Handler又隐式持有外部Activity,导致Activity无法回收。标准解法是用静态内部类加弱引用:
static class SafeHandler extends Handler {
private final WeakReference<Activity> ref;
SafeHandler(Activity activity) {
ref = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = ref.get();
if (activity == null || activity.isFinishing()) {
// Activity已被回收或即将销毁,直接返回避免崩溃
return;
}
// 正常处理消息
}
}ANR分析也是常见考点。ANR的类型有四种:输入事件超时5秒、前台Service超时20秒、前台广播超时10秒、后台Service超时200秒。分析时先看traces文件,定位主线程当时的调用栈,判断是IO阻塞、锁等待还是耗时计算。优化的通用思路是把耗时操作移到子线程,主线程只做UI相关工作。
RecyclerView缓存与线程并发:容易被忽略的加分项
RecyclerView的四级缓存经常被问到。第一级是mAttachedScrap和mChangedScrap,用于屏幕内item的快速重用,不需要重新绑定;第二级是mCachedViews,缓存刚刚移出屏幕的ViewHolder,位置信息保留,也不需要重新bind;第三级是ViewCacheExtension,留给开发者自定义;第四级是RecycledViewPool,按viewType存储,跨屏幕复用,需要重新bind数据。能主动对比ListView的两级缓存并说明为什么RecyclerView更适合复杂列表,会是很好的加分点。
线程方面,除了Java基础的线程池参数、synchronized和volatile的区别,Android特有的考点包括:AsyncTask为什么被废弃(默认串行、无法灵活控制、与生命周期绑定导致泄漏)、HandlerThread的适用场景(需要一个有消息循环的后台线程时)、IntentService的原理以及WorkManager相对它的优势。建议把答案往实际项目经验上靠,比如你在项目中是怎么用线程池统一管理网络请求的,这比背概念更有说服力。
最后给一个备考建议:面试答题时先给结论,再展开原理,最后补充实际项目中的例子。比如被问Handler,先一句话概括它是线程间通信的机制,再讲MessageQueue和Looper的协作,最后提一次你用Handler解决过的具体问题。这样的回答结构清晰、有层次,远比一口气背诵所有细节更能打动面试官。