腾讯T3大牛亲自讲解!面试字节跳动Android研发岗,值得收藏!
開(kāi)頭
最近有粉絲反應(yīng),不想做安卓了,有朋友轉(zhuǎn)到前端了,安卓不行了,問(wèn)我怎么辦?
自從RN,Weex這種跨平臺(tái)編程語(yǔ)言出來(lái)以后,安卓將死的言論總是不絕于耳。隨著頗有摧枯拉朽之勢(shì)Flutter的出現(xiàn),加之近幾年的裁員潮,使這種論調(diào)好像更加可信。
恍惚間會(huì)讓人想到2010年,安卓初興,一切還是桌面應(yīng)用開(kāi)發(fā)的天下,當(dāng)時(shí)的語(yǔ)言還集中中JAVA、HTML、MFC、.NET、C#,各個(gè)語(yǔ)言為了爭(zhēng)奪桌面應(yīng)用開(kāi)發(fā)第一位,也是拼得你死我活。當(dāng)時(shí)的安卓系統(tǒng)還是方興未艾,誰(shuí)也不知道,它會(huì)引領(lǐng)著另一個(gè)時(shí)代。
幾年過(guò)去了,桌面開(kāi)發(fā)已經(jīng)基本死了,現(xiàn)在輪到Android開(kāi)發(fā)了?
目錄:
1.網(wǎng)絡(luò)
 2.Java 基礎(chǔ)&容器&同步&設(shè)計(jì)模式
 3.Java 虛擬機(jī)&內(nèi)存結(jié)構(gòu)&GC&類加載&四種引用&動(dòng)態(tài)代理
 4.Android 基礎(chǔ)&性能優(yōu)化&Framwork
 5.Android 模塊化&熱修復(fù)&熱更新&打包&混淆&壓縮
 6.音視頻&FFmpeg&播放器
1、網(wǎng)絡(luò)
網(wǎng)絡(luò)協(xié)議模型
應(yīng)用層:負(fù)責(zé)處理特定的應(yīng)用程序細(xì)節(jié)
 HTTP、FTP、DNS
傳輸層:為兩臺(tái)主機(jī)提供端到端的基礎(chǔ)通信
 TCP、UDP
網(wǎng)絡(luò)層:控制分組傳輸、路由選擇等
 IP
鏈路層:操作系統(tǒng)設(shè)備驅(qū)動(dòng)程序、網(wǎng)卡相關(guān)接口
TCP 和 UDP 區(qū)別
TCP 連接;可靠;有序;面向字節(jié)流;速度慢;較重量;全雙工;適用于文件傳輸、瀏覽器等
- 全雙工:A 給 B 發(fā)消息的同時(shí),B 也能給 A 發(fā)
 - 半雙工:A 給 B 發(fā)消息的同時(shí),B 不能給 A 發(fā)
 
UDP 無(wú)連接;不可靠;無(wú)序;面向報(bào)文;速度快;輕量;適用于即時(shí)通訊、視頻通話等
TCP 三次握手
A:你能聽(tīng)到嗎?
 B:我能聽(tīng)到,你能聽(tīng)到嗎?
 A:我能聽(tīng)到,開(kāi)始吧
A 和 B 兩方都要能確保:我說(shuō)的話,你能聽(tīng)到;你說(shuō)的話,我能聽(tīng)到。所以需要三次握手
TCP 四次揮手
A:我說(shuō)完了
 B:我知道了,等一下,我可能還沒(méi)說(shuō)完
 B:我也說(shuō)完了
 A:我知道了,結(jié)束吧
B 收到 A 結(jié)束的消息后 B 可能還沒(méi)說(shuō)完,沒(méi)法立即回復(fù)結(jié)束標(biāo)示,只能等說(shuō)完后再告訴 A :我說(shuō)完了。
POST 和 GET 區(qū)別
Get 參數(shù)放在 url 中;Post 參數(shù)放在 request Body 中
 Get 可能不安全,因?yàn)閰?shù)放在 url 中
HTTPS
HTTP 是超文本傳輸協(xié)議,明文傳輸;HTTPS 使用 SSL 協(xié)議對(duì) HTTP 傳輸數(shù)據(jù)進(jìn)行了加密
HTTP 默認(rèn) 80 端口;HTTPS 默認(rèn) 443 端口
優(yōu)點(diǎn):安全
 缺點(diǎn):費(fèi)時(shí)、SSL 證書收費(fèi),加密能力還是有限的,但是比 HTTP 強(qiáng)多了
2、Java 基礎(chǔ)&容器&同步&設(shè)計(jì)模式
StringBuilder、StringBuffer、+、String.concat 鏈接字符串:
- StringBuffer 線程安全,StringBuilder 線程不安全
 - +實(shí)際上是用 StringBuilder 來(lái)實(shí)現(xiàn)的,所以非循環(huán)體可以直接用 +,循環(huán)體不行,因?yàn)闀?huì)頻繁創(chuàng)建 StringBuilder
 - String.concat 實(shí)質(zhì)是 new String ,效率也低,耗時(shí)排序:StringBuilder < StringBuffer < concat < +
 
Java 泛型擦除
- 修飾成員變量等類結(jié)構(gòu)相關(guān)的泛型不會(huì)被擦除
 - 容器類泛型會(huì)被擦除
 
ArrayList、LinkedList
ArrayList
基于數(shù)組實(shí)現(xiàn),查找快:o(1),增刪慢:o(n)
 初始容量為10,擴(kuò)容通過(guò) System.arrayCopy 方法
LinkedList
基于雙向鏈表實(shí)現(xiàn),查找慢:o(n),增刪快:o(1)
 封裝了隊(duì)列和棧的調(diào)用
HashMap 、HashTable
HashMap
- 基于數(shù)組和鏈表實(shí)現(xiàn),數(shù)組是 HashMap 的主體;鏈表是為解決哈希沖突而存在的
 - 當(dāng)發(fā)生哈希沖突且鏈表 size 大于閾值時(shí)會(huì)擴(kuò)容,JAVA 8 會(huì)將鏈表轉(zhuǎn)為紅黑樹(shù)提高性能
允許 key/value 為 null 
HashTable
- 數(shù)據(jù)結(jié)構(gòu)和 HashMap 一樣
 - 不允許 value 為 null
 - 線程安全
 
ArrayMap、SparseArray
ArrayMap
1.基于兩個(gè)數(shù)組實(shí)現(xiàn),一個(gè)存放 hash;一個(gè)存放鍵值對(duì)。擴(kuò)容的時(shí)候只需要數(shù)組拷貝,不需要重建哈希表
 2.內(nèi)存利用率高
 3.不適合存大量數(shù)據(jù),因?yàn)闀?huì)對(duì) key 進(jìn)行二分法查找(1000以下)
SparseArray
1.基于兩個(gè)數(shù)組實(shí)現(xiàn),int 做 key
 2.內(nèi)存利用率高
 3.不適合存大量數(shù)據(jù),因?yàn)闀?huì)對(duì) key 進(jìn)行二分法查找(1000以下)
volatile 關(guān)鍵字
- 只能用來(lái)修飾變量,適用修飾可能被多線程同時(shí)訪問(wèn)的變量
 - 相當(dāng)于輕量級(jí)的 synchronized,volatitle 能保證有序性(禁用指令重排序)、可見(jiàn)性;后者還能保證原子性
 - 變量位于主內(nèi)存中,每個(gè)線程還有自己的工作內(nèi)存,變量在自己線程的工作內(nèi)存中有份拷貝,線程直接操作的是這個(gè)拷貝
 - 被 volatile 修飾的變量改變后會(huì)立即同步到主內(nèi)存,保持變量的可見(jiàn)性。
 
雙重檢查單例,為什么要加 volatile?
1.volatile想要解決的問(wèn)題是,在另一個(gè)線程中想要使用instance,發(fā)現(xiàn)instance!=null,但是實(shí)際上instance還未初始化完畢這個(gè)問(wèn)題
2.將instance =newInstance();拆分為3句話是。1.分配內(nèi)存2.初始化3.將instance指向分配的內(nèi)存空
3.volatile可以禁止指令重排序,確保先執(zhí)行2,后執(zhí)行3
wait 和 sleep
- sleep 是 Thread 的靜態(tài)方法,可以在任何地方調(diào)用
 - wait 是 Object 的成員方法,只能在 synchronized 代碼塊中調(diào)用,否則會(huì)報(bào) IllegalMonitorStateException 非法監(jiān)控狀態(tài)異常
 - sleep 不會(huì)釋放共享資源鎖,wait 會(huì)釋放共享資源鎖
 
lock 和 synchronized
- synchronized 是 Java 關(guān)鍵字,內(nèi)置特性;Lock 是一個(gè)接口
 - synchronized 會(huì)自動(dòng)釋放鎖;lock 需要手動(dòng)釋放,所以需要寫到 try catch 塊中并在 finally 中釋放鎖
 - synchronized 無(wú)法中斷等待鎖;lock 可以中斷
 - Lock 可以提高多個(gè)線程進(jìn)行讀/寫操作的效率
 - 競(jìng)爭(zhēng)資源激烈時(shí),lock 的性能會(huì)明顯的優(yōu)于 synchronized
 
可重入鎖
- 定義:已經(jīng)獲取到鎖后,再次調(diào)用同步代碼塊/嘗試獲取鎖時(shí)不必重新去申請(qǐng)鎖,可以直接執(zhí)行相關(guān)代碼
 - ReentrantLock 和 synchronized 都是可重入鎖
 
公平鎖
- 定義:等待時(shí)間最久的線程會(huì)優(yōu)先獲得鎖
 - 非公平鎖無(wú)法保證哪個(gè)線程獲取到鎖,synchronized 就是非公平鎖
 - ReentrantLock 默認(rèn)時(shí)非公平鎖,可以設(shè)置為公平鎖
 
樂(lè)觀鎖和悲觀鎖
- 悲觀鎖:線程一旦得到鎖,其他線程就掛起等待,適用于寫入操作頻繁的場(chǎng)景;synchronized 就是悲觀鎖
 - 樂(lè)觀鎖:假設(shè)沒(méi)有沖突,不加鎖,更新數(shù)據(jù)時(shí)判斷該數(shù)據(jù)是否過(guò)期,過(guò)期的話則不進(jìn)行數(shù)據(jù)更新,適用于讀取操作頻繁的場(chǎng)景
 - 樂(lè)觀鎖 CAS:Compare And Swap,更新數(shù)據(jù)時(shí)先比較原值是否相等,不相等則表示數(shù)據(jù)過(guò)去,不進(jìn)行數(shù)據(jù)更新
 - 樂(lè)觀鎖實(shí)現(xiàn):AtomicInteger、AtomicLong、AtomicBoolean
 
死鎖 4 個(gè)必要條件
- 互斥
 - 占有且等待
 - 不可搶占
 - 循環(huán)等待
 
synchronized 原理
- 每個(gè)對(duì)象都有一個(gè)監(jiān)視器鎖:monitor,同步代碼塊會(huì)執(zhí)行 monitorenter 開(kāi)始,motnitorexit 結(jié)束
 - wait/notify 就依賴 monitor 監(jiān)視器,所以在非同步代碼塊中執(zhí)行會(huì)報(bào) IllegalMonitorStateException 異常
 
3、Java 虛擬機(jī)&內(nèi)存結(jié)構(gòu)&GC&類加載&四種引用&動(dòng)態(tài)代理
JVM
- 定義:可以理解成一個(gè)虛構(gòu)的計(jì)算機(jī),解釋自己的字節(jié)碼指令集映射到本地 CPU 或 OS 的指令集,上層只需關(guān)注 Class 文件,與操作系統(tǒng)無(wú)關(guān),實(shí)現(xiàn)跨平臺(tái)
 - Kotlin 就是能解釋成 Class 文件,所以可以跑在 JVM 上
 
JVM 內(nèi)存模型
- Java 多線程之間是通過(guò)共享內(nèi)存來(lái)通信的,每個(gè)線程都有自己的本地內(nèi)存
 - 共享變量存放于主內(nèi)存中,線程會(huì)拷貝一份共享變量到本地內(nèi)存
 - volatile 關(guān)鍵字就是給內(nèi)存模型服務(wù)的,用來(lái)保證內(nèi)存可見(jiàn)性和順序性
 
JVM 內(nèi)存結(jié)構(gòu)
線程私有:
1.程序計(jì)數(shù)器:記錄正在執(zhí)行的字節(jié)碼指令地址,若正在執(zhí)行 Native 方法則為空
 2.虛擬機(jī)棧:執(zhí)行方法時(shí)把方法所需數(shù)據(jù)存為一個(gè)棧幀入棧,執(zhí)行完后出棧
 3.本地方法棧:同虛擬機(jī)棧,但是針對(duì)的是 Native 方法
線程共享:
1.堆:存儲(chǔ) Java 實(shí)例,GC 主要區(qū)域,分代收集 GC 方法會(huì)吧堆劃分為新生代、老年代
 2.方法區(qū):存儲(chǔ)類信息,常量池,靜態(tài)變量等數(shù)據(jù)
GC
回收區(qū)域:只針對(duì)堆、方法區(qū);線程私有區(qū)域數(shù)據(jù)會(huì)隨線程結(jié)束銷毀,不用回收
回收類型:
1.堆中的對(duì)象
- 分代收集 GC 方法會(huì)吧堆劃分為新生代、老年代
 - 新生代:新建小對(duì)象會(huì)進(jìn)入新生代;通過(guò)復(fù)制算法回收對(duì)象
 - 老年代:新建大對(duì)象及老對(duì)象會(huì)進(jìn)入老年代;通過(guò)標(biāo)記-清除算法回收對(duì)象
 
2.方法區(qū)中的類信息、常量池
判斷一個(gè)對(duì)象是否可被回收:
1.引用計(jì)數(shù)法
 缺點(diǎn):循環(huán)引用
2.可達(dá)性分析法
 定義:從 GC ROOT 開(kāi)始搜索,不可達(dá)的對(duì)象都是可以被回收的
GC ROOT
1.虛擬機(jī)棧/本地方法棧中引用的對(duì)象
 2.方法區(qū)中常量/靜態(tài)變量引用的對(duì)象
四種引用
- 強(qiáng)引用:不會(huì)被回收
 - 軟引用:內(nèi)存不足時(shí)會(huì)被回收
 - 弱引用:gc 時(shí)會(huì)被回收
 - 虛引用:無(wú)法通過(guò)虛引用得到對(duì)象,可以監(jiān)聽(tīng)對(duì)象的回收
 
ClassLoader
類的生命周期:
1.加載;2.驗(yàn)證;3.準(zhǔn)備;4.解析;5.初始化;6.使用;7.卸載
類加載過(guò)程:
1.加載:獲取類的二進(jìn)制字節(jié)流;生成方法區(qū)的運(yùn)行時(shí)存儲(chǔ)結(jié)構(gòu);在內(nèi)存中生成 Class 對(duì)象
 2.驗(yàn)證:確保該 Class 字節(jié)流符合虛擬機(jī)要求
 3.準(zhǔn)備:初始化靜態(tài)變量
 4.解析:將常量池的符號(hào)引用替換為直接引用
 5.初始化:執(zhí)行靜態(tài)塊代碼、類變量賦值
類加載時(shí)機(jī):
1.實(shí)例化對(duì)象
 2.調(diào)用類的靜態(tài)方法
 3.調(diào)用類的靜態(tài)變量(放入常量池的常量除外)
類加載器:負(fù)責(zé)加載 class 文件
分類:
1.引導(dǎo)類加載器 - 沒(méi)有父類加載器
 2.拓展類加載器 - 繼承自引導(dǎo)類加載器
 3.系統(tǒng)類加載器 - 繼承自拓展類加載器
雙親委托模型:
當(dāng)要加載一個(gè) class 時(shí),會(huì)先逐層向上讓父加載器先加載,加載失敗才會(huì)自己加載
為什么叫雙親?不考慮自定義加載器,系統(tǒng)類加載器需要網(wǎng)上詢問(wèn)兩層,所以叫雙親
判斷是否是同一個(gè)類時(shí),除了類信息,還必須時(shí)同一個(gè)類加載器
優(yōu)點(diǎn):
- 防止重復(fù)加載,父加載器加載過(guò)了就沒(méi)必要加載了
 - 安全,防止篡改核心庫(kù)類
 
動(dòng)態(tài)代理原理及實(shí)現(xiàn)
- InvocationHandler 接口,動(dòng)態(tài)代理類需要實(shí)現(xiàn)這個(gè)接口
 - Proxy.newProxyInstance,用于動(dòng)態(tài)創(chuàng)建代理對(duì)象
 - Retrofit 應(yīng)用: Retrofit 通過(guò)動(dòng)態(tài)代理,為我們定義的請(qǐng)求接口都生成一個(gè)動(dòng)態(tài)代理對(duì)象,實(shí)現(xiàn)請(qǐng)求
 
4、Android 基礎(chǔ)&性能優(yōu)化&Framwork
Activity 啟動(dòng)模式
- standard 標(biāo)準(zhǔn)模式
 - singleTop 棧頂復(fù)用模式, 
- 推送點(diǎn)擊消息界面
 
 - singleTask 棧內(nèi)復(fù)用模式, 
- 首頁(yè)
 
 - singleInstance 單例模式,單獨(dú)位于一個(gè)任務(wù)棧中 
- 撥打電話界面
細(xì)節(jié): - taskAffinity:任務(wù)相關(guān)性,用于指定任務(wù)棧名稱,默認(rèn)為應(yīng)用包名
 - allowTaskReparenting:允許轉(zhuǎn)移任務(wù)棧
 
 - 撥打電話界面
 
View 工作原理
- DecorView (FrameLayout) 
- LinearLayout 
- titlebar
 - Content
 - 調(diào)用 setContentView 設(shè)置的 View
 
 
 - LinearLayout 
 
ViewRoot 的 performTraversals 方法調(diào)用觸發(fā)開(kāi)始 View 的繪制,然后會(huì)依次調(diào)用:
- performMeasure:遍歷 View 的 measure 測(cè)量尺寸
 - performLayout:遍歷 View 的 layout 確定位置
 - performDraw:遍歷 View 的 draw 繪制
 
事件分發(fā)機(jī)制
- 一個(gè) MotionEvent 產(chǎn)生后,按 Activity -> Window -> decorView -> View 順序傳遞,View 傳遞過(guò)程就是事件分發(fā),主要依賴三個(gè)方法:
 - dispatchTouchEvent:用于分發(fā)事件,只要接受到點(diǎn)擊事件就會(huì)被調(diào)用,返回結(jié)果表示是否消耗了當(dāng)前事件
 - onInterceptTouchEvent:用于判斷是否攔截事件,當(dāng) ViewGroup 確定要攔截事件后,該事件序列都不會(huì)再觸發(fā)調(diào)用此 ViewGroup 的 onIntercept
 - onTouchEvent:用于處理事件,返回結(jié)果表示是否處理了當(dāng)前事件,未處理則傳遞給父容器處理
 - 細(xì)節(jié): 
- 一個(gè)事件序列只能被一個(gè) View 攔截且消耗
 - View 沒(méi)有 onIntercept 方法,直接調(diào)用 onTouchEvent 處理
 - OnTouchListener 優(yōu)先級(jí)比 OnTouchEvent 高,onClickListener 優(yōu)先級(jí)最低
 - requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercet 方法的調(diào)用
 
 
Window 、 WindowManager、WMS、SurfaceFlinger
- Window:抽象概念不是實(shí)際存在的,而是以 View 的形式存在,通過(guò) PhoneWindow 實(shí)現(xiàn)
 - WindowManager:外界訪問(wèn) Window 的入口,內(nèi)部與 WMS 交互是個(gè) IPC 過(guò)程
 - WMS:管理窗口 Surface 的布局和次序,作為系統(tǒng)級(jí)服務(wù)單獨(dú)運(yùn)行在一個(gè)進(jìn)程
 - SurfaceFlinger:將 WMS 維護(hù)的窗口按一定次序混合后顯示到屏幕上
 
View 動(dòng)畫、幀動(dòng)畫及屬性動(dòng)畫
View 動(dòng)畫:
- 作用對(duì)象是 View,可用 xml 定義,建議 xml 實(shí)現(xiàn)比較易讀
 - 支持四種效果:平移、縮放、旋轉(zhuǎn)、透明度
 
幀動(dòng)畫:
- 通過(guò) AnimationDrawable 實(shí)現(xiàn),容易 OOM
 
屬性動(dòng)畫:
- 可作用于任何對(duì)象,可用 xml 定義,Android 3 引入,建議代碼實(shí)現(xiàn)比較靈活
 - 包括 ObjectAnimator、ValuetAnimator、AnimatorSet
 - 時(shí)間插值器:根據(jù)時(shí)間流逝的百分比計(jì)算當(dāng)前屬性改變的百分比
 - 系統(tǒng)預(yù)置勻速、加速、減速等插值器
 - 類型估值器:根據(jù)當(dāng)前屬性改變的百分比計(jì)算改變后的屬性值
 - 系統(tǒng)預(yù)置整型、浮點(diǎn)、色值等類型估值器
 - 使用注意事項(xiàng):
 - 避免使用幀動(dòng)畫,容易OOM
 - 界面銷毀時(shí)停止動(dòng)畫,避免內(nèi)存泄漏
 - 開(kāi)啟硬件加速,提高動(dòng)畫流暢性 ,硬件加速:
 - 將 cpu 一部分工作分擔(dān)給 gpu ,使用 gpu 完成繪制工作
 - 從工作分?jǐn)偤屠L制機(jī)制兩個(gè)方面優(yōu)化了繪制速度
 
Handler、MessageQueue、Looper
- Handler:開(kāi)發(fā)直接接觸的類,內(nèi)部持有 MessageQueue 和 Looper
 - MessageQueue:消息隊(duì)列,內(nèi)部通過(guò)單鏈表存儲(chǔ)消息
 - Looper:內(nèi)部持有 MessageQueue,循環(huán)查看是否有新消息,有就處理,沒(méi)就阻塞
 - 如何實(shí)現(xiàn)阻塞:通過(guò) nativePollOnce 方法,基于 Linux epoll 事件管理機(jī)制
 - 為什么主線程不會(huì)因?yàn)?Looper 阻塞:系統(tǒng)每 16ms 會(huì)發(fā)送一個(gè)刷新 UI 消息喚醒
 
MVC、MVP、MVVM
- MVP:Model:處理數(shù)據(jù);View:控制視圖;Presenter:分離 Activity 和 Model
 - MVVM:Model:處理獲取保存數(shù)據(jù);View:控制視圖;ViewModel:數(shù)據(jù)容器 
- 使用 Jetpack 組件架構(gòu)的 LiveData、ViewModel 便捷實(shí)現(xiàn) MVVM
 
 
Serializable、Parcelable
- Serializable :Java 序列化方式,適用于存儲(chǔ)和網(wǎng)絡(luò)傳輸,serialVersionUID 用于確定反序列化和類版本是否一致,不一致時(shí)反序列化回失敗
 - Parcelable :Android 序列化方式,適用于組件通信數(shù)據(jù)傳遞,性能高,因?yàn)椴幌?Serializable 一樣有大量反射操作,頻繁 GC
 
Binder
- Android 進(jìn)程間通信的中流砥柱,基于客戶端-服務(wù)端通信方式
 - 使用 mmap 一次數(shù)據(jù)拷貝實(shí)現(xiàn) IPC,傳統(tǒng) IPC:用戶A空間->內(nèi)核->用戶B空間;mmap 將內(nèi)核與用戶B空間映射,實(shí)現(xiàn)直接從用戶A空間->用戶B空間
 - BinderPool 可避免創(chuàng)建多 Service
 
IPC 方式
- Intent extras、Bundle:要求傳遞數(shù)據(jù)能被序列化,實(shí)現(xiàn) Parcelable、Serializable ,適用于四大組件通信
 - 文件共享:適用于交換簡(jiǎn)單的數(shù)據(jù)實(shí)時(shí)性不高的場(chǎng)景
 - AIDL:AIDL 接口實(shí)質(zhì)上是系統(tǒng)提供給我們可以方便實(shí)現(xiàn) BInder 的工具 
- Android Interface Definition Language,可實(shí)現(xiàn)跨進(jìn)程調(diào)用方法
 - 服務(wù)端:將暴漏給客戶端的接口聲明在 AIDL 文件中,創(chuàng)建 Service 實(shí)現(xiàn) AIDL 接口并監(jiān)聽(tīng)客戶端連接請(qǐng)求
 - 客戶端:綁定服務(wù)端 Service ,綁定成功后拿到服務(wù)端 Binder 對(duì)象轉(zhuǎn)為 AIDL 接口調(diào)用
 - RemoteCallbackList 實(shí)現(xiàn)跨進(jìn)程接口監(jiān)聽(tīng),同個(gè) Binder 對(duì)象做 key 存儲(chǔ)客戶端注冊(cè)的 listener
 - 監(jiān)聽(tīng) Binder 斷開(kāi):1.Binder.linkToDeath 設(shè)置死亡代理;2. onServiceDisconnected 回調(diào)
 
 - Messenger:基于 AIDL 實(shí)現(xiàn),服務(wù)端串行處理,主要用于傳遞消息,適用于低并發(fā)一對(duì)多通信
 - ContentProvider:基于 Binder 實(shí)現(xiàn),適用于一對(duì)多進(jìn)程間數(shù)據(jù)共享
 - Socket:TCP、UDP,適用于網(wǎng)絡(luò)數(shù)據(jù)交換
 
Android 系統(tǒng)啟動(dòng)流程
- 按電源鍵 -> 加載引導(dǎo)程序 BootLoader 到 RAM -> 執(zhí)行 BootLoader 程序啟動(dòng)內(nèi)核 -> 啟動(dòng) init 進(jìn)程 -> 啟動(dòng) Zygote 和各種守護(hù)進(jìn)程 ->
 - 啟動(dòng) System Server 服務(wù)進(jìn)程開(kāi)啟 AMS、WMS 等 -> 啟動(dòng) Launcher 應(yīng)用進(jìn)程
 
App 啟動(dòng)流程
Launcher 中點(diǎn)擊一個(gè)應(yīng)用圖標(biāo) -> 通過(guò) AMS 查找應(yīng)用進(jìn)程,若不存在就通過(guò) Zygote 進(jìn)程 fork
進(jìn)程保活
- 進(jìn)程優(yōu)先級(jí):1.前臺(tái)進(jìn)程 ;2.可見(jiàn)進(jìn)程;3.服務(wù)進(jìn)程;4.后臺(tái)進(jìn)程;5.空進(jìn)程
 - 進(jìn)程被 kill 場(chǎng)景:1.切到后臺(tái)內(nèi)存不足時(shí)被殺;2.切到后臺(tái)廠商省電機(jī)制殺死;3.用戶主動(dòng)清理
 - 保活方式: 
- 1.Activity 提權(quán):掛一個(gè) 1像素 Activity 將進(jìn)程優(yōu)先級(jí)提高到前臺(tái)進(jìn)程
 - 2.Service 提權(quán):啟動(dòng)一個(gè)前臺(tái)服務(wù)(API>18會(huì)有正在運(yùn)行通知欄)
 - 3.廣播拉活
 - 4.Service 拉活
 - 5.JobScheduler 定時(shí)任務(wù)拉活
 - 6.雙進(jìn)程拉活
 
 
網(wǎng)絡(luò)優(yōu)化及檢測(cè)
- 速度:1.GZIP 壓縮(okhttp 自動(dòng)支持);2.Protocol Buffer 替代 json;3.優(yōu)化圖片/文件流量;4.IP 直連省去 DNS 解析時(shí)間
 - 成功率:1.失敗重試策略;
 - 流量:1.GZIP 壓縮(okhttp 自動(dòng)支持);2.Protocol Buffer 替代 json;3.優(yōu)化圖片/文件流量;5.文件下載斷點(diǎn)續(xù)傳 ;6.緩存
 - 協(xié)議層的優(yōu)化,比如更優(yōu)的 http 版本等
 - 監(jiān)控:Charles 抓包、Network Monitor 監(jiān)控流量
 
UI卡頓優(yōu)化
- 減少布局層級(jí)及控件復(fù)雜度,避免過(guò)度繪制
 - 使用 include、merge、viewstub
 - 優(yōu)化繪制過(guò)程,避免在 Draw 中頻繁創(chuàng)建對(duì)象、做耗時(shí)操作
 
內(nèi)存泄漏場(chǎng)景及規(guī)避
1.靜態(tài)變量、單例強(qiáng)引跟生命周期相關(guān)的數(shù)據(jù)或資源,包括 EventBus
 2.游標(biāo)、IO 流等資源忘記主動(dòng)釋放
 3.界面相關(guān)動(dòng)畫在界面銷毀時(shí)及時(shí)暫停
 4.內(nèi)部類持有外部類引用導(dǎo)致的內(nèi)存泄漏
- handler 內(nèi)部類內(nèi)存泄漏規(guī)避:1.使用靜態(tài)內(nèi)部類+弱引用 2.界面銷毀時(shí)清空消息隊(duì)列
 - 檢測(cè):Android Studio Profiler
 
LeakCanary 原理
- 通過(guò)弱引用和引用隊(duì)列監(jiān)控對(duì)象是否被回收
 - 比如 Activity 銷毀時(shí)開(kāi)始監(jiān)控此對(duì)象,檢測(cè)到未被回收則主動(dòng) gc ,然后繼續(xù)監(jiān)控
 
OOM 場(chǎng)景及規(guī)避
- 加載大圖:減小圖片
 - 內(nèi)存泄漏:規(guī)避內(nèi)存泄漏
 
5、Android 模塊化&熱修復(fù)&熱更新&打包&混淆&壓縮
Dalvik 和 ART
- Dalvik 
- 谷歌設(shè)計(jì)專用于 Android 平臺(tái)的 Java 虛擬機(jī),可直接運(yùn)行 .dex 文件,適合內(nèi)存和處理速度有限的系統(tǒng)
 - JVM 指令集是基于棧的;Dalvik 指令集是基于寄存器的,代碼執(zhí)行效率更優(yōu)
 
 - ART 
- Dalvik 每次運(yùn)行都要將字節(jié)碼轉(zhuǎn)換成機(jī)器碼;ART 在應(yīng)用安裝時(shí)就會(huì)轉(zhuǎn)換成機(jī)器碼,執(zhí)行速度更快
 - ART 存儲(chǔ)機(jī)器碼占用空間更大,空間換時(shí)間
 
 
APK 打包流程
1.aapt 打包資源文件生成 R.java 文件;aidl 生成 java 文件
 2.將 java 文件編譯為 class 文件
 3.將工程及第三方的 class 文件轉(zhuǎn)換成 dex 文件
 4.將 dex 文件、so、編譯過(guò)的資源、原始資源等打包成 apk 文件
 5.簽名
 6.資源文件對(duì)齊,減少運(yùn)行時(shí)內(nèi)存
App 安裝過(guò)程
- 首先要解壓 APK,資源、so等放到應(yīng)用目錄
 - Dalvik 會(huì)將 dex 處理成 ODEX ;ART 會(huì)將 dex 處理成 OAT;
 - OAT 包含 dex 和安裝時(shí)編譯的機(jī)器碼
 
組件化路由實(shí)現(xiàn)
ARoute:通過(guò) APT 解析 @Route 等注解,結(jié)合 JavaPoet 生成路由表,即路由與 Activity 的映射關(guān)系
6、音視頻&FFmpeg&播放器
FFmpeg
基于命令方式實(shí)現(xiàn)了一個(gè)音視頻編輯 App
集成編譯了 AAC、MP3、H264 編碼器
播放器原理
視頻播放原理:(mp4、flv)-> 解封裝 -> (mp3/aac、h264/h265)-> 解碼 -> (pcm、yuv)-> 音視頻同步 -> 渲染播放
音視頻同步:
- 選擇參考時(shí)鐘源:音頻時(shí)間戳、視頻時(shí)間戳和外部時(shí)間三者選擇一個(gè)作為參考時(shí)鐘源(一般選擇音頻,因?yàn)槿藢?duì)音頻更敏感,ijk 默認(rèn)也是音頻)
 - 通過(guò)等待或丟幀將視頻流與參考時(shí)鐘源對(duì)齊,實(shí)現(xiàn)同步
 
IjkPlayer 原理
集成了 MediaPlayer、ExoPlayer 和 IjkPlayer 三種實(shí)現(xiàn),其中 IjkPlayer 基于 FFmpeg 的 ffplay
音頻輸出方式:AudioTrack、OpenSL ES;視頻輸出方式:NativeWindow、OpenGL ES
最后
愿你有一天,真愛(ài)自己,善待自己。
本文在開(kāi)源項(xiàng)目:Android開(kāi)發(fā)不會(huì)這些?如何面試拿高薪?中已收錄,里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)、及系列技術(shù)文章等,資源持續(xù)更新中…
總結(jié)
以上是生活随笔為你收集整理的腾讯T3大牛亲自讲解!面试字节跳动Android研发岗,值得收藏!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: 循环数142857问题 java_神奇数
 - 下一篇: php 计算时间差,php 怎么计算两个