ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

小米2019秋招安卓开发笔试题全解析:从基础到实战

小米2019秋招安卓开发笔试题全解析:从基础到实战 2019年的安卓校招是近几年大厂筛人最狠的一轮。我见过太多简历漂亮、项目经验看起来比同龄人丰富一大截的候选人最后都倒在了笔试这一关。原因特别简单——笔试考的不是你做过多少项目而是你的基本功到底扎不扎实。小米这套2019秋招安卓开发笔试题A就是非常典型的代表。整套卷子覆盖面广、基础题居多但暗藏坑点编程题看起来不难却非常考验代码习惯。今天我把这套题从头到尾复盘一遍结合当年我自己的答题思路和后来辅导学弟学妹时发现的常见问题把每类题的考点、解题思路、容易丢分的地方全部拆开讲清楚。无论你是正在准备安卓校招的应届生还是想查漏补缺的初中级开发这篇文章都值得认真读完。1. 试卷整体结构与考点分布1.1 题型与时间分配这套笔试题A整体沿用了大厂校招笔试经典的三段式结构选择题、简答题、编程题。从题量和难度来看选择题覆盖的是Java基础和Android基础简答题考察的是对核心机制的理解深度编程题则要求手写代码且能正常运行通过测试用例。我当时拿到的卷面大概是这样的结构时间限制在90分钟到120分钟之间。选择题建议控制在30分钟以内简答题控制在40分钟左右剩下的时间全部留给编程题。很多同学容易在选择题上死磕一道题琢磨五分钟结果后面编程题时间不够这是笔试的大忌。注意笔试的时间分配一定要提前规划好。编程题的分值占比通常是最大的而且一旦编译不通过基本拿不到分必须保证充足的调试时间。1.2 各大模块考察意图整套题其实就围绕一个核心目标筛选出基础扎实、原理清晰、代码能力过关的人。选择题主要筛掉基本功不行的简答题筛掉只会调用API而不懂原理的编程题则直接检验真实的编码能力。从模块占比来看Java基础知识大概占30%Android四大组件约占25%Handler与消息机制约占15%性能优化与内存管理约占15%网络与数据存储约占15%。这个分布非常符合2019年安卓开发岗位的要求——既要有扎实的Java功底又要对Android系统机制有深度理解还要有实际的性能优化意识。对比现在的安卓面试题目开始大量偏向Kotlin、协程、Jetpack Compose、Flutter等新内容但2019年的这套题反而更贴近“经典”。老实说这种经典题对于打基础反而更有价值因为底层原理不会因为框架更迭而变化。2. Java基础与并发编程题2.1 HashMap的底层原理与扩容机制这套卷子的选择题里几乎必有一道HashMap的题问法通常是“关于HashMap的说法正确的是”或者“JDK 1.8中HashMap在什么条件下链表会转成红黑树”。这类题目考察的是对常用集合底层实现的掌握程度。HashMap的底层是数组加链表加红黑树的结构。数组的容量要求是2的幂次方原因在于计算索引时用(n - 1) hash替代取模运算只有容量是2的幂次方时这个位运算才能保证哈希值均匀分布。负载因子默认是0.75意思是当元素数量超过容量的75%时触发扩容扩容时容量翻倍。在JDK 1.8中链表转红黑树的条件是链表长度达到8且数组容量大于等于64。这里很多人会漏掉第二个条件。如果链表长度已经达到8但数组容量还没到64会优先进行扩容而不是转红黑树。原因是链表过长往往是因为哈希碰撞太严重扩容后散列更均匀可能就不需要转树了。HashMap在并发场景下的问题也是常考的点。JDK 1.7及之前扩容时采用头插法多线程并发扩容可能形成环形链表导致get操作死循环。JDK 1.8改为尾插法避免了这个问题但put操作在并发下仍然存在数据覆盖的问题。所以并发场景应该使用ConcurrentHashMap而ConcurrentHashMap在JDK 1.8中抛弃了分段锁改用CAS加synchronized锁住数组的每个桶位锁粒度更小。答题技巧方面选择题如果问到HashMap的性能可以从时间复杂度的角度来分析理想情况下get和put是O(1)最坏情况下是O(logn)红黑树场景。如果选项里有“HashMap是线程安全的”这种表述直接排除。2.2 synchronized与volatile的底层区别这道题在简答题里出现过很多次问法是“简述synchronized和volatile的区别”或者“volatile是否能保证原子性”。这几乎是所有大厂笔试面试的必考题。volatile的本质是JMMJava内存模型中的可见性保障。它通过内存屏障实现写操作时会强制将工作内存中的值刷新回主内存读操作时会强制从主内存中重新读取。但它只保证可见性和有序性不保证原子性。经典的i问题就是证明——即使加了volatile多个线程同时执行i仍然可能丢失更新。synchronized则通过Monitor锁机制实现线程同步它保证了原子性、可见性和有序性。在JDK 1.6之后引入了偏向锁、轻量级锁、重量级锁的升级过程这些统称为锁的膨胀。其中偏向锁适用于单线程竞争的场景轻量级锁适用于线程交替执行的场景重量级锁则依赖于操作系统底层的互斥量实现。两者的使用场景可以这样区分一个线程写、多个线程读的场景用volatile就足够了比如状态标志位多个线程同时修改共享变量的场景则必须使用synchronized或Lock。在答题时如果能把锁的升级过程说清楚把内存屏障的具体行为讲明白这道题基本就稳了。千万不要只说“一个是锁一个是关键字”这种幼儿园级别的答案。2.3 JVM类加载机制与双亲委派选择题里偶尔会出现一题关于类加载的问“双亲委派模型以及为什么要这样设计”。这道题其实是考察你对JVM基础的理解安卓开发虽然不用自己写类加载器但理解双亲委派对理解资源加载很有帮助。双亲委派模型指的是类加载器收到类加载请求时不会自己先加载而是先委托给父类加载器逐层向上委托直到最顶层的Bootstrap ClassLoader。只有当父类加载器无法完成加载时子类加载器才会自己尝试加载。这样设计的原因有两个一是避免类的重复加载父类加载过的类子类不需要再加载一遍二是保证核心类库的安全性防止用户自定义的java.lang.String替换系统核心类。安卓平台上虽然没有传统意义上的JVM而是ART虚拟机类加载机制略有不同但如果你在笔试时能把JVM的双亲委派讲透再补充一句“安卓的PathClassLoader和DexClassLoader都遵循了这种父优先的加载顺序”会让阅卷人觉得你有举一反三的能力。2.4 手写单例模式的多种写法编程题中经常出现“手写单例模式”这道题表面上是考察设计模式实际上考察的是多线程并发控制能力。2019年校招笔试时我发现一个规律凡是直接写public static final Singleton INSTANCE new Singleton()这种饿汉式的同学大概率会被追问“这种写法有什么问题”。单例模式的写法有五种以上的变体饿汉式、懒汉式、双重检查锁DCL、静态内部类、枚举。我在笔试时推荐写出以下几种并备注各自的适用场景// 双重检查锁面试最推荐手写的一种 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; } }双重检查锁有一个非常关键的细节instance必须用volatile修饰。原因在于new Singleton()不是一个原子操作它包含分配内存、初始化对象、将引用指向内存三个步骤。如果不用volatileJVM指令重排可能导致其他线程拿到了一个尚未初始化完成的半成品对象。还有静态内部类的写法也非常推荐因为它既保证了懒加载又利用了类加载机制天然保证线程安全public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }笔试时建议至少写两到三种写法并在代码旁边简要注释每种写法的优缺点和适用场景这能体现你不仅会写代码还懂为什么这样写。3. Android核心组件相关题目3.1 Activity生命周期与启动模式Activity生命周期是安卓笔试选择题里最经典的考点没有之一。整套卷子大概率会出一道关于Activity的题可能是给出一个操作流程让你排列生命周期方法调用的顺序也可能是问启动模式的特性。正常情况下的调用顺序是onCreate → onStart → onResume → onPause → onStop → onDestroy中间穿插着onRestart。但真正的考点在于异常情况下的生命周期变化。比如屏幕旋转时Activity会被销毁并重新创建调用顺序是onPause → onStop → onDestroy → onCreate → onStart → onResume。如果想要避免重建可以在AndroidManifest.xml中给Activity配置android:configChangesorientation|screenSize但这种做法并不推荐因为对资源的适配不够灵活。四种启动模式也是必考内容standard、singleTop、singleTask、singleInstance。选择题如果问你“在什么情况下onNewIntent会被调用”答案就是singleTop模式下栈顶已存在该Activity、singleTask模式下栈中已存在该Activity、singleInstance模式下该Activity所在任务栈已经存在时会走onNewIntent而不是重新创建。要特别注意singleTask和singleInstance的区别singleTask是单个任务栈中只有一个实例其他Activity可以跟它共存于同一个栈而singleInstance是实例自己单独占有一个任务栈栈中只有它一个Activity一般用于需要与系统应用共享的界面比如来电界面。很多人把这两个搞混丢分很可惜。3.2 Service的启动方式与绑定方式的区别Service这部分的简答题主要考察两种启动方式的对比startService和bindService。startService启动的Service会一直运行直到调用stopService或stopSelf才会停止即使启动它的组件比如Activity已经销毁Service仍然在后台运行。这种情况适合执行后台耗时任务比如下载文件、播放音乐。bindService启动的Service生命周期与绑定的组件绑定在一起当绑定的组件销毁时Service会自动解绑解绑后如果没有其他组件绑定它Service就会被销毁。这种方式适合需要与Service进行交互的场景比如视频播放器中需要动态获取播放进度。这里有一个常考的陷阱混合使用startService和bindService时Service的停止时机是什么。答案是必须同时调用stopService和unbindServiceService才会被销毁。只调用其中任何一个都无法让Service彻底停止。另外还有两个高频考点Service的onStartCommand的返回值含义以及IntentService的原理。返回值中START_STICKY表示Service被系统杀死后会尝试重建并调用onStartCommand但Intent会变为nullSTART_NOT_STICKY表示被杀死后不会重建START_REDELIVER_INTENT表示被杀死后会重建并重新传递之前的Intent。IntentService内部使用了HandlerThread在onHandleIntent方法中串行处理任务处理完自动停止现在已经逐渐被WorkManager等方案替代但在理解后台任务处理原理上仍有价值。3.3 Handler消息机制整体流程Handler的消息机制是安卓开发笔试中分量最重的考点。几乎可以说每次大厂的安卓笔试面试都少不了Handler。这套题里也不例外简答题大概率会有一道“简述Handler、Looper、MessageQueue之间的关系”。标准的回答流程是Handler通过sendMessage方法将Message发送到MessageQueue中Looper通过loop方法不断从MessageQueue中取出消息并回调Handler的dispatchMessage方法最终在handleMessage中处理消息。整个流程建立在MessageQueue的底层nativePollOnce和nativeWake机制之上通过epoll机制实现阻塞与唤醒在没有消息时阻塞线程而不是空转避免CPU资源的浪费。主线程的Looper在ActivityThread的main方法中被初始化通过Looper.prepareMainLooper()创建之后调用Looper.loop()进入消息循环。子线程默认没有Looper如果要创建Handler需要先调用Looper.prepare()再调用Looper.loop()。笔试中如果问“在子线程中new Handler会发生什么”答案是会抛出RuntimeException: Cant create handler inside thread that has not called Looper.prepare()。关于内存泄漏的问题题目可能会问“Handler为什么会造成内存泄漏怎么解决”。原因是Handler持有外部Activity的引用如果MessageQueue中还有未处理的消息这条消息会持有HandlerHandler又持有Activity导致Activity无法被回收。解决办法有两种一是使用静态内部类加WeakReference弱引用二是在onDestroy中移除所有消息和回调调用handler.removeCallbacksAndMessages(null)。还有一个常见的进阶问题一个线程可以有几个Looper和几个Handler。答案是只有一个Looper可以有多个Handler。Looper通过ThreadLocal存储每个线程只有一个独立的Looper实例而多个Handler可以共享同一个Looper只需要在创建Handler时传入同一个Looper即可。4. 自定义View与屏幕适配4.1 View的绘制流程详解自定义View相关的考题在整套卷子里占了一个多选题的位置可能问“关于View的绘制流程下列说法正确的是”。考察内容包括measure、layout、draw三个阶段。View的测量过程是从ViewRootImpl的performTraversals方法开始的依次调用performMeasure、performLayout、performDraw。其中measure阶段通过onMeasure方法测量View的宽高使用的MeasureSpec包含SpecMode和SpecSize两部分内容。SpecMode有三种模式UNSPECIFIED表示父容器不对View做任何限制EXACTLY表示父容器已经确定了精确尺寸match_parent和具体dp值AT_MOST表示父容器给出最大限制wrap_content。很多人在自定义View时经常犯一个错误直接使用view.getWidth()拿到的宽高是0就以为是布局问题。其实在onMeasure执行完之后View的宽高还不一定能立刻拿到必须在onSizeChanged或onLayout执行后才能获取到准确的宽高值。另一个坑是在自定义View中直接使用了getPaddingLeft()等参数但忘记在测量模式中计算padding导致显示效果和预期不一致。关于绘制阶段的常考问题还有invalidate和requestLayout的区别invalidate只会触发onDraw重绘不会重新测量和布局requestLayout会从根布局开始重新执行measure和layout流程但不一定会触发onDraw。如果只需要刷新某个View的显示内容用invalidate就足够了频繁调用requestLayout会带来性能问题。4.2 屏幕适配方案解析由于热词中反复出现“uniapp开发安卓解决地图遮挡不适配的问题”我在复盘这套题时也额外聊聊屏幕适配因为这道题在2019年小米的笔试中同样出现过值得单独说明。安卓屏幕适配的核心目的是让同一份UI代码在不同尺寸、不同分辨率的设备上都能有合理的显示效果。经典的适配方案有三种dp自适应、限定符资源目录、百分比布局。dp是安卓中的虚拟单位1dp在不同密度的设备上对应的物理像素不同计算公式是px dp * (dpi / 160)。资源目录限定符包括尺寸限定符layout-large、最小宽度限定符layout-sw600dp、屏幕方向限定符layout-land等。现代安卓开发中头条适配方案修改系统密度值和AutoSize开源库这两类方案比较流行。头条适配的核心原理是修改DisplayMetrics中的density值让dp和屏幕宽度的比例保持固定。但这种方案有局限——当我们用AutoSize做全局适配时会发现如果项目里存在大量自定义View某些View可能因为measure逻辑特殊而无法随全局密度变化。回到热词中的“地图遮挡不适配”问题这本质上是因为地图控件如百度地图、高德地图是SurfaceView或TextureView实现它们使用的不是常规的View绘制流程而是独立的Surface在Android系统中的层级关系比较特殊。有些开发者通过修改地图控件的z轴层级或布局参数解决遮挡问题但推荐的做法是使用FrameLayout或RelativeLayout管理地图和覆盖物的层级关系让覆盖物添加为地图的子View由地图SDK统一管理层级。4.3 事件分发机制从点击到消费事件分发机制是自定义View和日常开发中最容易出问题的地方笔试中如果出现相关简答题难度通常不低。题目可能会这样问“当一个手指触摸屏幕上的一个Button时事件是如何从Activity分发到Button并最终被消费的。”完整流程是Activity → PhoneWindow → DecorView → ViewGroup → View。从Activity的dispatchTouchEvent开始事件先传给PhoneWindow的superDispatchTouchEvent再进入DecorView之后由根ViewGroup的dispatchTouchEvent决定是自己处理还是分发给子View。ViewGroup的dispatchTouchEvent中会调用onInterceptTouchEvent决定是否拦截当前触摸事件。如果拦截了事件交由ViewGroup自己的onTouchEvent处理如果没有拦截事件继续向下传递给子View的dispatchTouchEvent子View的onTouchEvent处理完后如果消费了事件则事件传递结束。如果子View没有消费事件会通过dispatchTouchEvent的返回值逐层向上回传最终回到Activity的onTouchEvent。常考的问题场景包括Button被父容器的点击事件拦截了怎么办解决方式是给父容器设置requestDisallowInterceptTouchEvent(true)然后重写onInterceptTouchEventScrollView嵌套RecyclerView为什么会有滑动冲突原因是两个可滚动容器的嵌套导致事件被外层ScrollView拦截。答题时如果能够配合图示把事件分发流程画出来会给阅卷人留下非常好的印象。笔试答题纸上可以画一个简洁的时序图来表示事件流向不能画图的话就用文字分步骤描述。5. 网络、数据库与架构设计题5.1 Retrofit与OkHttp的请求流程2019年的安卓笔试已经开始考察主流网络框架的实现原理问法通常是“简述Retrofit和OkHttp之间的关系”或“OkHttp的请求执行流程”。这类题目主要考察你是否只停留在会用层面。OkHttp的请求流程核心是拦截器链。一个完整的请求会依次经过应用拦截器自定义Interceptor、RetryAndFollowUpInterceptor重试与重定向、BridgeInterceptor桥接请求和响应、CacheInterceptor缓存处理、ConnectInterceptor连接处理、CallServerInterceptor网络请求。最终通过RealCall的execute同步或enqueue异步方法触发。Retrofit是基于OkHttp的上层封装。它通过动态代理方式在调用接口方法时生成ServiceMethod把注解解析成OkHttp的Request执行后通过Converter将ResponseBody转换成对应的Java对象。核心要素是CallAdapter和ConverterCallAdapter决定如何处理Call是返回Call本身还是转成Observable、suspend函数等Converter决定如何把请求体和响应体转成目标类型。在笔试时如果能提到Retrofit的动态代理机制源于Proxy.newProxyInstance并说明它把每个接口方法都代理到InvocationHandler中统一处理会显得你对框架有深入的理解。5.2 数据库升级与SQLite优化数据库相关的题在这套卷子里比较基础一般是选择题或者简答题。“SQLite的升级方式”就是一个经典问题。SQLite的升级通过onUpgrade回调实现当我们修改了数据库版本号检测到版本变化时就会触发onUpgrade。在这个回调中通常执行ALTER TABLE语句比如添加新列ALTER TABLE user ADD COLUMN age INTEGER DEFAULT 0;如果需要修改表结构或者批量修改数据最稳妥的方式是创建临时表、复制数据、删除旧表、重命名新表。这套操作步骤繁琐所以实际开发中有很多人选择使用Room或者GreenDAO这样的ORM框架。Room数据库在升级时通过Migration类实现当你增加了一个Entity字段时必须提供对应的Migration否则应用会直接崩溃这就是Room相对SQLiteOpenHelper做得更安全的地方。关于索引的优化问题也是常考点。SQLite的索引适合频繁查询的字段但不适合频繁更新的字段因为每次更新都需要同步修改索引结构。使用事务批量插入数据时速度可以提升数倍因为避免了每次插入自动提交事务的磁盘IO开销。5.3 MVC、MVP、MVVM架构对比分析架构设计类的题目通常出现在简答题中多数情况下主观性比较强没有唯一标准答案。这道题考察的是你对代码组织方式的思考以及是否有项目经验积累。MVC最典型的问题是Model和View之间存在直接耦合。在安卓开发中Activity既充当Controller又充当View导致Activity类庞大臃肿难以维护。同时View可以监听Model的变化Model也能直接驱动View更新让状态流转变得不可控。MVP通过Presenter层隔离了View和ModelView只负责展示和用户操作分发Model只负责数据获取所有逻辑都集中在Presenter中。Presenter持有View的引用当View销毁时需要及时断开引用否则会造成内存泄漏。MVP的优势是职责单一、便于单元测试缺点是Presenter层容易出现业务逻辑过重的情况需要额外管理View的销毁以避免内存泄漏。MVVM通过ViewModel和LiveData等工具实现了数据驱动的双向绑定View感知数据变化并自动更新同时ViewModel不持有View的引用所以不存在内存泄漏问题。在2019年时Google正式推荐了MVVM架构这也成为后续几年安卓开发的主旋律。如果你在笔试中能提到MVVM中ViewModel在配置变更时不会丢失数据的原因是ViewModelStore内部通过非配置更改来保存数据在Activity重建时直接复用旧的ViewModel实例这会让阅卷老师对你另眼相看。6. 编程题与手写代码实战6.1 高频考题链表反转与二叉树遍历编程题部分的题目通常比较经典但越是经典越考验细节。链表反转、二叉树遍历、字符串处理、排序算法、动态规划是出现频率最高的几类。链表反转的迭代写法并不复杂但很多人在写的时候容易在指针更新顺序上出错。正确思路是用prev指向当前节点的前一个节点用curr指向当前节点每次循环先保存curr.next防止链表断裂然后让curr.next指向prev更新prev为curr最后让curr向后移动一位。边界条件要注意链表为空和只有一个节点的情况。二叉树的前中后序遍历虽然简单但很多人容易在递归的终止条件上写错。如果笔试要求用非递归方式实现那就需要借助栈来模拟递归过程。前序遍历是入栈时输出中序遍历是出栈时输出后序遍历需要额外的标记位来判断左右子树是否都已访问完这些细节都是失分点。6.2 高频考题字符串中的最长回文子串这道经典动态规划题在笔试中出现概率很高常见问法是“求一个字符串的最长回文子串长度”或“输出最长回文子串本身”。最优解是中心扩展法。中心扩展法的思路是遍历字符串的每一个位置把它当成回文串的中心向两边扩展。需要注意区分奇数长度回文和偶数长度回文两种情况奇数的中心是一个字符偶数的中心是两个字符之间的间隙。每次扩展时对比左右两个字符是否相等记录最大长度和起始位置。public String longestPalindrome(String s) { if (s null || s.length() 2) return s; int start 0, maxLen 1; for (int i 0; i s.length(); i) { int len1 expandAroundCenter(s, i, i); int len2 expandAroundCenter(s, i, i 1); int len Math.max(len1, len2); if (len maxLen) { start i - (len - 1) / 2; maxLen len; } } return s.substring(start, start maxLen); } private int expandAroundCenter(String s, int left, int right) { while (left 0 right s.length() s.charAt(left) s.charAt(right)) { left--; right; } return right - left - 1; }中心扩展法的时间复杂度是O(n²)空间复杂度是O(1)在笔试中是完全可以接受的。如果你能额外提到Manacher算法可以优化到O(n)但实现较复杂、笔试时不推荐写也能展示知识面的广度。6.3 编程题中的边界条件与代码规范编程题真正拉开差距的往往不是算法本身而是边界条件的处理。很多人的代码思路完全正确但因为没处理空值、数组越界、极值等边界情况在通过测试用例时不断失败。我复盘2019年校招候选人常见失分点时发现丢分集中在以下几个方面没有处理输入为null、长度为0的情况数组访问时没有检查索引范围递归函数没有考虑深度过大的栈溢出风险使用Java时没有考虑int类型溢出问题比如计算两个整数之和时使用加法而非Math.addExact或提前转换类型排序结果没有考虑稳定性要求写编程题时建议遵循一个固定套路先写方法签名和空值判断再写核心逻辑最后写边界条件注释。代码注释不需要多但关键步骤一定要写明尤其是对算法复杂度的说明这会让阅卷人觉得你思维严谨。7. 笔试复盘与独家备考建议7.1 刷题之外更重要的是整理错题很多备考同学有一个误区认为刷题数量等于掌握程度。但我见过大量刷了几百道题却在笔试中仍然失利的案例原因在于他们只是机械地看答案没有把同类题目的规律和方法总结出来。我的建议是准备一个错题本按照数据结构、算法、操作系统、网络、Java、Android六大类分类整理每道错题至少记录三部分内容题目描述、我的错误思路、最优解思路和代码。整理错题本时要强迫自己用一句话概括每道题的核心考点。比如遇到“求数组中两个数之和等于目标值的下标”时应该写下“核心考点是空间换时间用HashMap在O(n)时间内完成匹配”。这样二次复习时能快速回忆起核心方法不需要重新看完整道题。7.2 安卓基础知识的复习路径安卓基础部分的复习不需要看太多视频教程最重要的是阅读官方文档和源码。关于Activity、Service、ContentProvider、BroadcastReceiver这四大组件要能做到不看文档就能说出其生命周期方法和典型使用场景。更深一层还应该自己看一遍AOSP源码中的相关类比如ActivityThread、ActivityTaskManager、Handler等虽然2019年面试时很多候选人还没养成读源码的习惯但那些拿到Offer的人几乎都有过读源码的经历。关于Handler机制我建议按照这个顺序来阅读源码可以比较清晰地建立认知先看Handler的源码找到sendMessage、dispatchMessage、handleMessage的调用链再看Looper的loop方法找到死循环中Message的取出和分发逻辑最后看MessageQueue的next方法理解nativePollOnce和nativeWake的作用这三个类读完一遍之后你再看任何关于Handler的面试题都不会心虚。即使记不住源码细节也能用自己的话把核心流程描述清楚。7.3 编程题的应试技巧笔试编程题和平时开发最大的区别在于没有IDE的代码补全和提示并且需要在线判题环境通过用例。很多人在本地IDE里写代码非常流畅但一到在线笔试平台上就经常犯低级错误比如类名写错、没有导出公共类、方法签名不对等等。在线编程时建议先审题再动手不要盲目写完才发现题意理解错了。写完后一定要先在本地或者脑中跑一遍示例用例再检查边界条件最后再提交。如果在判断环境上遇到编译错误优先检查import语句是否有遗漏Java中常见的Arrays.sort、List、Map等都需要显式导入。还有一个细节是输入输出格式的问题。在线判题往往使用标准输入输出有些同学不熟悉BufferedReader读取数据的写法导致卡在输入解析上。建议提前练习几次牛客网、力扣的笔试模式把输入输出的解析代码熟练背下来特别是以while (in.hasNext())为主的多组数据处理情况。7.4 笔试之后的技术栈规划通过了笔试之后面试环节通常会更关注项目细节、对技术的热情和深度笔试中学习到的知识对整个求职过程都非常重要。如果笔试发现自己安卓基础题丢分比较大建议在面试前多看几遍官方文档把四大组件、Handler、自定义View、性能优化这几个核心专题重新梳理一遍。如果笔试中编程题发挥不好说明算法底子还需要加强建议集中刷题而不是零散刷题每天保持2到3道有质量的算法题即可配合错题本反复复习。我在2019年带过的一个学弟就是笔试编程题只做出来一道后来他花了一个月时间集中复习链表、二叉树、动态规划三类高频题型最后成功拿到了Offer。8. 写在最后复盘完这套小米2019秋招安卓开发笔试题我的整体感受是它不追求偏题怪题而是把安卓开发最基本的几块核心能力翻来覆去地考。Java集合、并发、JVM、四大组件、Handler、自定义View、网络框架、架构演进、算法手写这些知识点贯穿了笔试的每一个环节。死记硬背的答案在这儿作用不大关键是对原理的理解和对代码的掌控力。我后来再看这套题时发现里面的很多考点到现在依然是面试官喜欢问的内容比如HashMap在1.8的变化、Handler的内存泄漏、OkHttp的拦截器链、MVVM和MVP的对比这些底层原理不会因为技术栈的升级而失效。如果你能把这套卷子里的每道题都做到不仅会做、还能讲清楚为什么你的安卓基础就已经超过了绝大多数竞争者。最后分享一个小技巧做笔试复盘时不要只看重做对了多少题把每道错题都当成一次查漏补缺的机会找到背后的知识盲区然后针对性地补齐。真正拉开校招差距的往往不是你有多大天赋而是你愿意在别人看不到的地方下多少功夫。
返回列表