资讯动态

小米安卓开发笔试题复盘:从Handler到Binder的核心考点与解题思路

发布时间:2026/8/31 4:21:32 来源:尧图企业网站定制
2019年秋招的时候小米这套安卓开发笔试题A卷在牛客网和其他技术社区里被讨论得相当热闹。我前后帮几届学弟学妹复盘过这套题一个非常明显的感受是这套笔试题的考点设计并不追求“偏难怪”而是把大量基础概念放在各种真实工程场景里去考察。很多人考完对照答案发现自己“知识点都背过”但放在题目里就答不对问题基本出在没理解出题人真正想考的那个层面。这篇文章就以这套题A卷为切入点带你把各模块的考查逻辑、解题思路和复习方向完整过一遍。不管你是正准备安卓开发岗秋招的应届生还是想跳槽补基础的初级开发这篇文章都会比单纯刷题更有参考价值——因为我会把题目背后的判断标准一起讲清楚。1. 这套真题的考查版图先看清楚它想筛什么样的人1.1 从题型结构看筛选逻辑小米的安卓开发笔试题A卷整体分为客观题和主观题两大部分。客观题以单选、多选、填空为主覆盖Java基础、Android四大组件、Handler消息机制、多线程、集合类等高频考点主观题则是简答加编程题重点考察对系统机制的理解深度和代码落地能力。这里有个很关键的信号客观题部分不少题目是“一题多考点”的嵌套设计。比如给一段代码让你判断输出结果表面考的是HashMap的遍历顺序实际上还牵扯到hashCode()和equals()的重写规范、负载因子对扩容的影响。所以如果你只是孤立地背“HashMap是数组加链表”这种结论遇到这种题依然会翻车。主观题向来是拉分的关键。据我了解这套题里的简答题不会直接问“Activity有哪四种启动模式”这种干巴巴的问题而是给你一个具体场景假设App收到一个推送通知用户点击后需要跳转到某个已存在的页面并清空其上的所有页面你会怎么设计这本质上是考察singleTask和onNewIntent()的组合使用但更重要的是看你有没有处理过真实的任务栈场景。也就是说小米这套题筛选的并不是“背了多少八股文”的人而是“有没有真正写过、调过、优化过Android代码”的人。这一点贯穿全卷。1.2 高频考点地图哪些主题年年都出现我把这套题和这几年其他大厂的安卓笔试题做了个横向对比发现高频考点高度集中。下面这张表可以直接作为复习地图来用考点方向常见考查方式出题意图Java集合与并发代码输出题、线程安全判断题考察基础是否扎实能否写出并发安全代码Activity与任务栈场景题、启动模式判断考察对页面导航和返回栈的理解Handler消息机制源码流程简述、延时消息原理考察对主线程消息循环的底层认知Binder与AIDL面试问答、简答考察跨进程通信原理的理解屏幕适配dp/sp/density计算、适配方案对比考察真实项目中的兼容能力性能优化ANR、内存泄漏、布局优化考察线上问题排查经验设计模式手写单例、观察者、Builder考察代码抽象能力数据结构算法LRU、链表反转、二叉树遍历考察基本功这套题最聪明的地方在于它把上面这些点都打散重组进了看似常规的题目里。你越早看清这张地图复习就越有针对性。2. Java与Android基础题里的“陷阱”设计2.1 集合与并发那些反复被包装成坑的知识点这套题里Java基础部分占了相当大的比重尤其是集合类和并发编程。以HashMap为例题目可能这么出在多线程环境下往HashMap里同时put元素会发生什么A. 线程安全 B. 可能造成死循环 C. 数据丢失 D. 编译报错。正确答案是B和C。很多背过源码的人知道JDK 7里并发put可能触发resize()时的死循环但是题目把“丢失数据”也作为选项时不少人会犹豫。其实并发put时两个线程同时写同一个桶位后写的会覆盖前写的这本身就是数据丢失。这种出题方式就是在考察你有没有真正理解HashMap的结构而不是只会背“HashMap线程不安全”这一句话。再比如String相关的题。题目给出“String s new String(abc); 创建了几个对象”这种题在各类笔试里都算经典了但小米这道题里还加了个限定条件——如果常量池中已经有abc答案和没有是截然不同的。我建议复习时一定要把常量池、堆、栈这三块的内存模型彻底搞清楚不要死记答案因为同样的知识换个角度就被考到了。另外ArrayList和LinkedList的选择问题也很好被包装成陷阱。直接问“哪个查询更快”太简单了所以题目会给一段实际代码一个for循环通过get(i)遍历LinkedList问你时间复杂度是多少。答案是O(n²)因为每次get(i)都要从头遍历。这种题考的不是结论而是你有没有理解链表的结构特性。2.2 四大组件里最容易被追问的细节四大组件是安卓笔试必考的部分但这套题不满足于考“Activity有哪些启动模式”。它的典型问法是一个Activity A启动Activity BA和B各自会执行哪些生命周期回调如果B是透明主题A还会执行onStop()吗为什么如果B是singleTask模式并且已经存在于栈中启动它时走的是onNewIntent()还是重新执行onCreate()这几个问题连起来其实是一道完整的场景链。透明主题的情况下A只会走到onPause()而不会执行onStop()因为A仍然可见。singleTask模式下如果实例已存在系统会调用onNewIntent()并把之上的Activity清掉。答题的时候只要能把生命周期回调顺序写完整再配上对任务栈变化的描述这几分基本就稳了。Service这部分则喜欢考两种启动方式的区别。startService()和bindService()混用时Service的onDestroy()什么时候调用这个题目经常以选择题出现如果先startService()再bindService()最后只调用了unbindService()Service会销毁吗答案是不会必须再调用stopService()或stopSelf()。这也是实际项目中特别容易踩的坑——我见过不少同事在Activity销毁时只解绑不停止结果Service在后台一直跑着。ContentProvider和BroadcastReceiver在笔试里通常不会考太深。前者需要注意跨进程访问时的权限声明android:exported属性在targetSdkVersion 31以后默认值的变化是个新考点后者需要注意Android 8.0之后静态注册的隐式广播大部分被限制了动态注册成了主流方案。这些属于“版本敏感型”考点建议复习时对着官方行为变更文档过一遍。2.3 手写代码里的细节分单例模式远没有想象中简单这套题里有一道手写单例模式看似基础实际是失分重灾区。原因很简单很多人能写出懒汉式或饿汉式但写不出线程安全且性能良好的版本。我见过一个同学在试卷上写public class Singleton { private static Singleton instance; public static synchronized Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }这个写法能保证线程安全但getInstance()每次都要抢锁性能很差。如果要写成双重检查锁就必须注意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第三步可能先于第二步执行另一个线程就会拿到一个未初始化完成的对象。更推荐写的是静态内部类版本利用类加载机制天然实现线程安全public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }评卷的时候面试官想看的可不是“能跑就行”而是你有没有考虑线程安全、性能、指令重排这些细节。这类“手写基础代码”的题目恰恰最能拉开有项目经验和只会背书的两个人的差距。3. 系统机制题Handler、Binder、任务栈背后的底层逻辑3.1 Handler消息机制从MessageQueue到Looper再到IdleHandlerHandler这套机制在小米笔试题里是绝对的重头戏。直接问“Handler怎么用”太低级了这套题的考法是给你两个Handler一个在主线程创建一个在子线程创建问它们之间能不能互相发送消息。很多人的第一反应是“可以”因为Handler本来就是为了线程间通信。但这里有个前提子线程里创建Handler必须先调用Looper.prepare()和Looper.loop()否则直接new Handler()会抛RuntimeException: Cant create handler inside thread that has not called Looper.prepare()。还有一道经典的延时消息题postDelayed()的延时准确吗为什么不准确这个问题牵扯到MessageQueue的阻塞和唤醒机制。postDelayed()并不是精确计时它只是把消息按时间顺序排在队列里前一个消息执行时间过长后面的消息就会跟着延迟。所以答案是“不准”而且延迟的原因并非Handler本身而是消息队列前面有其他耗时任务占用了主线程。进阶一点还会考到IdleHandler。这个类的作用是当消息队列空闲时执行一些低优先级任务比如启动优化里的延迟初始化。它的queueIdle()方法返回true表示只执行一次返回false表示每次空闲都会执行。笔试里把这个概念放在“如何优化冷启动时间”的简答题里如果你能主动提到用IdleHandler做非必要的初始化绝对是个加分项。3.2 Binder为什么跨进程通信偏偏选它Binder是安卓系统里最核心的跨进程通信方式也是笔试里比较劝退的一道题。但说透了其实不复杂。小程序之间要传递数据Linux系统本身提供了很多IPC方式比如管道、Socket、共享内存、信号量。安卓为什么非要用Binder这道题的标准答案有几个关键点性能上Binder只需要一次内存拷贝而管道、Socket都需要两次拷贝安全上Binder为每个进程分配了UID/PID内核可以对调用方做身份校验设计上Binder把跨进程调用封装成了类似Java接口调用的体验开发者用AIDL写起来非常舒服。笔试题不会让你直接背这些但很可能给出一个情境请你设计一个跨进程获取用户信息的方案你会怎么做这种题就是给熟手准备的展示舞台。你可以从AIDL接口定义讲到Stub和Proxy的生成逻辑再到transact()方法如何通过Binder Driver完成数据封装和解包。把这些流程讲清楚比单纯写一个AIDL文件得分高得多。3.3 任务栈与启动模式一道场景题的完整推演任务栈是Activity相关题目里很多人最头疼的部分。小米这套题里的典型考法是当前任务栈从栈底到栈顶依次是A、B、C此时给A设置singleTask然后通过一个Intent去启动A问栈最终变成什么样。正确推演过程是系统发现A是singleTask模式就会检查任务栈中是否已有A的实例。因为有所以系统不会创建新的A而是把A上面的B和C全部出栈同时走A的onNewIntent()回调。最终栈里只剩A。如果A的launchMode是singleTop答案就完全不同了。singleTop只在栈顶复用实例。因为当前栈顶是CA并不在栈顶所以singleTop的A会正常创建一个新实例压入栈中变成A、B、C、A。这两种模式特别容易搞混我建议复习时自己画一下栈的变化图。更妙的是这套题会在此基础上追加一问如果B设置了FLAG_ACTIVITY_CLEAR_TOP启动B会怎样FLAG_ACTIVITY_CLEAR_TOP会把B之上的Activity全部清掉如果B是标准模式系统还会销毁原B并重建一个新的B如果B是singleTop且已经在栈顶则复用原实例。真实项目里“回到首页并清空中间页面”这种需求就靠这个Flag实现。4. 屏幕适配与多任务场景题从热词看高频方向4.1 屏幕适配不只会算dp还要懂方案选型屏幕适配的题在这套笔试题里并不少见而且近年来热度越来越高。网络上关于“uniapp开发安卓解决地图遮挡不适配”的讨论非常多本质上就是density适配没有做好导致的显示偏移。笔试最基础的考法是给一组数据让你算设计稿宽度是360dp实际设备宽度是1080pxdensity是3.0问某个控件在设计稿上的120dp应该在实际布局里写多少。这题很简单因为dp本身就是单位直接写120dp就行。但如果题目换成“以px为单位给出”你就需要除以density再换算成dp。进阶考法是方案对比。现在主流的适配方案有传统的res/values-xxxhdpi多套资源目录、widthDp限定符、今日头条的字节适配方案、以及最新的WindowInsets适配。笔试简答题可能会问你项目里是怎么做屏幕适配的为什么要选这个方案一个稳妥的答法是先说明你的App最低支持版本和目标版本再说明设计稿的尺寸基准然后重点讲你的适配单位换算逻辑。如果你用了自定义LayoutParams按比例计算就写清楚计算公式如果你用AutoSize这类开源库要能讲出它的核心原理是修改density全局变量。只要能体现出“我踩过适配的坑并且有一套成熟的解决路径”这道题就拿下大半了。4.2 多屏与多任务场景从多屏显示到多应用录音热词里有几个方向非常能说明问题“有两个屏幕cordova开发第二个屏幕怎么开发”“安卓开发mtk8.1支持多应用同时录音”。这两个都是非常典型的安卓多任务场景问题也是这套笔试题里常被隐藏考查的背景。多屏开发在车载系统和折叠屏设备上越来越重要。Activity可以通过Presentation或MediaRouter把内容投到第二块屏幕上。笔试如果出相关题多半会让你描述Presentation的用法创建一个继承Presentation的类在onCreate()里加载对应屏幕的Display然后通过WindowManager添加显示。完整的实现需要处理不同屏幕的density差异和触摸事件分发这些细节都能体现你的工程经验深度。多应用同时录音则牵扯到音频焦点和音频策略。Android 10之前常规情况下一个时间点只能有一个应用录音后续应用会抢占音频焦点导致先前应用录音中断。MTK平台支持多应用同时录音本质上是厂商在HAL层做了多路音频输入的处理。笔试如果考这个大概率是问“音频焦点是什么”以及“如何监听音频焦点变化”你要能写出AudioManager.requestAudioFocus()的监听回调知道AudioFocusRequest在Android 8.0之后的写法变化。这类场景题看起来很偏但背后考的都是系统机制。只要把音频管理、多屏显示这些系统服务的职责边界搞清楚不管题目包装成什么场景都能拆解出底层考点。4.3 Launcher开发桌面应用的考查价值Launcher在安卓开发岗的笔试里出现频率不低因为它把很多知识点串联在了一起。一个Launcher应用需要管理应用列表、处理桌面图标的长按拖拽、监听应用安装卸载的广播还得保证列表滑动的流畅性。笔试题里如果考Launcher通常会落在两个点上一是应用列表的数据加载怎么用PackageManager查询所有已安装应用并过滤掉系统应用二是桌面滑动性能优化怎么用RecyclerView配合DiffUtil做增量更新而不是每次全量刷新。这个话题在这套题里出现我的判断是出题人想考察“系统应用开发思维”——不只是写业务页面还要理解桌面和应用之间、应用和应用之间的数据交互方式。即使你没做过Launcher只要能把PackageManager和BroadcastReceiver的用法讲清楚这道题也能有不错的得分。5. 实战代码题设计模式、算法与工程实现的得分点5.1 手写LRU缓存一套代码看出三个层次LRU缓存是笔试题中的常青树小米这套也不例外。它的核心要求是实现一个get(key)和put(key, value)方法容量满时淘汰最久未使用的数据所有操作的平均时间复杂度是O(1)。最直接的答案是使用LinkedHashMap开启访问顺序模式public class LRUCacheK, V extends LinkedHashMapK, V { private final int maxSize; public LRUCache(int maxSize) { super(16, 0.75f, true); this.maxSize maxSize; } Override protected boolean removeEldestEntry(Map.EntryK, V eldest) { return size() maxSize; } }三个字能解决的事但不同层次的人写法完全不同。初级选手能写出上面的版本就算不错中级选手会指出LinkedHashMap不是线程安全的需要加锁或用Collections.synchronizedMap()包装高级选手则会直接手写HashMap加双向链表把节点插入、删除、移动的每一步都写清楚。如果让你手写双向链表版本你还需要注意get命中节点后要把该节点移动到链表头部put新节点时把节点加到头部如果容量满了把尾部节点移除同时删除HashMap中对应的键。链表节点和HashMap的value是同一个对象这里存在一个“节点即值”的设计技巧能写出来就是亮眼操作。5.2 图片加载框架设计从需求出发做架构这套笔试题里的另一道典型设计题是让你设计一个图片加载框架需要支持内存缓存、磁盘缓存和网络加载。这种题不要求你完整实现代码而是考察你的架构设计能力。我的建议是分三层来回答。第一层是接口层对外提供load(url).into(imageView)的链式调用第二层是缓存层L1内存缓存用LRUL2磁盘缓存用DiskLruCache第三层是请求执行层用线程池并发下载下载完成后切回主线程更新UI。如果你能继续说清楚以下几点得分会明显提升图片加载前要检查ImageView是否被复用了避免列表错位同一个URL的并发请求要合并避免重复下载内存缓存要考虑Bitmap所占内存大小而不是只按数量来限制。这些点全部来自实际开发中的痛点和解决方案比纸上谈兵的设计模式更有说服力。5.3 算法题的选题风格与刷题建议小米这套笔试里的编程题难度整体适中不考特别偏的算法。常见的题目类型包括链表反转、括号匹配、二叉树层次遍历、最长公共前缀偶尔会出现一道动态规划但都是爬楼梯这个级别。我的判断是这套题的重点不是筛算法天才而是确认候选人具备基本的代码能力。所以刷题策略上我不建议把大量时间花在Hard题上。LeetCode的Top 100高频题按标签分类刷一遍重点掌握数组、字符串、链表、二叉树、栈和队列、哈希表这几类就足够了。每道题尽量把时间复杂度和空间复杂度分析写清楚因为笔试编程题通常还会留一个空让你填算法复杂度。另外提醒一句笔试的编程环境往往没有本地IDE顺手考前一定要在牛客网或者力扣的在线编辑器上练几道题适应没有自动补全的环境。很多人算法能力不差但考试时因为手写API不熟悉白白丢了很多分太可惜了。6. 备考复盘从这套题反推的安卓复习路线6.1 必啃的基础知识清单如果你现在才开始准备时间又比较紧我建议按优先级来做第一优先级Java集合类源码、并发编程synchronized、volatile、线程池、JVM内存模型。这是选择题的绝对主力。第二优先级Activity生命周期与启动模式、Service两种启动方式、Handler消息机制、Binder原理。这是简答题的核心素材。第三优先级布局优化、ANR分析、内存泄漏排查、RecyclerView复用机制。这些是性能优化题的主要来源。第四优先级设计模式中的单例、观察者、Builder、责任链算法中的链表、二叉树、栈、队列。参考书方面我推荐《Android开发艺术探索》和《第一行代码》搭配使用。前者讲原理深入浅出后者适合快速过一遍基础API。Java部分的并发和集合直接看《Java并发编程的艺术》里的关键章节就够用了。6.2 项目经历怎么准备笔试过了之后还有面试关但笔试阶段就已经开始为面试做铺垫了。小米这套主观题里会问到“你项目里的内存优化是怎么做的”“如果让你设计一个消息推送SDK你会怎么设计”这类问题本质上就是在预演面试中的项目深挖环节。所以备考时一定要把项目经历重新梳理一遍重点想清楚你负责的模块有哪些技术难点为什么做这个技术选型有没有对比过其他方案最终的性能数据是多少哪怕是课程设计级别的项目只要能把这个逻辑讲透比那些包装得高大上但一问就露馅的项目有用得多。我在帮学弟改简历时发现一个高频问题很多人写了“使用Glide加载图片”但从来没想过Glide的缓存策略和Lifecycle感知是怎么实现的。建议你把自己项目里用到的每个第三方库都挑一个核心原理去读一遍源码哪怕只读懂一个类面试的时候都能多出很多可说的东西。6.3 踩坑记录与心态调整最后聊点实在的。很多人笔试题答不好不是不会而是栽在细节上。一个是审题不清。选择题里的“下列说法错误的是”和“下列说法正确的是”经常有人看反这类题一套卷子至少错两道。另一个是时间分配不合理简答题写太多废话导致最后编程题没时间做。我的建议是选择题控制在25分钟内简答题每题控制在10分钟内留下40分钟以上做编程题。还有一个容易忽视的坑是环境变量。有的在线笔试系统需要你配置Java环境或者选择语言版本。考试前一定提前半小时进入系统把环境检查好。我就见过有人因为JDK版本不对代码在本地能跑在系统里编译不过这种错误真的特别冤。心态上我不建议把某一家公司的笔试成败看得太重。笔试题的通过率往往跟岗位竞争热度直接相关有时候同一份答卷在这个部门能进面试在另一个部门连简历门槛都过不了。你要做的就是把基础打扎实把能拿的分都拿到剩下的事情交给运气。如果你正在备考安卓开发岗这套小米真题的复盘思路其实也适用于大多数大厂的笔试。重点永远是基础概念要精确系统机制要通透工程场景要有感。把这三点做到位笔试题再怎么换包装你都能一眼看穿考点在哪。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价