備:從背題到能力映射的系統(tǒng)復(fù)習(xí)指南)
聊 Android 面試這件事我踩過的坑大概比很多人想象得要多。前幾年我自己跳槽那陣子簡歷投出去石沉大海復(fù)盤時(shí)才發(fā)現(xiàn)問題根本不在題背得夠不夠多而在我講不清楚一件很樸素的事——這個(gè)方案當(dāng)初為什么選它。后來我陸續(xù)幫幾個(gè)學(xué)弟學(xué)妹做過模擬面試去年秋天一個(gè)準(zhǔn)備轉(zhuǎn) Android 方向的學(xué)妹拿到了心儀的大廠 offer把她三個(gè)多月整理的復(fù)習(xí)筆記丟給我看我才意識到真正有效的 Android 面試準(zhǔn)備是一套能力映射的工程而不是一份題目清單。這份筆記加上我自己這些年做技術(shù)面試官的經(jīng)驗(yàn)基本覆蓋了一個(gè) Android 崗位從簡歷篩選到終面的大部分考察面。不管你是剛學(xué)完 Android Studio 環(huán)境搭建、能跑通一個(gè)增刪改查的 App還是已經(jīng)寫了兩三年業(yè)務(wù)代碼想更進(jìn)一步下面這些內(nèi)容都能直接拿去對照補(bǔ)漏。我不會給你一份必背一百題而是按面試官到底在聽什么這個(gè)角度把每一塊該講的深度都拆開。1. 先別急著背題Android 面試真正篩的是什么能力1.1 面試官手里那張看不見的評分表絕大多數(shù)人準(zhǔn)備面試的方式是打開一個(gè)題庫文檔從第一題開始往下刷。這個(gè)方式不是沒用但效率極低因?yàn)樗僭O(shè)了問題和能力是一一對應(yīng)的。實(shí)際上面試官手里通常是一張相對模糊的評分表大致分成四欄基礎(chǔ)扎實(shí)度、工程判斷力、表達(dá)與溝通、成長潛力。同一道說說 Handler 機(jī)制的題不同的人答出來落點(diǎn)可能完全不同——有人背出了 MessageQueue 和 Looper有人能講清楚主線程為什么阻塞在 native 層的 epoll 上卻不觸發(fā) ANR還有人能順帶說出自己在項(xiàng)目里怎么用 IdleHandler 做延遲初始化。這三個(gè)答案在評分表上的位置是遞增的。我后來跟那位學(xué)妹聊她面試中的實(shí)際感受她說最有用的一個(gè)轉(zhuǎn)變是把我要答對這道題改成我要讓面試官相信我能獨(dú)立負(fù)責(zé)一個(gè)模塊。這個(gè)轉(zhuǎn)變聽起來虛但落地方式很具體每講一個(gè)知識點(diǎn)盡量帶上三個(gè)東西——它解決什么問題、它內(nèi)部大致怎么實(shí)現(xiàn)、我在真實(shí)項(xiàng)目里用它踩過什么坑。前兩個(gè)靠讀第三個(gè)只能靠做和復(fù)盤。如果你沒有足夠的項(xiàng)目經(jīng)歷去支撐第三點(diǎn)那就用我讀源碼時(shí)發(fā)現(xiàn)的或者我在一個(gè)小 demo 里驗(yàn)證過的來替代至少證明你是動(dòng)手驗(yàn)證過的人而不是復(fù)述文檔的人。1.2 三類問題的時(shí)間配比與準(zhǔn)備順序我把 Android 崗位的面試問題粗分成三類它們對應(yīng)完全不同的準(zhǔn)備方式混在一起準(zhǔn)備是最容易浪費(fèi)時(shí)間的。問題類型典型形式考察重點(diǎn)建議投入精力基礎(chǔ)原理類Java/Kotlin 語法、Handler、Binder、View 繪制概念是否成體系有沒有自相矛盾40%工程實(shí)踐類性能優(yōu)化、崩潰排查、架構(gòu)選型有沒有量化意識和取舍能力40%場景與手撕算法題、設(shè)計(jì)題、現(xiàn)場寫代碼編碼基本功、思維清晰度20%很多人把 80% 的時(shí)間砸在第一類上結(jié)果被問到你那個(gè)啟動(dòng)優(yōu)化到底省了多少毫秒、怎么測的就啞了。反過來說工程實(shí)踐類是最能拉開差距的地方因?yàn)樗茈y臨時(shí)背出來。我的建議順序是先用一兩周把基礎(chǔ)原理的骨架搭起來然后立刻轉(zhuǎn)到自己項(xiàng)目里找兩三個(gè)真實(shí)問題深挖最后臨考前兩周集中刷手撕題保持手感。1.3 我見過的兩種典型錯(cuò)誤準(zhǔn)備方式第一種是只背結(jié)論不留推導(dǎo)。比如你背下Android 的 GC 是并發(fā)復(fù)制收集器面試官追問一句那它和 HotSpot 的分代收集有什么區(qū)別為什么要這么設(shè)計(jì)你就卡住了。結(jié)論是可以查到的推導(dǎo)過程才能證明你理解。第二種是過度追求廣度。有人會去翻一大堆冷門 API覺得覆蓋面廣就穩(wěn)。但面試官通常只在他自己熟悉的領(lǐng)域深挖你把十個(gè)方向都講得浮在表面不如把三四個(gè)方向講到能畫圖、能寫代碼、能說出邊界條件。我個(gè)人的經(jīng)驗(yàn)是準(zhǔn)備二十個(gè)能講五分鐘的話題比準(zhǔn)備兩百個(gè)能講三十秒的話題要有效得多。2. Java 與 Kotlin從能用講到為什么這么設(shè)計(jì)2.1 Android 上的運(yùn)行時(shí)和標(biāo)準(zhǔn) JVM 不是一回事這是被低估的一個(gè)話題。你如果只說Java 靠 JVM 跑有垃圾回收那基本等于沒說。Android 從 4.4 之后逐步切到 ART運(yùn)行方式在 AOT 和 JIT 之間做過好幾輪調(diào)整理解這條演進(jìn)線很多性能問題就自然解釋得通了。最早 Dalvik 是解釋執(zhí)行加 JIT安裝快、運(yùn)行慢所以早期 Android 手機(jī)裝個(gè) App 要等很久的優(yōu)化中其實(shí)是在做 dexopt。ART 上來之后改成安裝時(shí)全量 AOT運(yùn)行快但安裝慢、占空間大。再往后引入 profile 引導(dǎo)的混合編譯先解釋執(zhí)行把熱點(diǎn)方法記錄下來設(shè)備空閑時(shí)后臺編譯這些熱點(diǎn)這樣就兼顧了安裝速度和運(yùn)行效率。你如果能把這幾個(gè)階段和為什么要這么改講清楚面試官對你的評價(jià)會立刻不一樣。GC 也是同理。早期是標(biāo)記清除加標(biāo)記整理容易產(chǎn)生碎片和長時(shí)間停頓后來換成并發(fā)復(fù)制的方案把內(nèi)存分成多個(gè)區(qū)域并行回收停頓時(shí)間明顯縮短再后面引入分代思路針對大部分對象朝生夕死這個(gè)特點(diǎn)做優(yōu)化。這背后其實(shí)就是一句話移動(dòng)端的 GC 目標(biāo)不是吞吐量而是盡可能短的卡頓。你把這句話講出來比背一堆參數(shù)有用。2.2 線程池參數(shù)到底怎么算線程池幾乎是必問題但多數(shù)人停在核心線程數(shù)、最大線程數(shù)、隊(duì)列、拒絕策略這四個(gè)名詞上。面試官真正想聽的是你憑什么定這組參數(shù)。先說參數(shù)含義。任務(wù)進(jìn)來時(shí)如果當(dāng)前線程數(shù)小于核心線程數(shù)直接開新線程否則丟進(jìn)阻塞隊(duì)列隊(duì)列滿了再嘗試把線程數(shù)擴(kuò)到最大值還是滿了就走拒絕策略。這里面最關(guān)鍵、也最容易出問題的是隊(duì)列的選擇。用無界隊(duì)列的話最大線程數(shù)這個(gè)參數(shù)基本是廢的因?yàn)殛?duì)列永遠(yuǎn)不