最新 Android 面試點梳理,我收藏了你呢?

目錄:java

  • 網絡:分層模型、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

網絡:分層模型、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)linux

    • 基於數組和單向鏈表實現,數組是 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 同樣
    • 線程安全
  • HashSetweb

    • 基於 HashMap 實現,元素就是 HashMap 的 key,Value 傳入了一個固定值

ArrayMap、SparseArray

  • ArrayMap算法

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

    • 基於 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 系統架構

  • 應用層
  • 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.失敗重試策略;
  • 流量:1.GZIP 壓縮(okhttp 自動支持);2.Protocol Buffer 替代 json;3.優化圖片/文件流量;5.文件下載斷點續傳 ;6.緩存
  • 協議層的優化,好比更優的 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

內存泄漏場景及規避

  • 1.靜態變量、單例強引跟生命週期相關的數據或資源,包括 EventBus
  • 2.遊標、IO 流等資源忘記主動釋放
  • 3.界面相關動畫在界面銷燬時及時暫停
  • 4.內部類持有外部類引用致使的內存泄漏
    • 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.雙進程拉活

播放器原理

  • 視頻播放原理:(mp四、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

相關文章
相關標籤/搜索