原文地址:https://juejin.cn/post/6844904190314037255
最新 Android 面試點(diǎn)梳理
目錄:
- 網(wǎng)絡(luò):分層模型榔幸、TCP闹司、UDP税课、HTTP、HTTPS
- 算法:數(shù)據(jù)結(jié)構(gòu)儡首、常用算法
- Java 基礎(chǔ):StringBuilder、泛型擦除、Exception恋拍、IO隧土、容器
- Java 同步:volatile提针、wait命爬、synchronized曹傀、可重入鎖、樂觀鎖饲宛、死鎖
- Java 設(shè)計(jì)模式:六大原則皆愉、23 種設(shè)計(jì)模式、動(dòng)態(tài)代理
- Java 虛擬機(jī):內(nèi)存模型艇抠、內(nèi)存結(jié)構(gòu)幕庐、GC、四種引用家淤、ClassLoader
- Android 基礎(chǔ):Activity异剥、View 繪制、動(dòng)畫絮重、Window冤寿、SurfaceView、事件分發(fā)
- Android 通信:Handler青伤、Parcelable督怜、IPC、Binder
- Android 系統(tǒng):系統(tǒng)架構(gòu)狠角、Dalvik号杠、ART、系統(tǒng)啟動(dòng)丰歌、類加載器姨蟋、Apk 打包、Apk 安裝
- Android 優(yōu)化:網(wǎng)絡(luò)優(yōu)化立帖、卡頓優(yōu)化芬探、內(nèi)存優(yōu)化、瘦包厘惦、內(nèi)存泄漏偷仿、ANR、Native Crash
- 其他:解析 XML宵蕉、進(jìn)程痹途玻活、播放器羡玛、Lint别智、CI、CD稼稿、AOP薄榛、JetPack
網(wǎng)絡(luò):分層模型讳窟、TCP、UDP敞恋、HTTP丽啡、HTTPS
分層模型
- 應(yīng)用層:負(fù)責(zé)處理特定的應(yīng)用程序細(xì)節(jié),如 HTTP硬猫、FTP补箍、DNS
- 運(yùn)輸層:為兩臺(tái)主機(jī)提供端到端的基礎(chǔ)通信,如 TCP啸蜜、UDP
- 網(wǎng)絡(luò)層:控制分組傳輸坑雅、路由選擇等,如 IP
- 鏈路層:操作系統(tǒng)設(shè)備驅(qū)動(dòng)程序衬横、網(wǎng)卡相關(guān)接口
UDP
- UDP 頭結(jié)構(gòu):來源端口裹粤、目的端口、長(zhǎng)度域蜂林、校驗(yàn)和
- 特點(diǎn):不可靠遥诉、無序、面向報(bào)文悉尾、速度快突那、輕量
- 適用場(chǎng)景:適用于即時(shí)通訊、視頻通話等
- 應(yīng)用:DHCP构眯、DNS愕难、QUCI、VXLAN惫霸、GTP-U猫缭、TFTP、SNMP
TCP
- TCP 頭結(jié)構(gòu):來源端口壹店、目的端口猜丹、序號(hào)、確認(rèn)序號(hào)硅卢、SYN/ACK 等狀態(tài)位射窒、窗口大小、校驗(yàn)和将塑、緊急指針
- 特點(diǎn):面向字節(jié)流脉顿、有擁塞和流量控制、可靠点寥、有序艾疟、速度慢、較重量,通過滑動(dòng)窗口實(shí)現(xiàn)流量控制蔽莱、用塞控制
- 適用場(chǎng)景:文件傳輸弟疆、瀏覽器等
- 應(yīng)用: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(我能聽到稠集,開始吧)
兩方都要能確保:我說的話,你能聽到饥瓷;你說的話剥纷,我能聽到。所以需要三次握手
復(fù)制代碼
- 四次揮手:
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(我知道了,結(jié)束吧)
S 收到 C 結(jié)束的消息后 S 可能還沒說完纱皆,沒法立即回復(fù)結(jié)束標(biāo)示湾趾,只能等說完后再告訴 C :我說完了
復(fù)制代碼
HTTP
- 超文本傳輸協(xié)議,明文傳輸派草,默認(rèn) 80 端口
- POST 和 GET:Get 參數(shù)放在 url 中搀缠;Post 參數(shù)放在 request Body 中
- 訪問網(wǎng)頁(yè)過程:DNS 域名解析、TCP 三次握手建立連接澳眷、發(fā)起 HTTP 請(qǐng)求
HTTPS
- 默認(rèn) 443 端口胡嘿,使用 SSL 協(xié)議對(duì) HTTP 傳輸數(shù)據(jù)進(jìn)行了加密,安全
- 加密過程:Client/Server 通過非對(duì)稱加密生成密鑰钳踊,然后用這個(gè)密鑰去對(duì)稱加密傳輸數(shù)據(jù)
算法:數(shù)據(jù)結(jié)構(gòu)衷敌、常用算法
數(shù)據(jù)結(jié)構(gòu)
- 數(shù)組勿侯、鏈表
- 棧、隊(duì)列
- 散列表
- 樹缴罗、堆助琐、圖
常用算法
- 排序
- 雙指針、滑動(dòng)窗口面氓、字符串
- 遞歸兵钮、分治、二分
- 回溯舌界、貪心掘譬、動(dòng)態(tài)規(guī)劃
Java 基礎(chǔ):StringBuilder、泛型擦除呻拌、Exception葱轩、IO、容器
StringBuilder
- StringBuffer 線程安全藐握,StringBuilder 線程不安全
- +實(shí)際上是用 StringBuilder 來實(shí)現(xiàn)的靴拱,所以非循環(huán)體可以直接用 +,循環(huán)體不行猾普,因?yàn)闀?huì)頻繁創(chuàng)建 StringBuilder
- String.concat 實(shí)質(zhì)是 new String 袜炕,效率也低,耗時(shí)排序:StringBuilder < StringBuffer < concat < +
泛型擦除
- 修飾成員變量等類結(jié)構(gòu)相關(guān)的泛型不會(huì)被擦除
- 容器類泛型會(huì)被擦除
Exception 和 Error
- Exception 和 Error 都繼承自 Throwable
- Error 大部分是指不可恢復(fù)的錯(cuò)誤狀態(tài)初家,比如 OOM偎窘,所以也不需要捕獲
- Exception 分為 CheckedException 和 UnCheckedException
- CheckedException:必須顯式捕獲,受編譯器檢查笤成,比如 io 操作
- UnCheckedException:不用顯示捕獲评架,比如空指針、數(shù)組越界等
IO 炕泳、 NIO纵诞、 OKIO
- IO 是面向流的,一次一個(gè)字節(jié)的處理培遵,NIO 是面向緩沖區(qū)的浙芙,一次產(chǎn)生或消費(fèi)一個(gè)數(shù)據(jù)塊
- IO 是阻塞的,NIO 是非阻塞的
- NIO 支持內(nèi)存映射方式
- okio 相比 io 和 nio籽腕,api 更簡(jiǎn)單易用
- okio 支持超時(shí)機(jī)制
- okio 引入 ByteString 空間換時(shí)間提高性能
- okio 采用 segment 機(jī)制進(jìn)行內(nèi)存共享嗡呼,節(jié)省 copy 時(shí)間消耗
ArrayList、LinkedList
- ArrayList
- 基于數(shù)組實(shí)現(xiàn)皇耗,查找快:o(1)南窗,增刪慢:o(n)
- 初始容量為10,擴(kuò)容通過 System.arrayCopy 方法
- LinkedList
- 基于雙向鏈表實(shí)現(xiàn),查找慢:o(n)万伤,增刪快:o(1)
- 封裝了隊(duì)列和棧的調(diào)用
HashMap 窒悔、HashTable、HashSet
-
HashMap(允許 key/value 為 null)
- 基于數(shù)組和單向鏈表實(shí)現(xiàn)敌买,數(shù)組是 HashMap 的主體简珠;鏈表是為解決哈希沖突而存在的,存放的是key和value結(jié)合的實(shí)體
- 數(shù)組索引通過 key.hashCode(還會(huì)二次 hash) 得到虹钮,在鏈表上通過 key.equals 索引
- 哈希沖突落在同一個(gè)桶中時(shí)聋庵,直接放在鏈表頭部(java1.8后放到尾部)
- JAVA 8 中鏈表數(shù)量大于 8 時(shí)會(huì)轉(zhuǎn)為紅黑樹存儲(chǔ),查找時(shí)間由 O(n) 變?yōu)?O(logn)
- 數(shù)組長(zhǎng)度總是2的n次方:這樣就能通過位運(yùn)算實(shí)現(xiàn)取余芙粱,從而讓 index 能落在數(shù)組長(zhǎng)度范圍內(nèi)
- 加載因子(默認(rèn)0.75)表示添加到多少填充比時(shí)進(jìn)行擴(kuò)容祭玉,填充比大:鏈表較長(zhǎng),查找慢宅倒;填充比腥林妗:鏈表短屯耸,查找快
- 擴(kuò)容時(shí)直接創(chuàng)建原數(shù)組兩倍的長(zhǎng)度拐迁,然后將原有對(duì)象再進(jìn)行hash找到新的index,重新放
-
HashTable(不允許 key/value 為 null)
- 數(shù)據(jù)結(jié)構(gòu)和 HashMap 一樣
- 線程安全
-
HashSet
- 基于 HashMap 實(shí)現(xiàn)疗绣,元素就是 HashMap 的 key线召,Value 傳入了一個(gè)固定值
ArrayMap、SparseArray
-
ArrayMap
- 基于兩個(gè)數(shù)組實(shí)現(xiàn)多矮,一個(gè)存放 hash缓淹;一個(gè)存放鍵值對(duì)
- 存放 hash 的數(shù)組是有序的,查找時(shí)使用二分法查找
- 發(fā)生哈希沖突時(shí)鍵值對(duì)數(shù)組里連續(xù)存放塔逃,查找時(shí)也是通過 key.equals索引讯壶,找不到時(shí)先向后再向前遍歷相同hash值的鍵值對(duì)數(shù)組
- 擴(kuò)容時(shí)不像 HashMap 直接 double,內(nèi)存利用率高湾盗;也不需要重建哈希表伏蚊,只需要調(diào)用 system.arraycopy 數(shù)組拷貝,性能較高
- 不適合存大量數(shù)據(jù)(1000以下)格粪,因?yàn)閿?shù)據(jù)量大的時(shí)候二分查找相比紅黑樹會(huì)慢很多
-
SparseArray
- 基于 ArrayMap躏吊,key 只能是特定類型
Concurrent 集合
- ConcurrentHashMap
- 數(shù)據(jù)結(jié)構(gòu)跟 HashMap 一樣,還是數(shù)組加鏈表
- 采用 segment 分段鎖技術(shù)帐萎,不像 HashTable 無腦直接同步 put 和 get 操作
- get 操作沒有加鎖比伏,因?yàn)?value 用 volatile 修飾來保證可見行,性能很高
- java1.8 后去除分段鎖疆导,采用 CAS 樂觀鎖加 synchronized 來實(shí)現(xiàn)
LRUCache 原理
- 基于訪問順序排序的 LinkedHashMap 實(shí)現(xiàn)赁项,最近訪問的會(huì)排在最后
Java 同步:volatile、wait、synchronized悠菜、可重入鎖紫新、樂觀鎖、死鎖
volatile 關(guān)鍵字
- 只能用來修飾變量李剖,適用修飾可能被多線程同時(shí)訪問的變量
- 相當(dāng)于輕量級(jí)的 synchronized芒率,volatitle 能保證有序性(禁用指令重排序)、可見性
- 變量位于主內(nèi)存中篙顺,每個(gè)線程還有自己的工作內(nèi)存偶芍,變量在自己線程的工作內(nèi)存中有份拷貝,線程直接操作的是這個(gè)拷貝
- 被 volatile 修飾的變量改變后會(huì)立即同步到主內(nèi)存德玫,保持變量的可見性
- 雙重檢查單例匪蟀,為什么要加 violate?
- volatile想要解決的問題是宰僧,在另一個(gè)線程中想要使用instance材彪,發(fā)現(xiàn)instance!=null,但是實(shí)際上instance還未初始化完畢這個(gè)問題琴儿。將instance = newInstance();拆分為3句話是段化。1.分配內(nèi)存2.初始化3.將instance指向分配的內(nèi)存空間,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ì)釋放共享資源鎖
wait鼓鲁、notify蕴轨、notifyAll
- 鎖池:某個(gè)對(duì)象的鎖已被線程A擁有,其他線程要執(zhí)行該對(duì)象的 synchronized 方法獲取鎖時(shí)就會(huì)進(jìn)入該對(duì)象的鎖池骇吭,鎖池中的線程回去競(jìng)爭(zhēng)該對(duì)象的鎖
- 等待池:某個(gè)線程調(diào)用了某個(gè)對(duì)象的 wait 方法橙弱,該線程就會(huì)釋放該對(duì)象的鎖,進(jìn)入該對(duì)象的等待池绵跷,等待池中的線程不會(huì)去競(jìng)爭(zhēng)該對(duì)象的鎖
- 調(diào)用 notify 會(huì)隨機(jī)喚醒等待池中的一個(gè)線程膘螟,喚醒后會(huì)進(jìn)入到鎖池
- 調(diào)用 notifyAll 會(huì)喚醒等待池中的所有線程,喚醒后會(huì)都進(jìn)入到鎖池
lock 和 synchronized
- synchronized 是 Java 關(guān)鍵字碾局,內(nèi)置特性荆残;Lock 是一個(gè)接口
- synchronized 會(huì)自動(dòng)釋放鎖;lock 需要手動(dòng)釋放净当,所以需要寫到 try catch 塊中并在 finally 中釋放鎖
- synchronized 無法中斷等待鎖内斯;lock 可以中斷
- Lock 可以提高多個(gè)線程進(jìn)行讀/寫操作的效率
- 競(jìng)爭(zhēng)資源激烈時(shí)蕴潦,lock 的性能會(huì)明顯的優(yōu)于 synchronized
Synchronized 原理
- 每個(gè)對(duì)象都有一個(gè)監(jiān)視器鎖:monitor,同步代碼塊會(huì)執(zhí)行 monitorenter 開始俘闯,motnitorexit 結(jié)束
- Wait/notify 就依賴 monitor 監(jiān)視器潭苞,所以在非同步代碼塊中執(zhí)行會(huì)報(bào) IllegalMonitorStateException 異常
可重入鎖
- 定義:已經(jīng)獲取到鎖后,再次調(diào)用同步代碼塊/嘗試獲取鎖時(shí)不必重新去申請(qǐng)鎖真朗,可以直接執(zhí)行相關(guān)代碼
- ReentrantLock 和 synchronized 都是可重入鎖
公平鎖
- 定義:等待時(shí)間最久的線程會(huì)優(yōu)先獲得鎖
- 非公平鎖無法保證哪個(gè)線程獲取到鎖此疹,synchronized 就是非公平鎖
- ReentrantLock 默認(rèn)時(shí)非公平鎖,可以設(shè)置為公平鎖
樂觀鎖和悲觀鎖
- 悲觀鎖:線程一旦得到鎖遮婶,其他線程就掛起等待蝗碎,適用于寫入操作頻繁的場(chǎng)景;synchronized 就是悲觀鎖
- 樂觀鎖:假設(shè)沒有沖突旗扑,不加鎖蹦骑,更新數(shù)據(jù)時(shí)判斷該數(shù)據(jù)是否過期,過期的話則不進(jìn)行數(shù)據(jù)更新臀防,適用于讀取操作頻繁的場(chǎng)景
- 樂觀鎖 CAS:Compare And Swap眠菇,更新數(shù)據(jù)時(shí)先比較原值是否相等,不相等則表示數(shù)據(jù)過去袱衷,不進(jìn)行數(shù)據(jù)更新
- 樂觀鎖實(shí)現(xiàn):AtomicInteger捎废、AtomicLong、AtomicBoolean
死鎖 4 個(gè)必要條件
- 互斥
- 占有且等待
- 不可搶占
- 循環(huán)等待
Java 設(shè)計(jì)模式:六大原則祟昭、23 種設(shè)計(jì)模式缕坎、動(dòng)態(tài)代理
六大原則
- 開閉原則:對(duì)拓展開放,對(duì)修改關(guān)閉
- 單一指責(zé)原則:一個(gè)類指責(zé)單一
- 里氏替換原則:引用基類的地方都能替換成子類對(duì)象
- 依賴倒置原則:高層次模塊不依賴低層次模塊的具體實(shí)現(xiàn)篡悟,抽象不應(yīng)該依賴細(xì)節(jié)
- 接口隔離原則:類之間的依賴關(guān)系應(yīng)該建立在最小的接口上
- 迪米特原則:一個(gè)對(duì)象對(duì)其他對(duì)象應(yīng)該有盡量少的了解
Java 23 種設(shè)計(jì)模式(按目的分類為:5+7+11)
1995 年 GoF(四人組)出了一本設(shè)計(jì)模式的書,收錄了 23 種設(shè)計(jì)模式匾寝,樹立設(shè)計(jì)模式里程碑搬葬,也叫:GoF 設(shè)計(jì)模式
- 創(chuàng)建型(5):描述怎么創(chuàng)建對(duì)象
- 1.單例模式
- 2.原型模式:對(duì)象的拷貝
- 3.建造者模式
- 4.工廠模式:建立一個(gè)工廠方法來制造新的對(duì)象
- 5.抽象工廠模式:
- 結(jié)構(gòu)型(7):描述如何將類或?qū)ο蟀茨撤N規(guī)則組成更大的結(jié)構(gòu)
- 1.橋接模式:對(duì)于兩個(gè)或以上緯度獨(dú)立變化的場(chǎng)景,將抽象與具體實(shí)現(xiàn)分離艳悔,實(shí)例:用不同顏色畫不同形狀
- 2.外觀模式:對(duì)外有一個(gè)統(tǒng)一接口急凰,外部不用關(guān)心內(nèi)部子系統(tǒng)的具體實(shí)現(xiàn),這是"迪米特原則"的典型應(yīng)用
- 3.適配器模式:改變類的接口猜年,使原本由于接口不匹配而無法一起工作的兩個(gè)類能夠在一工作抡锈,實(shí)例:RecycleView 的 Adapter 不管什么類型的 View 都返回 ViewHolder
- 4.代理模式:由代理對(duì)象控制對(duì)原對(duì)象的引用,包括靜態(tài)代理和動(dòng)態(tài)代理
- 5.組合模式:將對(duì)象組成樹形結(jié)構(gòu)乔外,用于對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性床三,實(shí)例:ViewGroup
- 6.裝飾模式:對(duì)對(duì)象包裝一層,動(dòng)態(tài)的增加一些額外功能杨幼,實(shí)例:ContextWrapper 包裝 Context
- 7.享元模式:復(fù)用對(duì)象撇簿,實(shí)例:java 的常量池(比如 String)聂渊,線程池,Message.obtain 等
- 行為型(11):描述類或?qū)ο笾g怎么相互協(xié)作四瘫,怎樣分配指責(zé)
- 1.觀察者模式:一對(duì)多依賴關(guān)系汉嗽,多個(gè)觀察者可以同時(shí)監(jiān)聽某一個(gè)對(duì)象,實(shí)例:jetpack 的 lifeCycle 添加生命周期觀察者
- 2.中介者模式:定義一個(gè)中介對(duì)象封裝一系列對(duì)象的交互找蜜,解耦這些對(duì)象饼暑,實(shí)例:MVP 的 P
- 3.訪問者模式:將作用于某數(shù)據(jù)結(jié)構(gòu)中各元素的操作分離出來封裝成獨(dú)立的類,對(duì)這些元素添加新的操作洗做,但不改變?cè)瓟?shù)據(jù)結(jié)構(gòu)撵孤,實(shí)例:asm 中的 classVisitor 中再分別對(duì)類注解、變量、方法等進(jìn)行處理
- 4.狀態(tài)模式:行為由狀態(tài)決定,不同狀態(tài)下由不同行為硫兰,與策略模式類似萧福,實(shí)例:不同狀態(tài)下有同一種操作的不同行為的子類實(shí)現(xiàn)
- 5.命令模式:將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象發(fā)出,交給別的對(duì)象去處理請(qǐng)求煌贴,實(shí)例:Handler 發(fā)送定義好的消息事件
- 6.策略模式:將一系列的算法封裝起來,方便替換,實(shí)例:動(dòng)畫的時(shí)間插值器
- 7.責(zé)任鏈模式:讓多個(gè)對(duì)象都有機(jī)會(huì)處理一個(gè)事件影钉,實(shí)例:View 事件傳遞機(jī)制
- 8.備忘錄模式:保存對(duì)象之前的狀態(tài),方便后面恢復(fù)
- 9.迭代器模式:提供一種方法遍歷容器中的元素掘剪,而不需要暴露該對(duì)象的內(nèi)部表示平委,實(shí)例:集合的迭代器
- 10.解釋器模式:多次出現(xiàn)的問題有一定規(guī)律,就可以歸納成一種簡(jiǎn)單的語(yǔ)言來解釋夺谁,實(shí)例:AndroidManifest 文件廉赔、GLES 著色器語(yǔ)言
- 11.模版方法模式:定義一套固定步驟,方便直接執(zhí)行匾鸥,實(shí)例:AsyncTask
動(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 通過動(dòng)態(tài)代理勿负,為我們定義的請(qǐng)求接口都生成一個(gè)動(dòng)態(tài)代理對(duì)象馏艾,實(shí)現(xiàn)請(qǐng)求
JVM:內(nèi)存模型、內(nèi)存結(jié)構(gòu)奴愉、GC琅摩、四種引用、ClassLoader
JVM
- 定義:可以理解成一個(gè)虛構(gòu)的計(jì)算機(jī)锭硼,解釋自己的字節(jié)碼指令集映射到本地 CPU 或 OS 的指令集房资,上層只需關(guān)注 Class 文件,與操作系統(tǒng)無關(guān)账忘,實(shí)現(xiàn)跨平臺(tái)
- Kotlin 就是能解釋成 Class 文件志膀,所以可以跑在 JVM 上
JVM 內(nèi)存模型
- Java 多線程之間是通過共享內(nèi)存來通信的熙宇,每個(gè)線程都有自己的本地內(nèi)存
- 共享變量存放于主內(nèi)存中,線程會(huì)拷貝一份共享變量到本地內(nèi)存
- volatile 關(guān)鍵字就是給內(nèi)存模型服務(wù)的溉浙,用來保證內(nèi)存可見性和順序性
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)入新生代;通過復(fù)制算法回收對(duì)象替劈;老年代:新建大對(duì)象及老對(duì)象會(huì)進(jìn)入老年代寄雀;通過標(biāo)記-清除算法回收對(duì)象。
- 2.方法區(qū)中的類信息陨献、常量池
- 判斷一個(gè)對(duì)象是否可被回收:
- 1.引用計(jì)數(shù)法:有循環(huán)引用的缺點(diǎn)
- 2.可達(dá)性分析法:從 GC ROOT 開始搜索盒犹,不可達(dá)的對(duì)象都是可以被回收的。其中 GC ROOT 包括虛擬機(jī)棧/本地方法棧中引用的對(duì)象眨业、方法區(qū)中常量/靜態(tài)變量引用的對(duì)象急膀。
Minor GC/Major GC/Full GC
- Minor GC(Young GC):即新生代(分為一個(gè) Eden 區(qū)和兩個(gè) Survivor 區(qū))的垃圾回收
- Eden 區(qū)無用對(duì)象被回收,存活對(duì)象會(huì)移到 Survivor 區(qū)
- Survivor 區(qū)的存活對(duì)象會(huì)被復(fù)制到另一個(gè) Survivor 區(qū)坛猪,復(fù)制次數(shù)也記做年齡脖阵,年齡足夠大時(shí)(15)會(huì)移到老年代
- 如果 Survivor 區(qū)已滿,則存活對(duì)象會(huì)被提前移動(dòng)到老年代(過早提升)墅茉,如果老年代也無法容納,則會(huì)觸發(fā) Full GC(提升失斘啬拧)
- 老年代的對(duì)象可能引用新生代對(duì)象就斤,所以這個(gè)引用會(huì)被作為 GC Roots
- Major GC:通常是跟 Full GC 等價(jià)的,回收整個(gè)堆
- Full GC:回收整個(gè)堆蘑辑,包括新生代和老年代
- 當(dāng)要在老年代分配空間但無法容納時(shí)觸發(fā)
- 當(dāng)主動(dòng)調(diào)用 System.gc 時(shí)觸發(fā)
四種引用
- 強(qiáng)引用:不會(huì)被回收
- 軟引用:內(nèi)存不足時(shí)會(huì)被回收
- 弱引用:gc 時(shí)會(huì)被回收
- 虛引用:無法通過虛引用得到對(duì)象洋机,可以監(jiān)聽對(duì)象的回收
ClassLoader
- 類的生命周期: 1.加載;2.驗(yàn)證洋魂;3.準(zhǔn)備绷旗;4.解析喜鼓;5.初始化;6.使用衔肢;7.卸載
- 類加載過程: 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)類加載器 - 沒有父類加載器 2.拓展類加載器 - 繼承自引導(dǎo)類加載器 3.系統(tǒng)類加載器 - 繼承自拓展類加載器
- 雙親委托模型:
- 當(dāng)要加載一個(gè) class 時(shí)隅忿,會(huì)先逐層向上讓父加載器先加載,加載失敗才會(huì)自己加載
- 為什么叫雙親邦尊?不考慮自定義加載器背桐,系統(tǒng)類加載器需要網(wǎng)上詢問兩層,所以叫雙親
- 判斷是否是同一個(gè)類時(shí)蝉揍,除了類信息链峭,還必須時(shí)同一個(gè)類加載器
- 優(yōu)點(diǎn):防止重復(fù)加載,父加載器加載過了就沒必要加載了又沾;安全弊仪,防止篡改核心庫(kù)類
Android 基礎(chǔ):Activity、View 繪制捍掺、動(dòng)畫撼短、Window、SurfaceView挺勿、事件分發(fā)
Activity 生命周期
- A 打開 B 界面曲横,會(huì)先執(zhí)行 A 的 onPause,再執(zhí)行 B 的 onCreate不瓶、onStart禾嫉、onResume,再執(zhí)行 A 的 onStop
- B 界面的打開依賴 A 界面 onPause 方法執(zhí)行完蚊丐,所以不要在 onPause 中做耗時(shí)操作
Activity 啟動(dòng)模式
- standard 標(biāo)準(zhǔn)模式
- singleTop 棧頂復(fù)用模式熙参,適用于推送點(diǎn)擊消息界面
- singleTask 棧內(nèi)復(fù)用模式,適用于 App 首頁(yè)
- singleInstance 單例模式麦备,單獨(dú)位于一個(gè)任務(wù)棧中孽椰,適用于撥打電話界面
- 細(xì)節(jié):
- taskAffinity:任務(wù)相關(guān)性,用于指定任務(wù)棧名稱凛篙,默認(rèn)為應(yīng)用包名
- allowTaskReparenting:允許轉(zhuǎn)移任務(wù)棧
View 工作原理
- ViewRoot 的 performTraversals 方法調(diào)用觸發(fā)開始 View 的繪制黍匾,然后會(huì)依次調(diào)用:
- performMeasure:遍歷 View 的 measure 測(cè)量尺寸
- performLayout:遍歷 View 的 layout 確定位置
- performDraw:遍歷 View 的 draw 繪制
MeasureSpec 測(cè)量規(guī)則
- EXACTLY:父 View 指定了子 View 確切的大小
- AT_MOST:父 View 指定一個(gè)大小,子 View 不能超過這個(gè)值
- UNSPECIFIEND: 父 View 不對(duì)子 View 有任何限制
View 動(dòng)畫呛梆、幀動(dòng)畫及屬性動(dòng)畫
- View 動(dòng)畫:
- 作用對(duì)象是 View锐涯,可用 xml 定義,建議 xml 實(shí)現(xiàn)比較易讀
- 支持四種效果:平移填物、縮放纹腌、旋轉(zhuǎn)霎终、透明度
- 幀動(dòng)畫:
- 通過 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)存泄漏;開啟硬件加速徒河,提高動(dòng)畫流暢性
- 硬件加速原理:將 cpu 一部分工作分擔(dān)給 gpu 系馆,使用 gpu 完成繪制工作;從工作分?jǐn)偤屠L制機(jī)制兩個(gè)方面優(yōu)化了繪制速度
Window 顽照、WindowManager由蘑、WMS、SurfaceFlinger
- WIndow:抽象概念不是實(shí)際存在的代兵,而是以 View 的形式存在尼酿,通過 PhoneWindow 實(shí)現(xiàn)
- WindowManager:外界訪問 Window 的入口,內(nèi)部與 WMS 交互是個(gè) IPC 過程
- WMS:管理窗口 Surface 的布局和次序植影,作為系統(tǒng)級(jí)服務(wù)單獨(dú)運(yùn)行在一個(gè)進(jìn)程
- SurfaceFlinger:將 WMS 維護(hù)的窗口按一定次序混合后顯示到屏幕上
SurfaceView裳擎、TextureView、SurfaceTexture思币、GLSurfaceView
- SurfaceView:使用雙緩沖機(jī)制鹿响,有自己的 surface,在一個(gè)獨(dú)立的線程里繪制谷饿,Android7.0之前不能平移抢野、縮放
- TextureView:持有 SurfaceTexture,將圖像處理為 OpenGL 紋理更新到 HardwareLayer各墨,必須開啟硬件加速,Android5.0之前在主線程渲染启涯,之后有獨(dú)立的渲染線程贬堵,可以平移恃轩、旋轉(zhuǎn)、縮放
- SurfaceTexture:將圖像流轉(zhuǎn)為 OpenGL 外部紋理黎做,不直接顯示
- GLSurfaceView:加入 EGL 管理叉跛,自帶 GL 上下文和 GL 渲染線程
事件分發(fā)機(jī)制
- 一個(gè) MotionEvent 產(chǎn)生后,按 Activity -> Window -> decorView -> View 順序傳遞蒸殿,View 傳遞過程就是事件分發(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 沒有 onIntercept 方法,直接調(diào)用 onTouchEvent 處理
- OnTouchListener 優(yōu)先級(jí)比 OnTouchEvent 高霞玄,onClickListener 優(yōu)先級(jí)最低
- requestDisallowInterceptTouchEvent 可以屏蔽父容器 onIntercept 方法的調(diào)用
Android 通信:Handler骤铃、Parcelable、IPC坷剧、Binder
Handler惰爬、MessageQueue、Looper 及 postDelayed 原理
- Handler:開發(fā)直接接觸的類惫企,內(nèi)部持有 MessageQueue 和 Looper
- MessageQueue:消息隊(duì)列撕瞧,內(nèi)部通過單鏈表存儲(chǔ)消息
- Looper:內(nèi)部持有 MessageQueue,循環(huán)查看是否有新消息雅任,有就處理风范,沒就阻塞
- postDelayed 其實(shí)就是調(diào)用 postAtTime 實(shí)現(xiàn)的,傳入的時(shí)間戳基于 SystemClock.uptimeMillis沪么,即 boot 時(shí)間
- 進(jìn)一步會(huì)調(diào)用 MessageQueue#enqueueMessage 將消息插入到隊(duì)列
- 插入消息時(shí)會(huì)根據(jù)消息執(zhí)行時(shí)刻 Message#when 來決定插入到什么位置硼婿,when 為 0 或最早執(zhí)行就會(huì)插入到鏈表頭,否則按執(zhí)行時(shí)刻排序插入
- 插入后如果正在阻塞則會(huì)嘗試喚醒禽车,插入到頭部則會(huì)喚醒寇漫,插入到隊(duì)列中則再根據(jù)其他條件判斷是否需要喚醒
- Looper#loop 中調(diào)用 MessageQueue#next 取消息,next 方法除非是即將銷毀時(shí)會(huì)返回 null殉摔,否則就會(huì)返回消息州胳,沒有消息就阻塞。如果當(dāng)前時(shí)刻還沒到消息的執(zhí)行時(shí)刻 when逸月,就會(huì)再阻塞這個(gè)時(shí)間差的時(shí)間
- 阻塞是調(diào)用 nativePollOnce 實(shí)現(xiàn)栓撞,基于 Linux epoll 事件管理機(jī)制
- Looper#loop 中取出消息后通過 Message#target 拿到 handler,然后調(diào)用 Handler#dispatchMessage 分發(fā)處理消息
Serializable、Parcelable
- Serializable :Java 序列化方式瓤湘,適用于存儲(chǔ)和網(wǎng)絡(luò)傳輸瓢颅,serialVersionUID 用于確定反序列化和類版本是否一致,不一致時(shí)反序列化回失敗
- Parcelable :Android 序列化方式弛说,適用于組件通信數(shù)據(jù)傳遞挽懦,性能高,因?yàn)椴幌?Serializable 一樣有大量反射操作
Linux IPC 方式
- 管道
- socket
- 信號(hào)量:常作為一種鎖機(jī)制木人,防止某進(jìn)程正在訪問共享資源時(shí)信柿,其他進(jìn)程也訪問該資源。因此醒第,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段
- 信號(hào):不適用于信息交換渔嚷,更適用于進(jìn)程中斷控制,比如非法內(nèi)存訪問淘讥,殺死某個(gè)進(jìn)程等(Android 中的 Kill Process 采用的就是 signal(信號(hào))機(jī)制)
- 消息隊(duì)列:信息復(fù)制兩次圃伶,額外的 CPU 消耗;不合適頻繁或信息量大的通信
- 共享內(nèi)存:無須復(fù)制蒲列,共享緩沖區(qū)直接付附加到進(jìn)程虛擬地址空間窒朋,速度快;但進(jìn)程間的同步問題操作系統(tǒng)無法實(shí)現(xiàn)蝗岖,必須各進(jìn)程利用同步工具解決
Binder
- Android 中基于 C/S 結(jié)構(gòu)的一種面向?qū)ο蟮倪M(jìn)程間通信的機(jī)制
- 主要用在 system_server 進(jìn)程與上層 App 層的 IPC 交互
- 包含:Client,Server,Binder 驅(qū)動(dòng)和 ServiceManager 四部分
Android 為什么選擇 binder
- 性能:使用 mmap 一次數(shù)據(jù)拷貝實(shí)現(xiàn) IPC侥猩,傳統(tǒng) IPC:用戶 A 空間->內(nèi)核->用戶 B 空間;mmap 將內(nèi)核與用戶 B 空間映射抵赢,實(shí)現(xiàn)直接從用戶 A 空間->用戶B空間欺劳,而 Linux 的管道、消息隊(duì)列铅鲤、Socket 都需要拷貝兩次划提,binder 僅次于共享內(nèi)存
- 穩(wěn)定性:基于C/S架構(gòu),架構(gòu)清晰邢享,穩(wěn)定性好鹏往,不像共享內(nèi)存實(shí)現(xiàn)方式復(fù)雜,需要充分考慮訪問臨界資源的并發(fā)同步問題
- 安全:傳統(tǒng)Linux IPC的接收方無法獲得對(duì)方進(jìn)程可靠的UID/PID骇塘,從而無法鑒別對(duì)方身份
Android 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)聽客戶端連接請(qǐng)求
- 客戶端:綁定服務(wù)端 Service 哄辣,綁定成功后拿到服務(wù)端 Binder 對(duì)象轉(zhuǎn)為 AIDL 接口調(diào)用
- RemoteCallbackList 實(shí)現(xiàn)跨進(jìn)程接口監(jiān)聽,同個(gè) Binder 對(duì)象做 key 存儲(chǔ)客戶端注冊(cè)的 listener
- 監(jiān)聽 Binder 斷開: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):系統(tǒng)架構(gòu)超全、Dalvik、ART邓馒、系統(tǒng)啟動(dòng)嘶朱、類加載器、Apk 打包光酣、Apk 安裝
Android 系統(tǒng)架構(gòu)
[圖片上傳失敗...(image-52567d-1626161018037)]
<figcaption style="display: block;"></figcaption>
- 應(yīng)用層
- Framework 框架層
- 本地 Native 庫(kù)和 Android 運(yùn)行時(shí)環(huán)境
- HAL
- Linux 內(nèi)核
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í)間
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)程開啟 AMS、WMS 等 -> 啟動(dòng) Launcher 應(yīng)用進(jìn)程
Android 類加載器
- BootClassLoader(加載 Framework 級(jí)別的類)
- PathClassLoader(加載系統(tǒng)類和 data/app 應(yīng)用目錄下的 dex 文件)
- DexClassLoader(加載自定義的 dex 文件或 jar拷泽,支持從 sd 卡中進(jìn)行加載)
APK 打包流程
- 1.aapt 打包資源文件生成 R.java 文件疫鹊;aidl 生成 java 文件
- 2.將 java 文件編譯為 class 文件
- 3.將工程及第三方的 class 文件轉(zhuǎn)換成 dex 文件
- 4.將 dex 文件、so司致、編譯過的資源拆吆、原始資源等打包成 apk 文件
- 5.簽名
- 6.資源文件對(duì)齊,減少運(yùn)行時(shí)內(nèi)存
App 安裝過程
- 首先要解壓 APK脂矫,資源枣耀、so等放到應(yīng)用目錄
- Dalvik 會(huì)將 dex 處理成 ODEX ;ART 會(huì)將 dex 處理成 OAT羹唠;
- OAT 包含 dex 和安裝時(shí)編譯的機(jī)器碼
Android 優(yōu)化:網(wǎng)絡(luò)優(yōu)化奕枢、卡頓優(yōu)化、內(nèi)存優(yōu)化佩微、瘦包缝彬、內(nèi)存泄漏、ANR哺眯、Native Crash
網(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ù)雜度荔茬,避免過度繪制
- 使用 include、merge竹海、viewstub
- 優(yōu)化繪制過程慕蔚,避免在 Draw 中頻繁創(chuàng)建對(duì)象、做耗時(shí)操作
內(nèi)存優(yōu)化
- 內(nèi)存問題
- 內(nèi)存泄漏
- 內(nèi)存抖動(dòng):頻繁創(chuàng)建臨時(shí)對(duì)象
- Bitmap 大內(nèi)存:規(guī)避位圖超標(biāo)
- 代碼質(zhì)量:intdef 代替枚舉斋配,使用 SparseArray 代替 HashMap
- 檢測(cè)工具
- MAT(Memory Analysis Tools) 孔飒,可分析 Java 堆數(shù)據(jù),可查看實(shí)例占用空間艰争、引用關(guān)系等
- Android Studio 自帶的 Profiler
- LeakCanary:通過弱引用和引用隊(duì)列監(jiān)控對(duì)象是否被回收坏瞄,比如 Activity 銷毀時(shí)開始監(jiān)控此對(duì)象,檢測(cè)到未被回收則主動(dòng) gc 园细,然后繼續(xù)監(jiān)控
瘦包
- 1.資源方面:資源在線化惦积、圖片使用 webp 格式、tint 著色生成不同色調(diào)的切猛频、使用 icon font
- 2.so 庫(kù):保留一個(gè) cpu 架構(gòu)的 so 文件
- 3.AS Inspect Code 清除無用代碼和資源
- 4.代碼混淆:使用 ProGuard 可以移除無用的類狮崩、字段、方法(壓縮)鹿寻,移除無用字節(jié)碼指令
- 5.不保留行號(hào):使用 ProGuard 配置不保留行號(hào)
- 6.開啟 shrinkResources:移除無用資源
- 7.資源混淆:使用 AndResGuard 縮短資源長(zhǎng)度睦柴,對(duì)資源進(jìn)行 7z 壓縮等(直接對(duì)apk操作)
- 8.代碼結(jié)構(gòu)簡(jiǎn)化,比如用 intdef 代替 枚舉(一個(gè)枚舉有1~1.4kb大小)
- 9.使用 compileOnly 在只需編譯時(shí)依賴的場(chǎng)景毡熏,不會(huì)打到 apk 里
- 10.使用 thinR 插件剔除 R 文件坦敌,將引用 R 字段的地方替換成對(duì)應(yīng)常量
- 11.Android 7.0 使用 V2(apksigner) 代替 V1(jarsigner) 簽名工具
- 12.動(dòng)態(tài)加載 so 庫(kù)(System.load加載絕對(duì)路徑文件)、插件化技術(shù)痢法、App Bundle
- 13.使用 facebook 的 redex
內(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
ANR 問題及分析
- anr 分類
- 主線程 5s 內(nèi)沒有處理完輸入事件
- service 阻塞 20s
- 前臺(tái)廣播阻塞 10s 或后臺(tái)廣告阻塞 20s
- ContentProvider publish 在 20s 內(nèi)沒有處理完
- anr 發(fā)生過程
- 1.捕獲到 anr蘸炸,發(fā)送 linux 信號(hào)量 3
- 2.進(jìn)程接受到信號(hào)量將 anr 信息寫入 data/anr/traces.txt 文件
- 3.Log 打印 anr 信息
- 4.進(jìn)程進(jìn)入 anr 狀態(tài),彈出 anr 提示框
- 監(jiān)控 anr
- 1.Android 5.0 以下監(jiān)聽 traces.txt 文件寫入
- 2.每隔 5s 向主線程發(fā)送消息判斷主線程是否阻塞
- 分析 anr
- 查看 cpu 負(fù)載是否是 cpu 資源緊張導(dǎo)致
- 查看堆椉獗迹看是否是我們的代碼耗時(shí)過長(zhǎng)
- 避免 anr
- 主線程中不要做耗時(shí)操作搭儒,注意使用 IntentService
- 降低子線程優(yōu)先級(jí)穷当,讓主線程可以更多的獲取到 cpu 資源
Native Crash
- 崩潰過程:native crash 時(shí)操作系統(tǒng)會(huì)向進(jìn)程發(fā)送信號(hào),崩潰信息會(huì)寫入到 data/tombstones 下淹禾,并在 logcat 輸出崩潰日志
- 定位:so 庫(kù)剝離調(diào)試信息的話馁菜,只有相對(duì)位置沒有具體行號(hào),可以使用 NDK 提供的 addr2line 或 ndk-stack 來定位
- addr2line:根據(jù)有調(diào)試信息的 so 和相對(duì)位置定位實(shí)際的代碼處
- ndk-stack:可以分析 tombstone 文件铃岔,得到實(shí)際的代碼調(diào)用棧
其他:解析 XML汪疮、進(jìn)程保活德撬、播放器铲咨、Lint、CI蜓洪、CD、AOP坯苹、JetPack
Android 解析 XML
- SAX:流式解析
- DOM:先把 XML 全部讀取到內(nèi)存隆檀,再訪問樹形結(jié)構(gòu),很消耗內(nèi)存
- PULL:流式解析粹湃,Android 內(nèi)置的默認(rèn)解析方式
熱修復(fù)恐仑、插件化、組件化
- 熱修復(fù)原理:
- Native Hook(AndFix):直接在 native 層進(jìn)行方法的結(jié)構(gòu)體信息對(duì)換
- 分包(QFix):插入新 dex 到 dexElements[]为鳄,利用 ClassLoader 通過遍歷 dexElements[] 來 findClass 的特性
- Java Hook(Robust):hook 每個(gè)方法裳仆,在每個(gè)方法里埋好準(zhǔn)備替換的邏輯
- 插件化:DexClassLoader 動(dòng)態(tài)加載,四大組件未注冊(cè)問題通過 hook AMS孤钦、Instrumentation 等解決歧斟,VirtualAPK 源碼分析
- 組件化:ARoute 路由實(shí)現(xiàn):通過 APT 解析 @Route 等注解,結(jié)合 JavaPoet 生成路由表偏形,即路由與 Activity 的映射關(guān)系
進(jìn)程本残洌活
- 進(jìn)程優(yōu)先級(jí):1.前臺(tái)進(jìn)程 ;2.可見進(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):?jiǎn)?dòng)一個(gè)前臺(tái)服務(wù)(API>18會(huì)有正在運(yùn)行通知欄)
- 3.廣播拉活
- 4.Service 拉活
- 5.JobScheduler 定時(shí)任務(wù)拉活
- 6.雙進(jìn)程拉活
播放器原理
- 視頻播放原理:(mp4庸蔼、flv)-> 解封裝 -> (mp3/aac解总、h264/h265)-> 解碼 -> (pcm、yuv)-> 音視頻同步 -> 渲染播放
- 音視頻同步:
- 選擇參考時(shí)鐘源:音頻時(shí)間戳朱嘴、視頻時(shí)間戳和外部時(shí)間三者選擇一個(gè)作為參考時(shí)鐘源(一般選擇音頻倾鲫,因?yàn)槿藢?duì)音頻更敏感粗合,ijk 默認(rèn)也是音頻)
- 通過等待或丟幀將視頻流與參考時(shí)鐘源對(duì)齊,實(shí)現(xiàn)同步
- IjkPlayer 原理
- 集成了 MediaPlayer乌昔、ExoPlayer 和 IjkPlayer 三種實(shí)現(xiàn)隙疚,其中 IjkPlayer 基于 FFmpeg 的 ffplay
- 音頻輸出方式:AudioTrack、OpenSL ES磕道;視頻輸出方式:NativeWindow供屉、OpenGL ES
Lint
- Android Lint 是 Google 提供給 Android 開發(fā)者的靜態(tài)代碼檢查工具
- 使用 Lint 對(duì) Android 工程代碼進(jìn)行掃描和檢查,可以發(fā)現(xiàn)代碼潛在的問題溺蕉,提醒程序員及早修正
- 基于 Detector伶丐、IssueRegistry 實(shí)現(xiàn),通過 lintChecks project 引入
CI
- Continuous integration(持續(xù)集成疯特,簡(jiǎn)稱CI):頻繁的將代碼集成到主干哗魂,防止分支大幅偏離主干,方便快速發(fā)現(xiàn)錯(cuò)誤
- Continuous delivery(持續(xù)交付):頻繁地將軟件的新版本漓雅,交付給質(zhì)量團(tuán)隊(duì)或者用戶录别,以供評(píng)審
- Continuous deployment(持續(xù)部署):持續(xù)交付的下一步,指的是代碼通過評(píng)審以后邻吞,自動(dòng)部署到生產(chǎn)環(huán)境
- 交付后需要進(jìn)行構(gòu)建组题,將源碼轉(zhuǎn)換為可以運(yùn)行的實(shí)際代碼,常用的構(gòu)建工具有 Jenkins抱冷、Strider
AOP
- 基于 Gradle Transform API 創(chuàng)建 TransForm 崔列,其執(zhí)行時(shí)機(jī)在 class 被打包成 dex 之前
- 在 TransForm 中通過 javassist 或 asm 修改字節(jié)碼
- 基于 Gradle Plugin API 自定義插件,應(yīng)用自定義的 TransForm
JetPack
- LiveData 感知聲明周期原理:像 Glide 一樣給界面添加了無視圖的 Fragment
- ViewModel 界面旋轉(zhuǎn)短暫銷毀重建時(shí)保存數(shù)據(jù)原理:
- ViewModel 保存在 ViewModelStore 中
- 當(dāng) Activity 配置變更銷毀時(shí)旺遮,系統(tǒng)會(huì)調(diào)用 onRetainNonConfigurationInstance 保存 NonConfigurationInstances赵讯,而 ViewModel 就保存在 NonConfigurationInstances 中
- 重建時(shí) onCreate 方法通過 getLastNonConfigurationInstance 方法獲取到 NonConfigurationInstances,從而獲取到 ViewModelStore
- JetPack 與 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