阿里架構師最新 Android 面試點梳理,我收藏了你呢?

目錄:

  • 網絡:分層模型、TCP、UDP、HTTP、HTTPS
  • 算法:數據結構、常用算法
  • Java 基礎:StringBuilder、泛型擦除、Exception、IO、容器
  • Java 同步:volatile、wait、synchronized、可重入鎖、樂觀鎖、死鎖
  • Java 設計模式:六大原則、23 種設計模式、動態代理
  • Java 虛擬機:內存模型、內存結構、GC、四種引用、ClassLoader
  • Android 基礎:Activity、View 繪製、動畫、Window、SurfaceView、事件分發
  • Android 通信:Handler、Parcelable、IPC、Binder
  • Android 系統:系統架構、Dalvik、ART、系統啓動、類加載器、Apk 打包、Apk 安裝
  • Android 優化:網絡優化、卡頓優化、內存優化、瘦包、內存泄漏、ANR、Native Crash
  • 其他:解析 XML、進程保活、播放器、Lint、CI、CD、AOP、JetPack

小編已將這些Android面試知識點整理成了文檔形式,如果大家有需要的話可以私信我【666】獲取這個面試知識點大禮包,可看文末圖;

網絡:分層模型、TCP、UDP、HTTP、HTTPS

分層模型

  • 應用層:負責處理特定的應用程序細節,如 HTTP、FTP、DNS
  • 運輸層:爲兩臺主機提供端到端的基礎通信,如 TCP、UDP
  • 網絡層:控制分組傳輸、路由選擇等,如 IP
  • 鏈路層:操作系統設備驅動程序、網卡相關接口

UDP

  • UDP 頭結構:來源端口、目的端口、長度域、校驗和
  • 特點:不可靠、無序、面向報文、速度快、輕量
  • 適用場景:適用於即時通訊、視頻通話等
  • 應用:DHCP、DNS、QUCI、VXLAN、GTP-U、TFTP、SNMP

TCP

  • TCP 頭結構:來源端口、目的端口、序號、確認序號、SYN/ACK 等狀態位、窗口大小、校驗和、緊急指針
  • 特點:面向字節流、有擁塞和流量控制、可靠、有序、速度慢、較重量,通過滑動窗口實現流量控制、用塞控制
  • 適用場景:文件傳輸、瀏覽器等
  • 應用:HTTP、HTTPS、RTMP、FTP、SMTP、POP3
  • 三次握手:
1. C->S:SYN,seq=x(你能聽到嗎?)
2. S->C:SYN,seq=y,ack=x+1(我能聽到,你能聽到嗎?)
3. C->S:ACK,seq=x+1,ack=y+1(我能聽到,開始吧)

兩方都要能確保:我說的話,你能聽到;你說的話,我能聽到。所以需要三次握手
  • 四次揮手:
1. C->S:FIN,seq=p(我說完了)
2. S->C:ACK,ack=p+1(我知道了,等一下,我可能還沒說完)
3. S->C:FIN,seq=q,ACK,ack=p+1(我也說完了)
4. C->S:ACK,ack=q+1(我知道了,結束吧)

S 收到 C 結束的消息後 S 可能還沒說完,沒法立即回覆結束標示,只能等說完後再告訴 C :我說完了

HTTP

  • 超文本傳輸協議,明文傳輸,默認 80 端口
  • POST 和 GET:Get 參數放在 url 中;Post 參數放在 request Body 中
  • 訪問網頁過程:DNS 域名解析、TCP 三次握手建立連接、發起 HTTP 請求

HTTPS

  • 默認 443 端口,使用 SSL 協議對 HTTP 傳輸數據進行了加密,安全
  • 加密過程:Client/Server 通過非對稱加密生成密鑰,然後用這個密鑰去對稱加密傳輸數據

算法:數據結構、常用算法

數據結構

  • 數組、鏈表
  • 棧、隊列
  • 散列表
  • 樹、堆、圖

常用算法

  • 排序
  • 雙指針、滑動窗口、字符串
  • 遞歸、分治、二分
  • 回溯、貪心、動態規劃

Java 基礎:StringBuilder、泛型擦除、Exception、IO、容器

StringBuilder

  • StringBuffer 線程安全,StringBuilder 線程不安全
  • 實際上是用 StringBuilder 來實現的,所以非循環體可以直接用 +,循環體不行,因爲會頻繁創建 StringBuilder
  • String.concat 實質是 new String ,效率也低,耗時排序:StringBuilder < StringBuffer < concat < +

泛型擦除

  • 修飾成員變量等類結構相關的泛型不會被擦除
  • 容器類泛型會被擦除

Exception 和 Error

  • Exception 和 Error 都繼承自 Throwable
  • Error 大部分是指不可恢復的錯誤狀態,比如 OOM,所以也不需要捕獲
  • Exception 分爲 CheckedException 和 UnCheckedException CheckedException:必須顯式捕獲,受編譯器檢查,比如 io 操作 UnCheckedException:不用顯示捕獲,比如空指針、數組越界等

IO 、 NIO、 OKIO

  • IO 是面向流的,一次一個字節的處理,NIO 是面向緩衝區的,一次產生或消費一個數據塊
  • IO 是阻塞的,NIO 是非阻塞的
  • NIO 支持內存映射方式
  • okio 相比 io 和 nio,api 更簡單易用
  • okio 支持超時機制
  • okio 引入 ByteString 空間換時間提高性能
  • okio 採用 segment 機制進行內存共享,節省 copy 時間消耗

ArrayList、LinkedList

  • ArrayList 基於數組實現,查找快:o(1),增刪慢:o(n) 初始容量爲10,擴容通過 System.arrayCopy 方法
  • LinkedList 基於雙向鏈表實現,查找慢:o(n),增刪快:o(1) 封裝了隊列和棧的調用

HashMap 、HashTable、HashSet

  • HashMap(允許 key/value 爲 null) 基於數組和單向鏈表實現,數組是 HashMap 的主體;鏈表是爲解決哈希衝突而存在的,存放的是key和value結合的實體 數組索引通過 key.hashCode(還會二次 hash) 得到,在鏈表上通過 key.equals 索引 哈希衝突落在同一個桶中時,直接放在鏈表頭部(java1.8後放到尾部) JAVA 8 中鏈表數量大於 8 時會轉爲紅黑樹存儲,查找時間由 O(n) 變爲 O(logn) 數組長度總是2的n次方:這樣就能通過位運算實現取餘,從而讓 index 能落在數組長度範圍內 加載因子(默認0.75)表示添加到多少填充比時進行擴容,填充比大:鏈表較長,查找慢;填充比小:鏈表短,查找快 擴容時直接創建原數組兩倍的長度,然後將原有對象再進行hash找到新的index,重新放
  • HashTable(不允許 key/value 爲 null) 數據結構和 HashMap 一樣 線程安全
  • HashSet 基於 HashMap 實現,元素就是 HashMap 的 key,Value 傳入了一個固定值

ArrayMap、SparseArray

  • ArrayMap 基於兩個數組實現,一個存放 hash;一個存放鍵值對 存放 hash 的數組是有序的,查找時使用二分法查找 發生哈希衝突時鍵值對數組裏連續存放,查找時也是通過 key.equals索引,找不到時先向後再向前遍歷相同hash值的鍵值對數組 擴容時不像 HashMap 直接 double,內存利用率高;也不需要重建哈希表,只需要調用 system.arraycopy 數組拷貝,性能較高 不適合存大量數據(1000以下),因爲數據量大的時候二分查找相比紅黑樹會慢很多
  • SparseArray 基於 ArrayMap,key 只能是特定類型

Concurrent 集合

  • ConcurrentHashMap 數據結構跟 HashMap 一樣,還是數組加鏈表 採用 segment 分段鎖技術,不像 HashTable 無腦直接同步 put 和 get 操作 get 操作沒有加鎖,因爲 value 用 volatile 修飾來保證可見行,性能很高 java1.8 後去除分段鎖,採用 CAS 樂觀鎖加 synchronized 來實現

LRUCache 原理

  • 基於訪問順序排序的 LinkedHashMap 實現,最近訪問的會排在最後

Java 同步:volatile、wait、synchronized、可重入鎖、樂觀鎖、死鎖

volatile 關鍵字

  • 只能用來修飾變量,適用修飾可能被多線程同時訪問的變量
  • 相當於輕量級的 synchronized,volatitle 能保證有序性(禁用指令重排序)、可見性
  • 變量位於主內存中,每個線程還有自己的工作內存,變量在自己線程的工作內存中有份拷貝,線程直接操作的是這個拷貝
  • 被 volatile 修飾的變量改變後會立即同步到主內存,保持變量的可見性
  • 雙重檢查單例,爲什麼要加 violate? volatile想要解決的問題是,在另一個線程中想要使用instance,發現instance!=null,但是實際上instance還未初始化完畢這個問題。將instance = newInstance();拆分爲3句話是。1.分配內存2.初始化3.將instance指向分配的內存空間,volatile可以禁止指令重排序,確保先執行2,後執行3

wait 和 sleep

  • sleep 是 Thread 的靜態方法,可以在任何地方調用
  • wait 是 Object 的成員方法,只能在 synchronized 代碼塊中調用,否則會報 IllegalMonitorStateException 非法監控狀態異常
  • sleep 不會釋放共享資源鎖,wait 會釋放共享資源鎖

wait、notify、notifyAll

  • 鎖池:某個對象的鎖已被線程A擁有,其他線程要執行該對象的 synchronized 方法獲取鎖時就會進入該對象的鎖池,鎖池中的線程回去競爭該對象的鎖
  • 等待池:某個線程調用了某個對象的 wait 方法,該線程就會釋放該對象的鎖,進入該對象的等待池,等待池中的線程不會去競爭該對象的鎖
  • 調用 notify 會隨機喚醒等待池中的一個線程,喚醒後會進入到鎖池
  • 調用 notifyAll 會喚醒等待池中的所有線程,喚醒後會都進入到鎖池

lock 和 synchronized

  • synchronized 是 Java 關鍵字,內置特性;Lock 是一個接口
  • synchronized 會自動釋放鎖;lock 需要手動釋放,所以需要寫到 try catch 塊中並在 finally 中釋放鎖
  • synchronized 無法中斷等待鎖;lock 可以中斷
  • Lock 可以提高多個線程進行讀/寫操作的效率
  • 競爭資源激烈時,lock 的性能會明顯的優於 synchronized

Synchronized 原理

  • 每個對象都有一個監視器鎖:monitor,同步代碼塊會執行 monitorenter 開始,motnitorexit 結束
  • Wait/notify 就依賴 monitor 監視器,所以在非同步代碼塊中執行會報 IllegalMonitorStateException 異常

可重入鎖

  • 定義:已經獲取到鎖後,再次調用同步代碼塊/嘗試獲取鎖時不必重新去申請鎖,可以直接執行相關代碼
  • ReentrantLock 和 synchronized 都是可重入鎖

公平鎖

  • 定義:等待時間最久的線程會優先獲得鎖
  • 非公平鎖無法保證哪個線程獲取到鎖,synchronized 就是非公平鎖
  • ReentrantLock 默認時非公平鎖,可以設置爲公平鎖

樂觀鎖和悲觀鎖

  • 悲觀鎖:線程一旦得到鎖,其他線程就掛起等待,適用於寫入操作頻繁的場景;synchronized 就是悲觀鎖
  • 樂觀鎖:假設沒有衝突,不加鎖,更新數據時判斷該數據是否過期,過期的話則不進行數據更新,適用於讀取操作頻繁的場景
  • 樂觀鎖 CAS:Compare And Swap,更新數據時先比較原值是否相等,不相等則表示數據過去,不進行數據更新
  • 樂觀鎖實現:AtomicInteger、AtomicLong、AtomicBoolean

死鎖 4 個必要條件

  • 互斥
  • 佔有且等待
  • 不可搶佔
  • 循環等待

Java 設計模式:六大原則、23 種設計模式、動態代理

六大原則

  • 開閉原則:對拓展開放,對修改關閉
  • 單一指責原則:一個類指責單一
  • 里氏替換原則:引用基類的地方都能替換成子類對象
  • 依賴倒置原則:高層次模塊不依賴低層次模塊的具體實現,抽象不應該依賴細節
  • 接口隔離原則:類之間的依賴關係應該建立在最小的接口上
  • 迪米特原則:一個對象對其他對象應該有儘量少的瞭解

Java 23 種設計模式(按目的分類爲:5+7+11)

1995 年 GoF(四人組)出了一本設計模式的書,收錄了 23 種設計模式,樹立設計模式里程碑,也叫:GoF 設計模式

  • 創建型(5):描述怎麼創建對象
  1. 單例模式
  2. 原型模式:對象的拷貝
  3. 建造者模式
  4. 工廠模式:建立一個工廠方法來製造新的對象
  5. 抽象工廠模式
  • 結構型(7):描述如何將類或對象按某種規則組成更大的結構 1.橋接模式:對於兩個或以上緯度獨立變化的場景,將抽象與具體實現分離,實例:用不同顏色畫不同形狀 2.外觀模式:對外有一個統一接口,外部不用關心內部子系統的具體實現,這是"迪米特原則"的典型應用 3.適配器模式:改變類的接口,使原本由於接口不匹配而無法一起工作的兩個類能夠在一工作,實例:RecycleView 的 Adapter 不管什麼類型的 View 都返回 ViewHolder 4.代理模式:由代理對象控制對原對象的引用,包括靜態代理和動態代理 5.組合模式:將對象組成樹形結構,用於對單個對象和組合對象的使用具有一致性,實例:ViewGroup 6.裝飾模式:對對象包裝一層,動態的增加一些額外功能,實例:ContextWrapper 包裝 Context 7.享元模式:複用對象,實例:java 的常量池(比如 String),線程池,Message.obtain 等
  • 行爲型(11):描述類或對象之間怎麼相互協作,怎樣分配指責
  1. 觀察者模式:一對多依賴關係,多個觀察者可以同時監聽某一個對象,實例:jetpack 的 lifeCycle 添加生命週期觀察者
  2. 中介者模式:定義一箇中介對象封裝一系列對象的交互,解耦這些對象,實例:MVP 的 P
  3. 訪問者模式:將作用於某數據結構中各元素的操作分離出來封裝成獨立的類,對這些元素添加新的操作,但不改變原數據結構,實例:asm 中的 classVisitor 中再分別對類註解、變量、方法等進行處理
  4. 狀態模式:行爲由狀態決定,不同狀態下由不同行爲,與策略模式類似,實例:不同狀態下有同一種操作的不同行爲的子類實現
  5. 命令模式:將一個請求封裝爲一個對象發出,交給別的對象去處理請求,實例:Handler 發送定義好的消息事件
  6. 策略模式:將一系列的算法封裝起來,方便替換,實例:動畫的時間插值器
  7. 責任鏈模式:讓多個對象都有機會處理一個事件,實例:View 事件傳遞機制
  8. 備忘錄模式:保存對象之前的狀態,方便後面恢復
  9. 迭代器模式:提供一種方法遍歷容器中的元素,而不需要暴露該對象的內部表示,實例:集合的迭代器
  10. 解釋器模式:多次出現的問題有一定規律,就可以歸納成一種簡單的語言來解釋,實例:AndroidManifest 文件、GLES 着色器語言
  11. 模版方法模式:定義一套固定步驟,方便直接執行,實例:AsyncTask

動態代理原理及實現

  • InvocationHandler 接口,動態代理類需要實現這個接口
  • Proxy.newProxyInstance,用於動態創建代理對象
  • Retrofit 應用: Retrofit 通過動態代理,爲我們定義的請求接口都生成一個動態代理對象,實現請求

JVM:內存模型、內存結構、GC、四種引用、ClassLoader

JVM

  • 定義:可以理解成一個虛構的計算機,解釋自己的字節碼指令集映射到本地 CPU 或 OS 的指令集,上層只需關注 Class 文件,與操作系統無關,實現跨平臺
  • Kotlin 就是能解釋成 Class 文件,所以可以跑在 JVM 上

JVM 內存模型

  • Java 多線程之間是通過共享內存來通信的,每個線程都有自己的本地內存
  • 共享變量存放於主內存中,線程會拷貝一份共享變量到本地內存
  • volatile 關鍵字就是給內存模型服務的,用來保證內存可見性和順序性

JVM 內存結構

  • 線程私有: 1.程序計數器:記錄正在執行的字節碼指令地址,若正在執行 Native 方法則爲空 2.虛擬機棧:執行方法時把方法所需數據存爲一個棧幀入棧,執行完後出棧 3.本地方法棧:同虛擬機棧,但是針對的是 Native 方法
  • 線程共享: 1.堆:存儲 Java 實例,GC 主要區域,分代收集 GC 方法會吧堆劃分爲新生代、老年代 2.方法區:存儲類信息,常量池,靜態變量等數據

GC

  • 回收區域:只針對堆、方法區;線程私有區域數據會隨線程結束銷燬,不用回收
  • 回收類型: 1.堆中的對象:分代收集 GC 方法會吧堆劃分爲新生代、老年代。 新生代:新建小對象會進入新生代;通過複製算法回收對象;老年代:新建大對象及老對象會進入老年代;通過標記-清除算法回收對象。 2.方法區中的類信息、常量池
  • 判斷一個對象是否可被回收: 1.引用計數法:有循環引用的缺點 2.可達性分析法:從 GC ROOT 開始搜索,不可達的對象都是可以被回收的。其中 GC ROOT 包括虛擬機棧/本地方法棧中引用的對象、方法區中常量/靜態變量引用的對象。

Minor GC/Major GC/Full GC

  • Minor GC(Young GC):即新生代(分爲一個 Eden 區和兩個 Survivor 區)的垃圾回收 Eden 區無用對象被回收,存活對象會移到 Survivor 區 Survivor 區的存活對象會被複制到另一個 Survivor 區,複製次數也記做年齡,年齡足夠大時(15)會移到老年代 如果 Survivor 區已滿,則存活對象會被提前移動到老年代(過早提升),如果老年代也無法容納,則會觸發 Full GC(提升失敗) 老年代的對象可能引用新生代對象,所以這個引用會被作爲 GC Roots
  • Major GC:通常是跟 Full GC 等價的,回收整個堆
  • Full GC:回收整個堆,包括新生代和老年代 當要在老年代分配空間但無法容納時觸發 當主動調用 System.gc 時觸發

四種引用

  • 強引用:不會被回收
  • 軟引用:內存不足時會被回收
  • 弱引用:gc 時會被回收
  • 虛引用:無法通過虛引用得到對象,可以監聽對象的回收

ClassLoader

  • 類的生命週期:
  1. 加載;
  2. 驗證;
  3. 準備;
  4. 解析;
  5. 初始化;
  6. 使用;
  7. 卸載
  • 類加載過程:
  1. 加載:獲取類的二進制字節流;生成方法區的運行時存儲結構;在內存中生成 Class 對象
  2. 驗證:確保該 Class 字節流符合虛擬機要求
  3. 準備:初始化靜態變量
  4. 解析:將常量池的符號引用替換爲直接引用
  5. 初始化:執行靜態塊代碼、類變量賦值
  • 類加載時機:
  1. 實例化對象
  2. 調用類的靜態方法
  3. 調用類的靜態變量(放入常量池的常量除外)
  • 類加載器:負責加載 class 文件
  1. 引導類加載器 - 沒有父類加載器
  2. 拓展類加載器 - 繼承自引導類加載器
  3. 系統類加載器 - 繼承自拓展類加載器
  • 雙親委託模型: 當要加載一個 class 時,會先逐層向上讓父加載器先加載,加載失敗纔會自己加載 爲什麼叫雙親?不考慮自定義加載器,系統類加載器需要網上詢問兩層,所以叫雙親 判斷是否是同一個類時,除了類信息,還必須時同一個類加載器 優點:防止重複加載,父加載器加載過了就沒必要加載了;安全,防止篡改核心庫類

Android 基礎:Activity、View 繪製、動畫、Window、SurfaceView、事件分發

Activity 生命週期

  • A 打開 B 界面,會先執行 A 的 onPause,再執行 B 的 onCreate、onStart、onResume,再執行 A 的 onStop
  • B 界面的打開依賴 A 界面 onPause 方法執行完,所以不要在 onPause 中做耗時操作

Activity 啓動模式

  • standard 標準模式
  • singleTop 棧頂複用模式,適用於推送點擊消息界面
  • singleTask 棧內複用模式,適用於 App 首頁
  • singleInstance 單例模式,單獨位於一個任務棧中,適用於撥打電話界面
  • 細節: taskAffinity:任務相關性,用於指定任務棧名稱,默認爲應用包名 allowTaskReparenting:允許轉移任務棧

View 工作原理

  • ViewRoot 的 performTraversals 方法調用觸發開始 View 的繪製,然後會依次調用: performMeasure:遍歷 View 的 measure 測量尺寸 performLayout:遍歷 View 的 layout 確定位置 performDraw:遍歷 View 的 draw 繪製

MeasureSpec 測量規則

  • EXACTLY:父 View 指定了子 View 確切的大小
  • AT_MOST:父 View 指定一個大小,子 View 不能超過這個值
  • UNSPECIFIEND: 父 View 不對子 View 有任何限制

View 動畫、幀動畫及屬性動畫

  • View 動畫: 作用對象是 View,可用 xml 定義,建議 xml 實現比較易讀 支持四種效果:平移、縮放、旋轉、透明度
  • 幀動畫: 通過 AnimationDrawable 實現,容易 OOM
  • 屬性動畫: 可作用於任何對象,可用 xml 定義,Android 3 引入,建議代碼實現比較靈活 包括 ObjectAnimator、ValuetAnimator、AnimatorSet 時間插值器:根據時間流逝的百分比計算當前屬性改變的百分比,系統預置勻速、加速、減速等插值器 類型估值器:根據當前屬性改變的百分比計算改變後的屬性值,系統預置整型、浮點、色值等類型估值器 使用注意事項:避免使用幀動畫,容易OOM;界面銷燬時停止動畫,避免內存泄漏;開啓硬件加速,提高動畫流暢性 硬件加速原理:將 cpu 一部分工作分擔給 gpu ,使用 gpu 完成繪製工作;從工作分攤和繪製機制兩個方面優化了繪製速度

Window 、WindowManager、WMS、SurfaceFlinger

  • WIndow:抽象概念不是實際存在的,而是以 View 的形式存在,通過 PhoneWindow 實現
  • WindowManager:外界訪問 Window 的入口,內部與 WMS 交互是個 IPC 過程
  • WMS:管理窗口 Surface 的佈局和次序,作爲系統級服務單獨運行在一個進程
  • SurfaceFlinger:將 WMS 維護的窗口按一定次序混合後顯示到屏幕上

SurfaceView、TextureView、SurfaceTexture、GLSurfaceView

  • SurfaceView:使用雙緩衝機制,有自己的 surface,在一個獨立的線程裏繪製,Android7.0之前不能平移、縮放
  • TextureView:持有 SurfaceTexture,將圖像處理爲 OpenGL 紋理更新到 HardwareLayer,必須開啓硬件加速,Android5.0之前在主線程渲染,之後有獨立的渲染線程,可以平移、旋轉、縮放
  • SurfaceTexture:將圖像流轉爲 OpenGL 外部紋理,不直接顯示
  • GLSurfaceView:加入 EGL 管理,自帶 GL 上下文和 GL 渲染線程

事件分發機制

  • 一個 MotionEvent 產生後,按 Activity -> Window -> decorView -> View 順序傳遞,View 傳遞過程就是事件分發,主要依賴三個方法:
  • dispatchTouchEvent:用於分發事件,只要接受到點擊事件就會被調用,返回結果表示是否消耗了當前事件
  • onInterceptTouchEvent:用於判斷是否攔截事件,當 ViewGroup 確定要攔截事件後,該事件序列都不會再觸發調用此 ViewGroup 的 onIntercept
  • onTouchEvent:用於處理事件,返回結果表示是否處理了當前事件,未處理則傳遞給父容器處理
  • 細節: 一個事件序列只能被一個 View 攔截且消耗 View 沒有 onIntercept 方法,直接調用 onTouchEvent 處理 OnTouchListener 優先級比 OnTouchEvent 高,onClickListener 優先級最低 requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercept 方法的調用

Android 通信:Handler、Parcelable、IPC、Binder

Handler、MessageQueue、Looper 及 postDelayed 原理

  • Handler:開發直接接觸的類,內部持有 MessageQueue 和 Looper
  • MessageQueue:消息隊列,內部通過單鏈表存儲消息
  • Looper:內部持有 MessageQueue,循環查看是否有新消息,有就處理,沒就阻塞
  • postDelayed 其實就是調用 postAtTime 實現的,傳入的時間戳基於 SystemClock.uptimeMillis,即 boot 時間
  • 進一步會調用 MessageQueue#enqueueMessage 將消息插入到隊列
  • 插入消息時會根據消息執行時刻 Message#when 來決定插入到什麼位置,when 爲 0 或最早執行就會插入到鏈表頭,否則按執行時刻排序插入
  • 插入後如果正在阻塞則會嘗試喚醒,插入到頭部則會喚醒,插入到隊列中則再根據其他條件判斷是否需要喚醒
  • Looper#loop 中調用 MessageQueue#next 取消息,next 方法除非是即將銷燬時會返回 null,否則就會返回消息,沒有消息就阻塞。如果當前時刻還沒到消息的執行時刻 when,就會再阻塞這個時間差的時間
  • 阻塞是調用 nativePollOnce 實現,基於 Linux epoll 事件管理機制
  • Looper#loop 中取出消息後通過 Message#target 拿到 handler,然後調用 Handler#dispatchMessage 分發處理消息

Serializable、Parcelable

  • Serializable :Java 序列化方式,適用於存儲和網絡傳輸,serialVersionUID 用於確定反序列化和類版本是否一致,不一致時反序列化回失敗
  • Parcelable :Android 序列化方式,適用於組件通信數據傳遞,性能高,因爲不像 Serializable 一樣有大量反射操作

Linux IPC 方式

  • 管道
  • socket
  • 信號量:常作爲一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作爲進程間以及同一進程內不同線程之間的同步手段
  • 信號:不適用於信息交換,更適用於進程中斷控制,比如非法內存訪問,殺死某個進程等(Android 中的 Kill Process 採用的就是 signal(信號)機制)
  • 消息隊列:信息複製兩次,額外的 CPU 消耗;不合適頻繁或信息量大的通信
  • 共享內存:無須複製,共享緩衝區直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操作系統無法實現,必須各進程利用同步工具解決

Binder

  • Android 中基於 C/S 結構的一種面向對象的進程間通信的機制
  • 主要用在 system_server 進程與上層 App 層的 IPC 交互
  • 包含:Client,Server,Binder 驅動和 ServiceManager 四部分

Android 爲什麼選擇 binder

  • 性能:使用 mmap 一次數據拷貝實現 IPC,傳統 IPC:用戶 A 空間->內核->用戶 B 空間;mmap 將內核與用戶 B 空間映射,實現直接從用戶 A 空間->用戶B空間,而 Linux 的管道、消息隊列、Socket 都需要拷貝兩次,binder 僅次於共享內存
  • 穩定性:基於C/S架構,架構清晰,穩定性好,不像共享內存實現方式複雜,需要充分考慮訪問臨界資源的併發同步問題
  • 安全:傳統Linux IPC的接收方無法獲得對方進程可靠的UID/PID,從而無法鑑別對方身份

Android IPC 方式

  • Intent extras、Bundle:要求傳遞數據能被序列化,實現 Parcelable、Serializable ,適用於四大組件通信
  • 文件共享:適用於交換簡單的數據實時性不高的場景
  • AIDL:AIDL 接口實質上是系統提供給我們可以方便實現 Binder 的工具 Android Interface Definition Language,可實現跨進程調用方法 服務端:將暴漏給客戶端的接口聲明在 AIDL 文件中,創建 Service 實現 AIDL 接口並監聽客戶端連接請求 客戶端:綁定服務端 Service ,綁定成功後拿到服務端 Binder 對象轉爲 AIDL 接口調用 RemoteCallbackList 實現跨進程接口監聽,同個 Binder 對象做 key 存儲客戶端註冊的 listener 監聽 Binder 斷開:1.Binder.linkToDeath 設置死亡代理;2. onServiceDisconnected 回調
  • Messenger:基於 AIDL 實現,服務端串行處理,主要用於傳遞消息,適用於低併發一對多通信
  • ContentProvider:基於 Binder 實現,適用於一對多進程間數據共享
  • Socket:TCP、UDP,適用於網絡數據交換

Android 系統:系統架構、Dalvik、ART、系統啓動、類加載器、Apk 打包、Apk 安裝

Android 系統架構

阿里架構師最新 Android 面試點梳理,我收藏了你呢?

Android 系統架構

  • 應用層
  • Framework 框架層
  • 本地 Native 庫和 Android 運行時環境
  • HAL
  • Linux 內核

Dalvik 和 ART

  • Dalvik 谷歌設計專用於 Android 平臺的 Java 虛擬機,可直接運行 .dex 文件,適合內存和處理速度有限的系統 JVM 指令集是基於棧的;Dalvik 指令集是基於寄存器的,代碼執行效率更優
  • ART Dalvik 每次運行都要將字節碼轉換成機器碼;ART 在應用安裝時就會轉換成機器碼,執行速度更快 ART 存儲機器碼佔用空間更大,空間換時間

Android 系統啓動流程

  • 按電源鍵 -> 加載引導程序 BootLoader 到 RAM -> 執行 BootLoader 程序啓動內核 -> 啓動 init 進程 -> 啓動 Zygote 和各種守護進程 -> 啓動 System Server 服務進程開啓 AMS、WMS 等 -> 啓動 Launcher 應用進程

Android 類加載器

  • BootClassLoader(加載 Framework 級別的類)
  • PathClassLoader(加載系統類和 data/app 應用目錄下的 dex 文件)
  • DexClassLoader(加載自定義的 dex 文件或 jar,支持從 sd 卡中進行加載)

APK 打包流程

  1. aapt 打包資源文件生成 R.java 文件;aidl 生成 java 文件
  2. 將 java 文件編譯爲 class 文件
  3. 將工程及第三方的 class 文件轉換成 dex 文件
  4. 將 dex 文件、so、編譯過的資源、原始資源等打包成 apk 文件
  5. 簽名
  6. 資源文件對齊,減少運行時內存

App 安裝過程

  • 首先要解壓 APK,資源、so等放到應用目錄
  • Dalvik 會將 dex 處理成 ODEX ;ART 會將 dex 處理成 OAT;
  • OAT 包含 dex 和安裝時編譯的機器碼

Android 優化:網絡優化、卡頓優化、內存優化、瘦包、內存泄漏、ANR、Native Crash

網絡優化及檢測

  • 速度:
  1. GZIP 壓縮(okhttp 自動支持);
  2. Protocol Buffer 替代 json;
  3. 優化圖片/文件流量;
  4. IP 直連省去 DNS 解析時間
  • 成功率:失敗重試策略;
  • 流量:
  1. GZIP 壓縮(okhttp 自動支持);
  2. Protocol Buffer 替代 json;
  3. 優化圖片/文件流量;
  4. 文件下載斷點續傳 ;
  5. 緩存
  • 協議層的優化,比如更優的 http 版本等
  • 監控:Charles 抓包、Network Monitor 監控流量

UI卡頓優化

  • 減少佈局層級及控件複雜度,避免過度繪製
  • 使用 include、merge、viewstub
  • 優化繪製過程,避免在 Draw 中頻繁創建對象、做耗時操作

內存優化

  • 內存問題 內存泄漏 內存抖動:頻繁創建臨時對象 Bitmap 大內存:規避位圖超標 代碼質量:intdef 代替枚舉,使用 SparseArray 代替 HashMap
  • 檢測工具 MAT(Memory Analysis Tools) ,可分析 Java 堆數據,可查看實例佔用空間、引用關係等 Android Studio 自帶的 Profiler LeakCanary:通過弱引用和引用隊列監控對象是否被回收,比如 Activity 銷燬時開始監控此對象,檢測到未被回收則主動 gc ,然後繼續監控

瘦包

  1. 資源方面:資源在線化、圖片使用 webp 格式、tint 着色生成不同色調的切、使用 icon font
  2. so 庫:保留一個 cpu 架構的 so 文件
  3. AS Inspect Code 清除無用代碼和資源
  4. 代碼混淆:使用 ProGuard 可以移除無用的類、字段、方法(壓縮),移除無用字節碼指令
  5. 不保留行號:使用 ProGuard 配置不保留行號
  6. 開啓 shrinkResources:移除無用資源
  7. 資源混淆:使用 AndResGuard 縮短資源長度,對資源進行 7z 壓縮等(直接對apk操作)
  8. 代碼結構簡化,比如用 intdef 代替 枚舉(一個枚舉有1~1.4kb大小)
  9. 使用 compileOnly 在只需編譯時依賴的場景,不會打到 apk 裏
  10. 使用 thinR 插件剔除 R 文件,將引用 R 字段的地方替換成對應常量
  11. Android 7.0 使用 V2(apksigner) 代替 V1(jarsigner) 簽名工具
  12. 動態加載 so 庫(System.load加載絕對路徑文件)、插件化技術、App Bundle
  13. 使用 facebook 的 redex

內存泄漏場景及規避

  • 靜態變量、單例強引跟生命週期相關的數據或資源,包括 EventBus
  • 遊標、IO 流等資源忘記主動釋放
  • 界面相關動畫在界面銷燬時及時暫停
  • 內部類持有外部類引用導致的內存泄漏 handler 內部類內存泄漏規避:
  1. 使用靜態內部類+弱引用
  2. 界面銷燬時清空消息隊列 檢測:Android Studio Profiler

ANR 問題及分析

  • anr 分類 主線程 5s 內沒有處理完輸入事件 service 阻塞 20s 前臺廣播阻塞 10s 或後臺廣告阻塞 20s ContentProvider publish 在 20s 內沒有處理完
  • anr 發生過程
  1. 捕獲到 anr,發送 linux 信號量 3
  2. 進程接受到信號量將 anr 信息寫入 data/anr/traces.txt 文件
  3. Log 打印 anr 信息
  4. 進程進入 anr 狀態,彈出 anr 提示框
  • 監控 anr
  1. Android 5.0 以下監聽 traces.txt 文件寫入
  2. 每隔 5s 向主線程發送消息判斷主線程是否阻塞
  • 分析 anr 查看 cpu 負載是否是 cpu 資源緊張導致 查看堆棧看是否是我們的代碼耗時過長
  • 避免 anr 主線程中不要做耗時操作,注意使用 IntentService 降低子線程優先級,讓主線程可以更多的獲取到 cpu 資源

Native Crash

  • 崩潰過程:native crash 時操作系統會向進程發送信號,崩潰信息會寫入到 data/tombstones 下,並在 logcat 輸出崩潰日誌
  • 定位:so 庫剝離調試信息的話,只有相對位置沒有具體行號,可以使用 NDK 提供的 addr2line 或 ndk-stack 來定位
  • addr2line:根據有調試信息的 so 和相對位置定位實際的代碼處
  • ndk-stack:可以分析 tombstone 文件,得到實際的代碼調用棧

其他:解析 XML、進程保活、播放器、Lint、CI、CD、AOP、JetPack

Android 解析 XML

  • SAX:流式解析
  • DOM:先把 XML 全部讀取到內存,再訪問樹形結構,很消耗內存
  • PULL:流式解析,Android 內置的默認解析方式

熱修復、插件化、組件化

  • 熱修復原理: Native Hook(AndFix):直接在 native 層進行方法的結構體信息對換 分包(QFix):插入新 dex 到 dexElements[],利用 ClassLoader 通過遍歷 dexElements[] 來 findClass 的特性 Java Hook(Robust):hook 每個方法,在每個方法裏埋好準備替換的邏輯
  • 插件化:DexClassLoader 動態加載,四大組件未註冊問題通過 hook AMS、Instrumentation 等解決,VirtualAPK 源碼分析
  • 組件化:ARoute 路由實現:通過 APT 解析 @Route 等註解,結合 JavaPoet 生成路由表,即路由與 Activity 的映射關係

進程保活

  • 進程優先級:
  1. 前臺進程 ;
  2. 可見進程;
  3. 服務進程;
  4. 後臺進程;
  5. 空進程
  • 進程被 kill 場景:
  1. 切到後臺內存不足時被殺;
  2. 切到後臺廠商省電機制殺死;
  3. 用戶主動清理
  • 保活方式:
  1. Activity 提權:掛一個 1像素 Activity 將進程優先級提高到前臺進程
  2. Service 提權:啓動一個前臺服務(API>18會有正在運行通知欄)
  3. 廣播拉活
  4. Service 拉活
  5. JobScheduler 定時任務拉活
  6. 雙進程拉活

播放器原理

  • 視頻播放原理:(mp4、flv)-> 解封裝 -> (mp3/aac、h264/h265)-> 解碼 -> (pcm、yuv)-> 音視頻同步 -> 渲染播放
  • 音視頻同步: 選擇參考時鐘源:音頻時間戳、視頻時間戳和外部時間三者選擇一個作爲參考時鐘源(一般選擇音頻,因爲人對音頻更敏感,ijk 默認也是音頻) 通過等待或丟幀將視頻流與參考時鐘源對齊,實現同步
  • IjkPlayer 原理 集成了 MediaPlayer、ExoPlayer 和 IjkPlayer 三種實現,其中 IjkPlayer 基於 FFmpeg 的 ffplay 音頻輸出方式:AudioTrack、OpenSL ES;視頻輸出方式:NativeWindow、OpenGL ES

Lint

  • Android Lint 是 Google 提供給 Android 開發者的靜態代碼檢查工具
  • 使用 Lint 對 Android 工程代碼進行掃描和檢查,可以發現代碼潛在的問題,提醒程序員及早修正
  • 基於 Detector、IssueRegistry 實現,通過 lintChecks project 引入

CI

  • Continuous integration(持續集成,簡稱CI):頻繁的將代碼集成到主幹,防止分支大幅偏離主幹,方便快速發現錯誤
  • Continuous delivery(持續交付):頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審
  • Continuous deployment(持續部署):持續交付的下一步,指的是代碼通過評審以後,自動部署到生產環境
  • 交付後需要進行構建,將源碼轉換爲可以運行的實際代碼,常用的構建工具有 Jenkins、Strider

AOP

  • 基於 Gradle Transform API 創建 TransForm ,其執行時機在 class 被打包成 dex 之前
  • 在 TransForm 中通過 javassist 或 asm 修改字節碼
  • 基於 Gradle Plugin API 自定義插件,應用自定義的 TransForm

JetPack

  • LiveData 感知聲明週期原理:像 Glide 一樣給界面添加了無視圖的 Fragment
  • ViewModel 界面旋轉短暫銷燬重建時保存數據原理: ViewModel 保存在 ViewModelStore 中 當 Activity 配置變更銷燬時,系統會調用 onRetainNonConfigurationInstance 保存 NonConfigurationInstances,而 ViewModel 就保存在 NonConfigurationInstances 中 重建時 onCreate 方法通過 getLastNonConfigurationInstance 方法獲取到 NonConfigurationInstances,從而獲取到 ViewModelStore
  • JetPack 與 MVVM: 先了解下 MVP:Model:處理數據;
  • View:控制視圖;Presenter:分離 Activity 和 Model
  • 再看 MVVM:Model:處理獲取保存數據;
  • View:控制視圖;
  • ViewModel:數據容器 使用 Jetpack 組件架構的 LiveData、ViewModel 可以便捷的實現 MVVM

下面是已整理好的Android面試知識點文檔和一些面試題、視頻學習資料等,想要獲取的朋友可以私信我【666】領取

阿里架構師最新 Android 面試點梳理,我收藏了你呢?

阿里架構師最新 Android 面試點梳理,我收藏了你呢?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章