news 2026/9/8 11:07:25

爱奇艺2019秋招Android笔试题核心考点与复习路线解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
爱奇艺2019秋招Android笔试题核心考点与复习路线解析

每年秋招,Android岗的笔试题目都绕不开那几个老伙计:Handler、线程池、事件分发、性能优化。爱奇艺2019秋招Android方向笔试题(B)我印象很深,因为我当时把这套题当成了“考前摸底卷”,认认真真做完一遍,再去对比其他大厂的真题,发现核心考点高度重叠。这套题不是什么偏题怪题,它的价值在于考察面广、贴近实际开发,很适合用来检验自己到底有没有真正理解Android的底层运行逻辑。

如果你是正在准备秋招的应届生,或者工作一两年想跳槽的Android开发,我强烈建议你找一套类似的真题,按考试节奏做一遍。做完别急着对答案,先把每道题背后牵涉的知识链捋清楚,再看自己在哪里卡壳。这篇文章我会把这一套题涉及的核心知识点、我当时踩过的坑、以及笔试后总结出来的复习路线完整写出来,方便你对照着查漏补缺。

1. 这套题想考的,其实是这三层能力

很多人刷笔试题喜欢背答案,这是最亏的做法。笔试题目只是表象,出题人真正想筛选的,是三层递进的能力:基础知识的覆盖面、原理理解的深度、以及解决实际问题的思路。爱奇艺这套B卷的题目设计,恰好把这三层都踩到了。

1.1 高频考点板块与建议权重分配

先看整体考察范围。以当年的秋招行情来看,Android笔试题基本可以划分成六个板块,这套卷子也沿用了类似结构。我根据自己的做题经验,给每个板块列一个建议复习权重:

考察板块常见题型建议复习权重
Java语言基础选择题、输出题20%
并发与多线程选择题、代码分析15%
Android核心机制选择题、简答25%
性能优化与稳定性简答、方案设计15%
设计模式与代码题手写代码15%
网络与数据存储选择题、简答10%

别小看最后那10%的网络和数据存储,很多人在这一块丢分丢得莫名其妙。比如HTTP与HTTPS的差异、TCP三次握手为什么不是两次、SQLite升级时怎么处理字段变更,这些都是笔试常客。我后来复盘了一下,真正拉开差距的往往不是难题,而是这些“你觉得你会、但说不清楚”的基础题。

1.2 B卷相比A卷的差异点

爱奇艺这套B卷和同期的A卷相比,明显更偏原理考察。A卷里有很多直接背结论就能选的题,B卷则喜欢给你一段代码,让你判断输出结果,或者问“为什么这样写会有问题”。这就意味着,如果你只是背了八股文,做B卷会非常难受。

举个例子,关于单例模式,A卷可能只问你“以下哪种写法是线程安全的”,B卷则会给你一个双重检查锁的代码片段,让你指出哪里有问题、volatile到底起着什么作用。题目本身不难,但需要你真正理解Java内存模型,知道指令重排和可见性的概念。所以我建议准备笔试时,不要光看结论,要把结论背后的“为什么”弄透,这样无论题目怎么变形,你都能应付。

2. Java与并发基础:笔试中丢分最狠的两个板块

Java基础这块,看似简单,实际上涵盖了JVM内存模型、集合类源码、异常处理、泛型、反射等多个细节点。爱奇艺这套题里,我印象最深的是关于HashMap的考点,因为它一问就能问出一串连环题。

2.1 HashMap的底层原理与并发问题

HashMap在笔试题里出现频率极高,几乎到了“十套题八套有”的地步。核心考点包括:底层数据结构、put操作的流程、扩容机制、为什么线程不安全、以及和Hashtable、ConcurrentHashMap的区别。

我更想强调的是底层数据结构的变化。JDK 1.7及之前的HashMap是数组加链表,JDK 1.8之后引入了红黑树。当链表长度超过阈值8,并且数组长度大于等于64时,链表会树化。这个阈值8不是随便定的,它基于泊松分布的计算结果,在负载因子0.75的情况下,链表长度达到8的概率已经非常低,树化是为了应对极端情况下的哈希碰撞。

面试官如果顺着HashMap继续问ConcurrentHashMap,你要能说出JDK 1.7的Segment分段锁和JDK 1.8的CAS加synchronized有什么区别。这个知识点在笔试简答题中也常见,不要只答“线程安全”,要答出锁的粒度变化和性能提升的原因。

2.2 equals与hashCode的约定

这个考点单独拿出来说,是因为它经常藏在代码输出题里。题目会给你一个自定义类,重写了equals但没有重写hashCode,然后把它放进HashSet或HashMap中,让你判断结果。很多人在这里翻车,原因是只记住了“重写equals必须重写hashCode”这个结论,却不理解背后的逻辑。

规则其实很简单:如果两个对象equals返回true,那么它们的hashCode必须相等。反过来不成立,hashCode相等不代表equals返回true。HashMap和HashSet依赖hashCode定位桶的位置,依赖equals确认对象是否相同。如果你只重写equals而不重写hashCode,两个逻辑相等的对象会被分到不同的桶里,导致HashMap中出现“两个key值相同但对象却存在”这种诡异情况。

2.3 线程池参数与拒绝策略

线程池是并发题里的重头戏。笔试很少让你背ThreadPoolExecutor的构造方法参数,而是给你一个场景,让你判断该用哪种线程池,或者分析某个线程池的队列满了之后会发生什么。

核心参数有七个:核心线程数、最大线程数、空闲线程存活时间、时间单位、任务队列、线程工厂、拒绝策略。很多人能背下来,但理解有偏差。比如核心线程数和最大线程数的关系,很多人以为任务来了就直接加到最大线程数,实际上线程池的处理逻辑是:先判断核心线程数是否已满,如果没满就创建线程执行任务;满了之后,新任务先放进队列排队;队列也满了,才会尝试创建新线程直到最大线程数;实在不行,才会触发拒绝策略。

我建议你亲手写一个ThreadPoolExecutor,把各个参数打印出来观察一遍,特别是LinkedBlockingQueue和SynchronousQueue的区别。SynchronousQueue不存储任务,而是直接把任务交给线程,如果当前没有空闲线程就会立即创建新线程,所以Executors.newCachedThreadPool用的是它,适合大量短时任务,但并发量高时会频繁创建线程,开销很大。

2.4 JVM内存模型与GC的常见问法

JVM相关考点在Android笔试里通常不会考得太深,但“Java内存区域”和“垃圾回收算法”是必背内容。我当时的复习方法是画一张内存区域的图,把程序计数器、虚拟机栈、本地方法栈、堆、方法区各自存什么标清楚,再对照Android的OOM场景去理解堆内存。

GC的常考算法有三个:标记清除、标记复制、标记整理。它们各有优缺点,标记清除会产生内存碎片,标记复制浪费一半空间,标记整理效率较低。现代JVM的垃圾回收器大多是分代收集,新生代用复制算法,老年代用标记清除或标记整理。Android的ART运行时和传统JVM不完全一样,但笔试中一般还是按JVM的知识回答,如果你能补充一句“ART在Android 8.0之后改用并发标记清除回收器”,会显得你的知识面更广。

3. Android核心机制:Handler、Binder、组件与事件分发

Android核心机制是整套笔试题的压舱石,也是面试官最看重的部分。爱奇艺这套题在这一板块考察了Activity启动模式、Service生命周期、Handler消息机制、事件分发和自定义View等内容,题量不小。

3.1 Activity启动模式与taskAffinity的使用场景

Activity的四种启动模式——standard、singleTop、singleTask、singleInstance——基本是送分题,但想拿满分不容易。难点在于它们与taskAffinity的配合,以及onNewIntent回调的触发时机。

我遇到过一道题,问的是“两个Activity分别设置了不同的taskAffinity,A以singleTask启动,B以standard启动,点击通知从后台拉起A,此时任务栈会发生什么”。这个场景在真实开发中很常见,比如从通知栏跳转到某个页面,不希望界面上堆积大量历史页面。理解taskAffinity的关键在于,不同taskAffinity的Activity可以存在于不同任务栈,而singleTask会先查找是否存在相同taskAffinity的任务栈,并复用它。

还有一个高频考点是“singleTop和singleTask有什么区别”。前者是栈顶复用,后者是栈内复用。如果启动singleTask的Activity时,它所在的栈里已经存在该Activity实例,系统会把它上面的所有Activity出栈,并回调它的onNewIntent。这个“把上面所有Activity出栈”的行为,和“清除栈顶”很像,但很多人会记混。

3.2 Service两种启动方式与Binder原理

Service这个考点在笔试里常以生命周期选择题出现。startService启动的Service,生命周期是onCreate、onStartCommand、onDestroy,不会回调onBind。bindService启动的Service,生命周期是onCreate、onBind、onUnbind、onDestroy。两者可以共存,先startService再bindService时,需要同时调用stopService和unbindService才能销毁Service。

Binder是Android进程间通信的核心,也是很多人觉得难啃的硬骨头。笔试一般不会让你写AIDL代码,但会让你解释Binder相对其他IPC方式的优势。核心思路是:Binder只需要一次数据拷贝,而传统管道、消息队列需要两次拷贝,性能更高;同时Binder为每个进程分配了UID,安全性更好。回答这个问题时,把“一次拷贝”和“UID身份标识”这两个关键点答出来,就基本达标了。

3.3 Handler消息机制,以及为什么会导致内存泄漏

Handler几乎是Android笔试的必考题,没有之一。考察内容包括:Looper、MessageQueue、Handler三者的关系;主线程为什么能无限循环而不卡死;以及Handler造成内存泄漏的原因和解决方案。

先梳理关系。每个线程只有一个Looper,Looper内部维护一个MessageQueue,Handler在发送消息时把Message插入MessageQueue,Looper通过loop方法死循环取出Message并交给Handler的dispatchMessage处理。主线程的Looper在ActivityThread的main方法中通过Looper.prepareMainLooper创建,然后调用Looper.loop进入循环。

关于“为什么主线程无限循环不会卡死”,这是一个很经典的追问点。答案是:Looper.loop里的死循环并不占用CPU,当MessageQueue没有消息时,主线程会阻塞在MessageQueue.next的nativePollOnce方法上,此时线程是睡眠状态,不会消耗CPU资源。有消息到来时,通过epoll机制唤醒线程。这个机制总结成一句话就是“没有消息就休眠,有消息就唤醒”。

内存泄漏的原因则是:Handler持有外部Activity的引用,如果Handler中有延迟消息在排队,而Activity已经销毁,消息队列仍然持有Handler的引用,导致Activity无法被回收。解决方案有几种,最有效的是把Handler定义成静态内部类,使用WeakReference持有Activity引用,同时在onDestroy中移除所有未处理的消息。

3.4 事件分发机制,三个方法要分清

事件分发考察的是dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent这三个方法的职责。我见过最好的记忆方式是:dispatchTouchEvent是总调度员,决定把事件交给谁;onInterceptTouchEvent是拦截器,只有ViewGroup有;onTouchEvent是最终处理者。

笔试常考的一个问题是“子View的onTouchEvent返回false,事件会怎样传递”。答案是事件会从子View回传到父ViewGroup的onTouchEvent,如果所有View都不处理,最终会传回Activity的onTouchEvent。这个“递归返回”的过程,很多人画图能画明白,一写代码就懵。我当时是自己在Demo里给每个方法加日志,然后点击屏幕观察输出顺序,做了几遍就彻底记住了。

3.5 View的绘制流程三步走

自定义View相关的笔试题,通常会考measure、layout、draw这三个流程,以及MeasureSpec的三种模式。UNSPECIFIED、EXACTLY、AT_MOST分别对应什么含义,是必须背下来的。其中EXACTLY对应match_parent和具体数值,AT_MOST对应wrap_content。

如果题目让你自定义一个View并实现wrap_content,需要注意一个坑:如果不在onMeasure中处理wrap_content,自定义View的宽高会默认和父容器一样大,而不是根据内容大小来。原因很简单,系统对wrap_content的处理取决于View是否自己实现了onMeasure逻辑,默认实现等同于match_parent。很多新手自定义View时遇到的“wrap_content失效”,根源就在这里。

4. 性能优化与稳定性:进阶题拉开差距的地方

性能优化是Android工程师进阶的必经之路,也是笔试简答题和方案设计题的重点。爱奇艺这套B卷在这方面给了不少篇幅,考察内容包括内存泄漏、ANR、布局优化、Bitmap优化等。

4.1 内存泄漏的典型场景与排查套路

内存泄漏的考点,除了前面说的Handler,还有几个常见场景:静态变量持有Activity引用、单例模式持有Context、非静态内部类创建了静态实例、资源未关闭、以及注册了监听器但没有反注册。

笔试如果考内存泄漏的排查,答案通常是配合LeakCanary。你要能说出LeakCanary的原理是基于WeakReference和ReferenceQueue,检测到弱引用被回收之后,说明对象没有被泄漏;如果一段时间后弱引用还没有被加入引用队列,说明对象可能被泄漏了。这时候再手动触发一次GC,并dump堆内存,分析引用链,就能定位到泄漏路径。

实际开发中我自己的排查步骤一般是这样:先让用户复现内存暴增场景,然后用Android Studio的Memory Profiler录制一段内存分配;操作完功能后,手动执行一次GC,观察内存是否回落到操作前的水平;如果明显偏高,就dump一份Java heap文件,用MAT或者Profiler自带的分析工具查看大对象和泄漏嫌疑。这套流程看起来简单,但能解决90%的内存泄漏问题。

4.2 ANR的类型与定位方法

ANR在笔试中的问法通常是“什么情况下会触发ANR,怎么定位”。触发场景有三个,分别是输入事件5秒未响应、广播前台10秒后台60秒未完成、前台服务20秒后台200秒未完成。这个数据在历年笔试中出现过多次,值得死记硬背。

定位ANR的方法是查看/data/anr/traces.txt文件,但在Android 8.0之后,traces文件位置和格式有所变化,可以通过adb bugreport来抓取。关键是理解ANR的本质原因:主线程被耗时操作阻塞,或者主线程被其他进程的Binder调用长时间占用。我在实际项目里遇到过一个比较隐蔽的ANR,主线程没有明显的耗时操作,但频繁做了大量的文件读写,由于CPU被IO抢占,导致输入事件得不到及时处理。这种情况用trace文件不太容易一眼看出来,需要结合CPU Profile进一步分析。

4.3 布局优化与Bitmap内存占用计算

布局优化最常见的两个工具是ConstraintLayout和include/merge标签。2019年的时候,约束布局已经逐渐普及,笔试中会问“为什么ConstraintLayout比传统多层嵌套布局性能好”。答案是减少View层级,层级越深,measure和layout递归越耗时,也更容易在GPU渲染时造成过度绘制。

Bitmap的内存占用计算方式是一个高频计算题:内存大小等于图片宽度乘以图片高度乘以每像素字节数。ARGB_8888格式每个像素占4字节,RGB_565占2字节,ALPHA_8占1字节。举个例子,1920x1080的图片用ARGB_8888加载,内存占用是192010804,约8.29MB。如果不做压缩直接加载,很容易造成OOM。笔试可能会问你inSampleSize的采样率怎么选,回答时要讲清楚inSampleSize只能取2的整数次幂,而且系统会向下取整到最接近的2的幂。

5. 手写代码题:题题见功底

代码题在笔试中的占比一般不高,但一旦出现,就是决定能否进入面试环节的关键。爱奇艺这套B卷的代码题偏向基础和实用,没有特别偏门的算法,但写起来非常考验代码功底。

5.1 单例模式的双重检查锁写法

单例模式是代码题中的“Hello World”,但能完全写对的人不多。笔试时最容易出错的点就是漏写volatile关键字。

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?这里要讲清楚两句完整的逻辑。第一次判空是为了避免不必要的同步;第二次判空是为了在多个线程同时进入同步块时保证只有一个能创建实例。但instance = new Singleton()不是一个原子操作,它分为三步:分配内存、初始化对象、将引用指向内存地址。如果不加volatile,第二步和第三步可能被指令重排,导致一个线程先拿到了引用,却发现对象还没初始化完成,从而出现空指针。volatile可以禁止指令重排,并且保证可见性。

笔试如果问你“有没有更简单的线程安全写法”,可以回答静态内部类方式。它利用JVM的类加载机制保证初始化时的线程安全,没有同步开销,也是我日常工作中更推荐的方式。

5.2 生产者消费者模型,考察线程协作基本功

另一个常见代码题是生产者消费者模型。考察点包括wait和notify的配合使用、Lock和Condition的实现方式,以及对阻塞队列的掌握程度。

用synchronized加wait和notify的写法是最基础的版本,但需要注意wait必须在循环中使用,因为线程可能被虚假唤醒。这个细节很多人都知道,但手写时容易写成if。用ReentrantLock加Condition的版本更清晰,也更容易让面试官看出你对并发包的理解。

笔试时间紧张,如果你只打算记一种写法,我建议优先掌握BlockingQueue版本。它几行就写完,逻辑清晰,不易出错,而且能展示你对Java并发工具类的熟练度。当然,如果能再写出Lock和Condition的版本,会在面试中加更多分。

5.3 高频数据结构题:链表反转与LRU缓存

链表反转属于很基础的算法题,递归和迭代两种方式都要会。更值得重视的是LRU缓存的手写,因为它涉及HashMap和双向链表的结合,能考察数据结构和业务场景的结合能力。

LRU题目的要求一般是“设计一个数据结构,支持get和put操作,时间复杂度为O(1),当缓存满时淘汰最久未使用的数据”。手写时容易犯两个错误:一是直接用LinkedHashMap实现,代码虽然简单,但如果题目要求考察原理,需要写出底层结构;二是双向链表的节点更新逻辑没理清,出现断链问题。

我的建议是把LinkedHashMap的构造方法参数accessOrder设为true这个知识点记住,并理解它为什么能实现LRU。面试官如果追问原理,你再讲HashMap加双向链表的实现思路,就非常稳了。

6. 实战避坑清单与复习路线

最后这部分,我根据自己的实战经验,把笔试中容易翻车的细节和复习时需要注意的问题整理成一个速查清单,方便你在考前快速过一遍。

6.1 笔试中容易翻车的三个细节

第一个细节是代码输出题里的字符串比较。很多题目喜欢考String的==和equals区别,尤其是String s1 = "aaa"和String s2 = new String("aaa")同时出现时,s1 == s2是false,因为一个是常量池对象,一个是堆对象。这种题属于送分题,但越是送分题越容易粗心。

第二个细节是Activity的onSaveInstanceState调用时机。它不一定在onStop之前调用,而是在Activity即将被销毁且有机会被恢复时调用。系统在内存不足杀死后台Activity之前,会调用它保存状态,这个时机和onPause、onStop没有绝对的先后关系。笔试题有时会故意把顺序写错,诱导你选错。

第三个细节是AsyncTask的串行与并行问题。在Android 1.6到3.0之间,AsyncTask是串行执行的;3.0之后默认也是串行,但可以通过executeOnExecutor改为并行。2019年的笔试题还在考这个点,因为它很能体现你是否有实际开发经验,而不是只看过新版本文档。

6.2 可持续使用的复习路线

以我个人的经验来看,刷笔试真题不能只图数量,要按“知识点—原理—应用”三层递进。第一遍做套题时,先按考试时间完成,不管对错;第二遍逐题分析背后的知识点,把每道题涉及的原理写一遍;第三遍重点做错题,并且把错题转化为自己的知识清单。

如果时间充裕,我建议在笔试前一周,把Handler消息机制、自定义View绘制流程、Activity启动模式、线程池参数这几个核心模块,用几句话向别人讲一遍。能讲清楚,说明你是真的理解了,而不是停留在背结论的阶段。

6.3 面试进阶还可以沿着这个方向深挖

如果你笔试过了,准备面试,建议在现有基础上再延伸几个方向:一是Jetpack组件库的具体使用和原理,比如LiveData和Lifecycle如何实现生命周期感知;二是Kotlin协程与线程池的对比,为什么协程更轻量;三是性能优化实战案例,准备一个你亲手解决过的线上问题,从问题定位到修复再到验证,描述得越具体越好。爱奇艺这套题虽然是2019年的,但这些方向到今天依然是Android面试的核心主线,值得花时间继续深挖。

最后说一点个人的体会。笔试刷题最忌讳的就是“我见过这个题”的错觉,真正到了考场上,题目稍微换个角度,很多人就露馅了。我自己当时把爱奇艺这套B卷做了三遍,每一遍都能发现新的知识盲区。前两遍做的时候,我都是老老实实把涉及到的源码翻出来看,把关键方法的实现逻辑记下来,而不是只看答案。这个过程很花时间,但效果确实好。希望这套复盘思路也能帮你在今年秋招里少走一些弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 1:46:00

IETF破局Apple-Siri僵局:用标准化协议实现安全互操作

这次我们来看一个非常特殊的技术议题:IETF 公开讨论的 Apple-Siri 与欧盟监管之间的“僵局”。它不只是一条科技新闻,背后牵涉到接口开放、安全边界、隐私授权和标准化协议,和开发者日常写的 API、OAuth 令牌、沙盒权限、审计日志有直接关系。…

作者头像 李华
网站建设 2026/9/5 22:04:03

SpringBoot+WebSocket打造轻量级在线聊天室:实战与避坑指南

简介:这是一份面向Java后端初学者与SpringBoot实践者的轻量级在线聊天室项目,聚焦实时通信核心场景,帮助开发者快速掌握WebSocket在SpringBoot中的集成与应用。资源共115个文件,包含21个Java后端代码文件(含WebSocket端…

作者头像 李华