前言
由于疫情關系,最近在各大網絡技術交流平台看到很多同學的工作情況内心還是蠻觸動的(降薪、變相裁員、辭退等)。可能這并不是當下一個普遍的現象,但仍然使我感受到Android開發這碗青春飯不好混。于此同時聯系我内推的同學很多都處于待業狀态,能感受到他們内心的迷茫和焦灼。于是内心一直有聲音督促我,趕緊寫點面試相關的東西出來吧,哪怕對大家隻有一絲絲的幫助。當然這次我會以面試官的角度出發(可能不是一個優秀的面試官),讓大家更加了解位元組跳動的面試注意事項、重點面試題解析等。接下來我會從以下兩個子產品來講解:
- 面試注意點
- 面試題解析
01
代碼能力
是研發面試考察的核心!
其實說句老實話,研發面試所考察的最核心的能力就是......代碼能力!
代碼能力是計算機專業的基礎。能否在有限時間内寫出清晰簡潔、邏輯清晰的代碼,不僅可以考察出候選人是否有紮實的基本功,也可以讓面試官對候選人在未來是否能夠勝任相應的崗位工作有一個基礎判斷。
面試兩場之後我發現,有的候選人剛開始聊項目聊得非常開心,一些細節問題回答得都挺不錯的,可一旦到了手寫代碼這一關,連二分查找都寫不出來的大有人在。
平時寫的代碼多不多,面試之前有沒有做過準備,有經驗的面試官一眼就可以判斷出來。是以這裡給大家分享三點建議:
1、加強基本功,增加代碼量
多看優秀的源代碼,認真從效率、邏輯等方面分析他人如何簡潔明晰地實作一個函數,這對提升自己的基本代碼能力有很大的幫助。
2、面試前多做題,保持手感很重要
面試之前多做些練習,這不僅可以加快答題速度,也會讓自己養成較為規範的答題習慣。
3、複盤筆試答案,思考更優解
最後,不少面試官喜歡在面試的時候複盤筆試題目,與候選人讨論題目的更優解,進而考察考生是否具備主動思考能力。
02
我們不反對刷題
不過更希望大家舉一反三
在面試過程中,經常會有同學非常驕傲地坦白道:“我沒有刷題。”
而在這裡我想代表廣大面試官表個态:我們不反對刷題,甚至希望同學們在大量做題之後,能夠靈活運用、舉一反三。
大量做題不僅可以在短時間内提高同學們的解題速度,也會在一定程度上幫助你拓展自己的答題思路。
當然,如果發現同學刷題,面試官也會變種問題,從側面考察同學是否死記硬背答案。作為春招的面試官、你未來的潛在同僚,我們更希望同學們能夠靈活貫通。
是以說,該準備準備,該刷題刷題,臨時抱佛腳至少展現了你對面試重視是不是?等你題目刷到一定程度,你就會發現自己的能力有了一個量變到質變的提升。
03
項目描述切忌花哨
突出項目重點,表述邏輯要清晰
參與面試的同學們大多都有實習經曆。在公司的大環境下,你會在實踐中不斷夯實代碼基礎,也會有更多機會接觸到最新的技術。
我們希望大家對過往的實習經曆做更深入的思考,不是簡簡單單描述你在哪裡做了什麼。畢竟面試官需要在短時間内看到你的個人能力。發揮主觀能動性,多在幾種不同方法之間做比較,給出在思考之後得出的最優解,會顯得你格外與衆不同。
除此之外,思考的邏輯性和表達能力也是面試時考察的重點。在面試時切記要簡化答案,能表達清楚想法就好,項目描述時隻需要簡單介紹項目背景,并着重突出項目亮點就可以啦!千萬不要過度包裝,要知道坦誠清晰可是「位元組範兒」中很重要的一點哦~
04
跨專業面試不會受到差別對待
技術實力最重要
面試官們常常會被問到:非計算機專業的同學跨專業面試會不會受到差別對待?
在這裡我想強調:完全不會!
不同崗位對具體的專業能力要求不同,拿算法崗位舉例,隻要你代碼基礎功底過硬,數理能力夠強,又對所面試崗位是發自内心的熱愛,就算你是學挖掘機技術的,面試官們也是想給你發offer的。
算法行業的通用能力就是代碼硬實力。如果不具備這種能力,在實際工作中就總會感覺不自信,受人制肘。是以非科班出身的同學們不用受自己的專業所限,但一定要多多鍛煉,努力提升自己。
不過這裡也想真心地提醒大家幾句,千萬不要盲從跟風選擇自己的職業方向,每個領域都有自己廣闊的發展空間,适合自己的才是最好的。越熱門的崗位對基礎的要求越高,面試官在面試的時候是看得到筆試成績的,是以還希望大家在面試過程中誠實一點,不懂裝懂可是會減分的哦!
05
哪些行為一定是減分項?
除了上述幾點,我還想多囑咐幾句:
雖然面試考察最多的是技術基礎和代碼能力,但是從心理學的角度分析,第一印象也是相當重要的啊喂!
為了這場面試,逗比的我收斂了表情包,穿上了帥氣的格子衫,還把頭發梳成了面試官的模樣,你說你穿着拖鞋、臉都不洗就來面試是不是有點傷我心了。
如果同學們選擇的是視訊面試,希望大家在面試前做好充分的準備工作。面試開始之後,如果遇到候選人遲到、沒有調試裝置、網絡不暢通、麥克風關掉、面試環境嘈雜等情況,面試官會給一定時間調試,但是面試體驗會受到一定影響,心理上可能會減分的哦!
誠實守信是做人第一要務,也是位元組跳動用人的底線。你們要知道,即使是視訊面試,面試官也是可以看到考生行為的!答應我,像類似「一邊考試一邊用ipad查答案」這種事情千萬不要幹好嘛!作為考官的我在攝像頭另一邊看到了也是很尴尬的......
1、網絡
網絡協定模型
應用層:負責處理特定的應用程式細節
HTTP、FTP、DNS
傳輸層:為兩台主機提供端到端的基礎通信
TCP、UDP
網絡層:控制分組傳輸、路由選擇等
IP
鍊路層:作業系統裝置驅動程式、網卡相關接口
TCP 和 UDP 差別
TCP 連接配接;可靠;有序;面向位元組流;速度慢;較重量;全雙工;适用于檔案傳輸、浏覽器等
- 全雙工:A 給 B 發消息的同時,B 也能給 A 發
- 半雙工:A 給 B 發消息的同時,B 不能給 A 發
UDP 無連接配接;不可靠;無序;面向封包;速度快;輕量;适用于即時通訊、視訊通話等
TCP 三次握手
A:你能聽到嗎?
B:我能聽到,你能聽到嗎?
A:我能聽到,開始吧
A 和 B 兩方都要能確定:我說的話,你能聽到;你說的話,我能聽到。是以需要三次握手
TCP 四次揮手
A:我說完了
B:我知道了,等一下,我可能還沒說完
B:我也說完了
A:我知道了,結束吧
B 收到 A 結束的消息後 B 可能還沒說完,沒法立即回複結束标示,隻能等說完後再告訴 A :我說完了。
POST 和 GET 差別
Get 參數放在 url 中;Post 參數放在 request Body 中
Get 可能不安全,因為參數放在 url 中
HTTPS
HTTP 是超文本傳輸協定,明文傳輸;HTTPS 使用 SSL 協定對 HTTP 傳輸資料進行了加密
HTTP 預設 80 端口;HTTPS 預設 443 端口
優點:安全
缺點:費時、SSL 證書收費,加密能力還是有限的,但是比 HTTP 強多了
2、Java 基礎&容器&同步&設計模式
StringBuilder、StringBuffer、+、String.concat 連結字元串:
- StringBuffer 線程安全,StringBuilder 線程不安全
- +實際上是用 StringBuilder 來實作的,是以非循環體可以直接用 +,循環體不行,因為會頻繁建立 StringBuilder
- String.concat 實質是 new String ,效率也低,耗時排序:StringBuilder < StringBuffer < concat < +
Java 泛型擦除
- 修飾成員變量等類結構相關的泛型不會被擦除
- 容器類泛型會被擦除
ArrayList、LinkedList
ArrayList
基于數組實作,查找快:o(1),增删慢:o(n)
初始容量為10,擴容通過 System.arrayCopy 方法
LinkedList
基于雙向連結清單實作,查找慢:o(n),增删快:o(1)
封裝了隊列和棧的調用
HashMap 、HashTable
HashMap
- 基于數組和連結清單實作,數組是 HashMap 的主體;連結清單是為解決哈希沖突而存在的
- 當發生哈希沖突且連結清單 size 大于門檻值時會擴容,JAVA 8 會将連結清單轉為紅黑樹提高性能
允許 key/value 為 null
HashTable
- 資料結構和 HashMap 一樣
- 不允許 value 為 null
- 線程安全
ArrayMap、SparseArray
ArrayMap
1.基于兩個數組實作,一個存放 hash;一個存放鍵值對。擴容的時候隻需要數組拷貝,不需要重建哈希表
2.記憶體使用率高
3.不适合存大量資料,因為會對 key 進行二分法查找(1000以下)
SparseArray
1.基于兩個數組實作,int 做 key
volatile 關鍵字
- 隻能用來修飾變量,适用修飾可能被多線程同時通路的變量
- 相當于輕量級的 synchronized,volatitle 能保證有序性(禁用指令重排序)、可見性;後者還能保證原子性
- 變量位于主記憶體中,每個線程還有自己的工作記憶體,變量在自己線程的工作記憶體中有份拷貝,線程直接操作的是這個拷貝
- 被 volatile 修飾的變量改變後會立即同步到主記憶體,保持變量的可見性。
雙重檢查單例,為什麼要加 volatile?
1.volatile想要解決的問題是,在另一個線程中想要使用instance,發現instance!=null,但是實際上instance還未初始化完畢這個問題
2.将instance =newInstance();拆分為3句話是。1.配置設定記憶體2.初始化3.将instance指向配置設定的記憶體空
3.volatile可以禁止指令重排序,確定先執行2,後執行3
wait 和 sleep
- sleep 是 Thread 的靜态方法,可以在任何地方調用
- wait 是 Object 的成員方法,隻能在 synchronized 代碼塊中調用,否則會報 IllegalMonitorStateException 非法監控狀态異常
- sleep 不會釋放共享資源鎖,wait 會釋放共享資源鎖
lock 和 synchronized
- synchronized 是 Java 關鍵字,内置特性;Lock 是一個接口
- synchronized 會自動釋放鎖;lock 需要手動釋放,是以需要寫到 try catch 塊中并在 finally 中釋放鎖
- synchronized 無法中斷等待鎖;lock 可以中斷
- Lock 可以提高多個線程進行讀/寫操作的效率
- 競争資源激烈時,lock 的性能會明顯的優于 synchronized
可重入鎖
- 定義:已經擷取到鎖後,再次調用同步代碼塊/嘗試擷取鎖時不必重新去申請鎖,可以直接執行相關代碼
- ReentrantLock 和 synchronized 都是可重入鎖
公平鎖
- 定義:等待時間最久的線程會優先獲得鎖
- 非公平鎖無法保證哪個線程擷取到鎖,synchronized 就是非公平鎖
- ReentrantLock 預設時非公平鎖,可以設定為公平鎖
樂觀鎖和悲觀鎖
- 悲觀鎖:線程一旦得到鎖,其他線程就挂起等待,适用于寫入操作頻繁的場景;synchronized 就是悲觀鎖
- 樂觀鎖:假設沒有沖突,不加鎖,更新資料時判斷該資料是否過期,過期的話則不進行資料更新,适用于讀取操作頻繁的場景
- 樂觀鎖 CAS:Compare And Swap,更新資料時先比較原值是否相等,不相等則表示資料過去,不進行資料更新
- 樂觀鎖實作:AtomicInteger、AtomicLong、AtomicBoolean
死鎖 4 個必要條件
- 互斥
- 占有且等待
- 不可搶占
- 循環等待
synchronized 原理
- 每個對象都有一個螢幕鎖:monitor,同步代碼塊會執行 monitorenter 開始,motnitorexit 結束
- wait/notify 就依賴 monitor 螢幕,是以在非同步代碼塊中執行會報 IllegalMonitorStateException 異常
3、Java 虛拟機&記憶體結構&GC&類加載&四種引用&動态代理
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
1.虛拟機棧/本地方法棧中引用的對象
2.方法區中常量/靜态變量引用的對象
四種引用
- 強引用:不會被回收
- 軟引用:記憶體不足時會被回收
- 弱引用:gc 時會被回收
- 虛引用:無法通過虛引用得到對象,可以監聽對象的回收
ClassLoader
類的生命周期:
1.加載;2.驗證;3.準備;4.解析;5.初始化;6.使用;7.解除安裝
類加載過程:
1.加載:擷取類的二進制位元組流;生成方法區的運作時存儲結構;在記憶體中生成 Class 對象
2.驗證:確定該 Class 位元組流符合虛拟機要求
3.準備:初始化靜态變量
4.解析:将常量池的符号引用替換為直接引用
5.初始化:執行靜态塊代碼、類變量指派
類加載時機:
1.執行個體化對象
2.調用類的靜态方法
3.調用類的靜态變量(放入常量池的常量除外)
類加載器:負責加載 class 檔案
分類:
1.引導類加載器 - 沒有父類加載器
2.拓展類加載器 - 繼承自引導類加載器
3.系統類加載器 - 繼承自拓展類加載器
雙親委托模型:
當要加載一個 class 時,會先逐層向上讓父加載器先加載,加載失敗才會自己加載
為什麼叫雙親?不考慮自定義加載器,系統類加載器需要網上詢問兩層,是以叫雙親
判斷是否是同一個類時,除了類資訊,還必須時同一個類加載器
優點:
- 防止重複加載,父加載器加載過了就沒必要加載了
- 安全,防止篡改核心庫類
動态代理原理及實作
- InvocationHandler 接口,動态代理類需要實作這個接口
- Proxy.newProxyInstance,用于動态建立代理對象
- Retrofit 應用: Retrofit 通過動态代理,為我們定義的請求接口都生成一個動态代理對象,實作請求
4、Android 基礎&性能優化&Framework
Activity 啟動模式
- standard 标準模式
- singleTop 棧頂複用模式,
- 推送點選消息界面
- singleTask 棧内複用模式,
- 首頁
- singleInstance 單例模式,單獨位于一個任務棧中
- 撥打電話界面
細節:
- taskAffinity:任務相關性,用于指定任務棧名稱,預設為應用包名
- allowTaskReparenting:允許轉移任務棧
- 撥打電話界面
View 工作原理
- DecorView (FrameLayout)
- LinearLayout
- titlebar
- Content
- 調用 setContentView 設定的 View
- LinearLayout
ViewRoot 的 performTraversals 方法調用觸發開始 View 的繪制,然後會依次調用:
- performMeasure:周遊 View 的 measure 測量尺寸
- performLayout:周遊 View 的 layout 确定位置
- performDraw:周遊 View 的 draw 繪制
事件分發機制
- 一個 MotionEvent 産生後,按 Activity -> Window -> decorView -> View 順序傳遞,View 傳遞過程就是事件分發,主要依賴三個方法:
- dispatchTouchEvent:用于分發事件,隻要接受到點選事件就會被調用,傳回結果表示是否消耗了目前事件
- onInterceptTouchEvent:用于判斷是否攔截事件,當 ViewGroup 确定要攔截事件後,該事件序列都不會再觸發調用此 ViewGroup 的 onIntercept
- onTouchEvent:用于處理事件,傳回結果表示是否處理了目前事件,未處理則傳遞給父容器處理
- 細節:
- 一個事件序列隻能被一個 View 攔截且消耗
- View 沒有 onIntercept 方法,直接調用 onTouchEvent 處理
- OnTouchListener 優先級比 OnTouchEvent 高,onClickListener 優先級最低
- requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercet 方法的調用
Window 、 WindowManager、WMS、SurfaceFlinger
- Window:抽象概念不是實際存在的,而是以 View 的形式存在,通過 PhoneWindow 實作
- WindowManager:外界通路 Window 的入口,内部與 WMS 互動是個 IPC 過程
- WMS:管理視窗 Surface 的布局和次序,作為系統級服務單獨運作在一個程序
- SurfaceFlinger:将 WMS 維護的視窗按一定次序混合後顯示到螢幕上
View 動畫、幀動畫及屬性動畫
View 動畫:
- 作用對象是 View,可用 xml 定義,建議 xml 實作比較易讀
- 支援四種效果:平移、縮放、旋轉、透明度
幀動畫:
- 通過 AnimationDrawable 實作,容易 OOM
屬性動畫:
- 可作用于任何對象,可用 xml 定義,Android 3 引入,建議代碼實作比較靈活
- 包括 ObjectAnimator、ValuetAnimator、AnimatorSet
- 時間插值器:根據時間流逝的百分比計算目前屬性改變的百分比
- 系統預置勻速、加速、減速等插值器
- 類型估值器:根據目前屬性改變的百分比計算改變後的屬性值
- 系統預置整型、浮點、色值等類型估值器
- 使用注意事項:
- 避免使用幀動畫,容易OOM
- 界面銷毀時停止動畫,避免記憶體洩漏
- 開啟硬體加速,提高動畫流暢性 ,硬體加速:
- 将 cpu 一部分工作分擔給 gpu ,使用 gpu 完成繪制工作
- 從工作分攤和繪制機制兩個方面優化了繪制速度
Handler、MessageQueue、Looper
- Handler:開發直接接觸的類,内部持有 MessageQueue 和 Looper
- MessageQueue:消息隊列,内部通過單連結清單存儲消息
- Looper:内部持有 MessageQueue,循環檢視是否有新消息,有就處理,沒就阻塞
- 如何實作阻塞:通過 nativePollOnce 方法,基于 Linux epoll 事件管理機制
- 為什麼主線程不會因為 Looper 阻塞:系統每 16ms 會發送一個重新整理 UI 消息喚醒
MVC、MVP、MVVM
- MVP:Model:處理資料;View:控制視圖;Presenter:分離 Activity 和 Model
- MVVM:Model:處理擷取儲存資料;View:控制視圖;ViewModel:資料容器
- 使用 Jetpack 元件架構的 LiveData、ViewModel 便捷實作 MVVM
Serializable、Parcelable
- Serializable :Java 序列化方式,适用于存儲和網絡傳輸,serialVersionUID 用于确定反序列化和類版本是否一緻,不一緻時反序列化回失敗
- Parcelable :Android 序列化方式,适用于元件通信資料傳遞,性能高,因為不像 Serializable 一樣有大量反射操作,頻繁 GC
Binder
- Android 程序間通信的中流砥柱,基于用戶端-服務端通信方式
- 使用 mmap 一次資料拷貝實作 IPC,傳統 IPC:使用者A空間->核心->使用者B空間;mmap 将核心與使用者B空間映射,實作直接從使用者A空間->使用者B空間
- BinderPool 可避免建立多 Service
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 系統啟動流程
- 按電源鍵 -> 加載引導程式 BootLoader 到 RAM -> 執行 BootLoader 程式啟動核心 -> 啟動 init 程序 -> 啟動 Zygote 和各種守護程序 ->
- 啟動 System Server 服務程序開啟 AMS、WMS 等 -> 啟動 Launcher 應用程序
App 啟動流程
Launcher 中點選一個應用圖示 -> 通過 AMS 查找應用程序,若不存在就通過 Zygote 程序 fork
程序保活
- 程序優先級:1.前台程序 ;2.可見程序;3.服務程序;4.背景程序;5.空程序
- 程序被 kill 場景:1.切到背景記憶體不足時被殺;2.切到背景廠商省電機制殺死;3.使用者主動清理
- 保活方式:
- 1.Activity 提權:挂一個 1像素 Activity 将程序優先級提高到前台程序
- 2.Service 提權:啟動一個前台服務(API>18會有正在運作通知欄)
- 3.廣播拉活
- 4.Service 拉活
- 5.JobScheduler 定時任務拉活
- 6.雙程序拉活
網絡優化及檢測
- 速度: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 中頻繁建立對象、做耗時操作
記憶體洩漏場景及規避
1.靜态變量、單例強引跟生命周期相關的資料或資源,包括 EventBus
2.遊标、IO 流等資源忘記主動釋放
3.界面相關動畫在界面銷毀時及時暫停
4.内部類持有外部類引用導緻的記憶體洩漏
- handler 内部類記憶體洩漏規避:1.使用靜态内部類+弱引用 2.界面銷毀時清空消息隊列
- 檢測:Android Studio Profiler
LeakCanary 原理
- 通過弱引用和引用隊列監控對象是否被回收
- 比如 Activity 銷毀時開始監控此對象,檢測到未被回收則主動 gc ,然後繼續監控
OOM 場景及規避
- 加載大圖:減小圖檔
- 記憶體洩漏:規避記憶體洩漏
5、Android 子產品化&熱修複&熱更新&打包&混淆&壓縮
Dalvik 和 ART
- Dalvik
- 谷歌設計專用于 Android 平台的 Java 虛拟機,可直接運作 .dex 檔案,适合記憶體和處理速度有限的系統
- JVM 指令集是基于棧的;Dalvik 指令集是基于寄存器的,代碼執行效率更優
- ART
- Dalvik 每次運作都要将位元組碼轉換成機器碼;ART 在應用安裝時就會轉換成機器碼,執行速度更快
- ART 存儲機器碼占用空間更大,空間換時間
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 和安裝時編譯的機器碼
元件化路由實作
ARoute:通過 APT 解析 @Route 等注解,結合 JavaPoet 生成路由表,即路由與 Activity 的映射關系
6、音視訊&FFmpeg&播放器
FFmpeg
基于指令方式實作了一個音視訊編輯 App:
https://github.com/yhaolpz/FFmpegCmd內建編譯了 AAC、MP3、H264 編碼器
播放器原理
視訊播放原理:(mp4、flv)-> 解封裝 -> (mp3/aac、h264/h265)-> 解碼 -> (pcm、yuv)-> 音視訊同步 -> 渲染播放
音視訊同步:
- 選擇參考時鐘源:音頻時間戳、視訊時間戳和外部時間三者選擇一個作為參考時鐘源(一般選擇音頻,因為人對音頻更敏感,ijk 預設也是音頻)
- 通過等待或丢幀将視訊流與參考時鐘源對齊,實作同步
IjkPlayer 原理
內建了 MediaPlayer、ExoPlayer 和 IjkPlayer 三種實作,其中 IjkPlayer 基于 FFmpeg 的 ffplay
音頻輸出方式:AudioTrack、OpenSL ES;視訊輸出方式:NativeWindow、OpenGL ES
如何做好面試突擊,規劃學習方向?
對于程式員來說,要學習的知識内容、技術有太多太多,要想不被環境淘汰就隻有不斷提升自己,從來都是我們去适應環境,而不是環境來适應我們!
這裡附上上述的技術體系圖相關的幾十套騰訊、頭條、阿裡、美團等公司19年的面試題,把技術點整理成了視訊和PDF(實際上比預期多花了不少精力),包含知識脈絡 + 諸多細節,由于篇幅有限,這裡以圖檔的形式給大家展示一部分。
面試題集可以幫助你查漏補缺,有方向有針對性的學習,為之後進大廠做準備。但是如果你僅僅是看一遍,而不去學習和深究。那麼這份面試題對你的幫助會很有限。最終還是要靠資深技術水準說話。網上學習 Android的資料一大堆,但如果學到的知識不成體系,遇到問題時隻是淺嘗辄止,不再深入研究,那麼很難做到真正的技術提升。
建議先制定學習計劃,根據學習計劃把知識點關聯起來,形成一個系統化的知識體系。
學習方向很容易規劃,但是如果隻通過碎片化的學習,對自己的提升是很慢的。我們搜集整理過這幾年位元組跳動,以及騰訊,阿裡,華為,小米等公司的面試題,把面試的要求和技術點梳理成一份大而全的“ Android架構師”面試 Xmind(實際上比預期多花了不少精力),包含知識脈絡 + 分支細節。
我們在搭建這些技術架構的時候,還整理了系統的進階進階教程,會比自己碎片化學習效果強太多;
上述【Android面試題解析】以及【Android進階架構進階視訊】可以 免費下載下傳擷取
下載下傳位址:
https://shimo.im/docs/YHJtVkC3y6qgp9xC當程式員容易,當一個優秀的程式員是需要不斷學習的,從初級程式員到進階程式員,從初級架構師到資深架構師,或者走向管理,從技術經理到技術總監,每個階段都需要掌握不同的能力。早早确定自己的職業方向,才能在工作和能力提升中甩開同齡人。