2023-Android-常見面試題

[TOC]

HASH算法

  • 常用的摘要算法包括MD5鉴吹,SHA1,SHA256

  • 消息摘要算法的特點(diǎn):

    1. 無論輸入的消息有多長惩琉,計算出來的消息摘要的長度總是固定的豆励。

    2. 消息摘要看起來是“隨機(jī)的”。這些比特看上去是胡亂的雜湊在一起的瞒渠。

    3. 一般地良蒸,只要輸入的消息不同,對其進(jìn)行摘要以后產(chǎn)生的摘要消息也必不相同在孝;但相同的輸入必會產(chǎn)生相同的輸出诚啃。

    4. 消息摘要函數(shù)是無陷門的單向函數(shù),即只能進(jìn)行正向的信息摘要私沮,而無法從摘要中恢復(fù)出任何的消息始赎,甚至根本就找不到任何與原信息相關(guān)的信息。

    5. 好的摘要算法仔燕,無法找到兩條消息造垛,是它們的摘要相同。

    6. 主要特征是加密過程不需要密鑰晰搀,并且經(jīng)過加密的數(shù)據(jù)無法被解密五辽,只有輸入相同的明文數(shù)據(jù)經(jīng)過相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在 密鑰的管理與分發(fā)問題外恕,適合于分布式網(wǎng)絡(luò)相同上使用杆逗。由于其加密計算的工作量相當(dāng)可觀乡翅,所以以前的這種算法通常只用于數(shù)據(jù)量有限的情況下的加密,例如計算機(jī)的口令就是 用不可逆加密算法加密的罪郊。

https握手過程

  • 非對稱加密算法用于在握手過程中加密生成的密碼

  • 對稱加密算法用于對真正傳輸?shù)臄?shù)據(jù)進(jìn)行加密

  • 而HASH算法用于驗(yàn)證數(shù)據(jù)的完整性蠕蚜。

  • RSA公私鑰密鑰加密算法,DES悔橄,AES對稱加密算法靶累,SHA1摘要算法

  • Client端和Server端:

    • Client發(fā)出https的請求->Server

    • Server回應(yīng)公鑰SPublic->Client

    • Client生成隨機(jī)數(shù)作為以后消息的加密密鑰key

    • Client用SPublic加密key發(fā)出https請求->Server

    • Server用私鑰SPrivate解密key

    • [圖片上傳失敗...(image-dd4729-1682881853345)]

第一次HTTP請求

  1. 客戶端向服務(wù)器發(fā)起HTTPS請求,連接到服務(wù)器的443端口癣疟。

  2. 服務(wù)器端有一個密鑰對挣柬,即公鑰和私鑰,是用來進(jìn)行非對稱加密使用的睛挚,服務(wù)器端保存著私鑰邪蛔,不能將其泄露,公鑰可以發(fā)送給任何人扎狱。

  3. 服務(wù)器將自己的公鑰發(fā)送給客戶端店溢。

  4. 客戶端收到服務(wù)器端的公鑰之后,會對公鑰進(jìn)行檢查委乌,驗(yàn)證其合法性床牧,如果公鑰合格,那么客戶端會生成一個隨機(jī)值遭贸,這個隨機(jī)值就是用于進(jìn)行對稱加密的密鑰戈咳,我們將該密鑰稱之為client key,即客戶端密鑰壕吹,這樣在概念上和服務(wù)器端的密鑰容易進(jìn)行區(qū)分著蛙。然后用服務(wù)器的公鑰對客戶端密鑰進(jìn)行非對稱加密,這樣客戶端密鑰就變成密文了耳贬,至此踏堡,HTTPS中的第一次HTTP請求結(jié)束。

第二次HTTP請求

  1. 客戶端會發(fā)起HTTPS中的第二個HTTP請求咒劲,將加密之后的客戶端密鑰發(fā)送給服務(wù)器顷蟆。

  2. 服務(wù)器接收到客戶端發(fā)來的密文之后,會用自己的私鑰對其進(jìn)行非對稱解密腐魂,解密之后的明文就是客戶端密鑰帐偎,然后用客戶端密鑰對數(shù)據(jù)進(jìn)行對稱加密,這樣數(shù)據(jù)就變成了密文蛔屹。

  3. 然后服務(wù)器將加密后的密文發(fā)送給客戶端削樊。

  4. 客戶端收到服務(wù)器發(fā)送來的密文,用客戶端密鑰對其進(jìn)行對稱解密,得到服務(wù)器發(fā)送的數(shù)據(jù)漫贞。這樣HTTPS中的第二個HTTP請求結(jié)束甸箱,整個HTTPS傳輸完成。

hashmap原理

  • HashMap是基于哈希表實(shí)現(xiàn)的迅脐,用Entry[]來存儲數(shù)據(jù)摇肌,而Entry中封裝了key、value仪际、hash以及Entry類型的next

  • HashMap存儲數(shù)據(jù)是無序的

  • hash沖突是通過拉鏈法解決的,也就是數(shù)組+鏈表

  • HashMap的容量永遠(yuǎn)為2的冪次方昵骤,有利于哈希表的散列

  • HashMap不支持存儲多個相同的key树碱,且只保存一個key為null的值,多個會覆蓋

  • put過程变秦,是先通過key算出hash成榜,然后用hash算出應(yīng)該存儲在table中的index,然后遍歷table[index]蹦玫,看是否有相同的key存在赎婚,存在,則更新value樱溉;不存在則插入到table[index]單向鏈表的表頭挣输,時間復(fù)雜度為O(n)

  • get過程,通過key算出hash福贞,然后用hash算出應(yīng)該存儲在table中的index撩嚼,然后遍歷table[index],然后比對key挖帘,找到相同的key完丽,則取出其value,時間復(fù)雜度為O(n)

  • HashMap是線程不安全的拇舀,如果有線程安全需求逻族,推薦使用ConcurrentHashMap。

socket

  • 粘包問題:包頭有包長度

  • 心跳包:5s一次登錄檢查骄崩,10s一次心跳

  • TCP的keepalive選項(xiàng)

Netty

  • 方便處理粘包問題聘鳞,自帶由定長拆包器,分隔符拆包器等

  • 零拷貝要拂,java的FileChannel.transferTo方法搁痛,通過bytebuffer直接進(jìn)行socket的讀寫谍肤,利用ByteBuf機(jī)制進(jìn)行多bytebuffer數(shù)組的合成(實(shí)際上ByteBuf只是記錄了bytebuffer的引用)

線程池

一個線程只能有一個Looper叫榕,但可以有多個Handler

  1. newCachedThreadPool

    • 建一個可緩存線程池么伯,如果線程池長度超過處理需要宾毒,可靈活回收空閑線程迂猴,若無可回收,則新建線程名扛。

    • ****線程池為無限大****

  2. newFixedThreadPool

    • 可控制線程最大并發(fā)數(shù)晰赞,超出的線程會在隊(duì)列中等待。
  3. newScheduledThreadPool

    • 支持定時纽甘,延遲及周期性任務(wù)執(zhí)行
  4. newSingleThreadExecutor

    • 它只會用唯一的工作線程來執(zhí)行任務(wù)良蛮,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行

app保活

焙酚活:監(jiān)聽SCREEN_ON/OFF廣播决瞳,啟動1px的透明Activity; 啟動空通知,提高fg-service; 申請權(quán)限左权,加入白名單

Activity Bundle

  • Bundle可對對象進(jìn)行操作皮胡,而Intent是不可以。Bundle相對于Intent擁有更多的接口赏迟,用起來比較靈活屡贺,但是使用Bundle也還是需要借助Intent才可以完成數(shù)據(jù)傳遞總之,Bundle旨在存儲數(shù)據(jù)锌杀,而Intent旨在傳值甩栈。

  • 為什么Bundle不直接使用Hashmap代替呢?

      • Bundle內(nèi)部是由ArrayMap實(shí)現(xiàn)的糕再,ArrayMap的內(nèi)部實(shí)現(xiàn)是兩個數(shù)組量没,一個int數(shù)組是存儲對象數(shù)據(jù)對應(yīng)下標(biāo),一個對象數(shù)組保存key和value突想,內(nèi)部使用二分法對key進(jìn)行排序允蜈,所以在添加、刪除蒿柳、查找數(shù)據(jù)的時候饶套,都會使用二分法查找,只適合于小數(shù)據(jù)量操作垒探,如果在數(shù)據(jù)量比較大的情況下妓蛮,那么它的性能將退化。而HashMap內(nèi)部則是數(shù)組+鏈表結(jié)構(gòu)圾叼,所以在數(shù)據(jù)量較少的時候蛤克,HashMap的Entry Array比ArrayMap占用更多的內(nèi)存。因?yàn)槭褂肂undle的場景大多數(shù)為小數(shù)據(jù)量夷蚊,我沒見過在兩個Activity之間傳遞10個以上數(shù)據(jù)的場景构挤,所以相比之下,在這種情況下使用ArrayMap保存數(shù)據(jù)惕鼓,在操作速度和內(nèi)存占用上都具有優(yōu)勢筋现,因此使用Bundle來傳遞數(shù)據(jù),可以保證更快的速度和更少的內(nèi)存占用。
      • 另外一個原因矾飞,則是在Android中如果使用Intent來攜帶數(shù)據(jù)的話一膨,需要數(shù)據(jù)是基本類型或者是可序列化類型,HashMap使用Serializable進(jìn)行序列化洒沦,而Bundle則是使用Parcelable進(jìn)行序列化豹绪。而在Android平臺中,更推薦使用Parcelable實(shí)現(xiàn)序列化申眼,雖然寫法復(fù)雜瞒津,但是開銷更小,所以為了更加快速的進(jìn)行數(shù)據(jù)的序列化和反序列化括尸,系統(tǒng)封裝了Bundle類巷蚪,方便我們進(jìn)行數(shù)據(jù)的傳輸。
  • Activity/Fragment狀態(tài)數(shù)據(jù)的保存與恢復(fù)

  • 消息機(jī)制中的Message的setData

LeakCanary v2

  1. 先注冊監(jiān)聽觀察Activity/Fragment

  2. 在Activity destroy后將Activity的弱引用關(guān)聯(lián)到ReferenceQueue中姻氨,這樣Activity將要被GC前,會出現(xiàn)在ReferenceQueue中剪验。

  3. 隨后肴焊,會向主線程的MessageQueue添加一個IdleHandler,用于在idle時觸發(fā)一個發(fā)生在HandlerThread的等待5秒后開始檢測內(nèi)存泄漏的代碼功戚。 這段代碼首先會判斷是否對象出現(xiàn)在引用隊(duì)列中娶眷,如果有,則說明沒有內(nèi)存泄漏啸臀,結(jié)束届宠。否則,調(diào)用Runtime.getRuntime().gc()進(jìn)行GC乘粒,等待100ms后再次判斷是否已經(jīng)出現(xiàn)在引用隊(duì)列中豌注,若還沒有被出現(xiàn),那么說明有內(nèi)存泄漏灯萍,開始dump hprof轧铁。

Service

在后臺執(zhí)行長時間運(yùn)行操作而沒有用戶界面的應(yīng)用組件

  • 啟動狀態(tài)

    • startService()后臺無限期運(yùn)行,即使啟動服務(wù)的組件已被銷毀也不受影響旦棉,除非手動調(diào)用才能停止服務(wù)齿风, 已啟動的服務(wù)通常是執(zhí)行單一操作,而且不會將結(jié)果返回給調(diào)用方绑洛。
  • 綁定狀態(tài)

    • bindService()綁定服務(wù)提供了一個客戶端-服務(wù)器接口救斑,允許組件與服務(wù)進(jìn)行交互、發(fā)送請求真屯、獲取結(jié)果脸候,甚至是利用進(jìn)程間通信 (IPC) 跨進(jìn)程執(zhí)行這些操作。 僅當(dāng)與另一個應(yīng)用組件綁定時,綁定服務(wù)才會運(yùn)行纪他。 多個組件可以同時綁定到該服務(wù)鄙煤,但全部取消綁定后,該服務(wù)即會被銷毀

    • 先綁定服務(wù)后啟動服務(wù) 則宿主(Activity)被銷毀了茶袒,也不會影響服務(wù)的運(yùn)行

    • 先啟動服務(wù)后綁定服務(wù) 則解除綁定后梯刚,服務(wù)依然按啟動服務(wù)的生命周期在后臺運(yùn)行,直到有Context調(diào)用了stopService()或是服務(wù)本身調(diào)用了stopSelf()方法抑或內(nèi)存不足時才會銷毀服務(wù)

  • onBind() 當(dāng)另一個組件想通過調(diào)用 bindService() 與服務(wù)綁定(例如執(zhí)行 RPC)時薪寓,系統(tǒng)將調(diào)用此方法亡资。在此方法的實(shí)現(xiàn)中,必須返回 一個IBinder 接口的實(shí)現(xiàn)類向叉,供客戶端用來與服務(wù)進(jìn)行通信锥腻。無論是啟動狀態(tài)還是綁定狀態(tài),此方法必須重寫母谎,但在啟動狀態(tài)的情況下直接返回 null瘦黑。

  • onCreate() 首次創(chuàng)建服務(wù)時,系統(tǒng)將調(diào)用此方法來執(zhí)行一次性設(shè)置程序(在調(diào)用 onStartCommand() 或onBind() 之前)奇唤。如果服務(wù)已在運(yùn)行幸斥,則不會調(diào)用此方法,該方法只調(diào)用一次

  • onStartCommand() 當(dāng)另一個組件(如 Activity)通過調(diào)用 startService() 請求啟動服務(wù)時咬扇,系統(tǒng)將調(diào)用此方法甲葬。一旦執(zhí)行此方法,服務(wù)即會啟動并可在后臺無限期運(yùn)行懈贺。 如果自己實(shí)現(xiàn)此方法经窖,則需要在服務(wù)工作完成后,通過調(diào)用 stopSelf() 或 stopService() 來停止服務(wù)梭灿。(在綁定狀態(tài)下画侣,無需實(shí)現(xiàn)此方法。)

    • intent :啟動時堡妒,啟動組件傳遞過來的Intent棉钧,如Activity可利用Intent封裝所需要的參數(shù)并傳遞給Service

    • flags:表示啟動請求時是否有額外數(shù)據(jù),可選值有 0涕蚤,START_FLAG_REDELIVERY宪卿,START_FLAG_RETRY,0代表沒有万栅,它們具體含義如下:

      START_FLAG_REDELIVERY 這個值代表了onStartCommand方法的返回值為 START_REDELIVER_INTENT佑钾,而且在上一次服務(wù)被殺死前會去調(diào)用stopSelf方法停止服務(wù)孽尽。其中START_REDELIVER_INTENT意味著當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后,則會重建服務(wù),并通過傳遞給服務(wù)的最后一個 Intent 調(diào)用 onStartCommand(),此時Intent時有值的。

      START_FLAG_RETRY 該flag代表當(dāng)onStartCommand調(diào)用后一直沒有返回值時晃危,會嘗試重新去調(diào)用onStartCommand()

    • startId : 指明當(dāng)前服務(wù)的唯一ID胧砰,與stopSelfResult (int startId)配合使用,stopSelfResult 可以更安全地根據(jù)ID停止服務(wù)苇瓣。

    • 實(shí)際上onStartCommand的返回值int類型才是最最值得注意的尉间,它有三種可選值, START_STICKY钓简,START_NOT_STICKY乌妒,START_REDELIVER_INTENT,它們具體含義如下:

      START_STICKY ??當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后外邓,一段時間后內(nèi)存再次空閑時撤蚊,系統(tǒng)將會嘗試重新創(chuàng)建此Service,一旦創(chuàng)建成功后將回調(diào)onStartCommand方法损话,但其中的Intent將是null侦啸,除非有掛起的Intent,如pendingintent丧枪,這個狀態(tài)下比較適用于不執(zhí)行命令光涂、但無限期運(yùn)行并等待作業(yè)的媒體播放器或類似服務(wù)。

      START_NOT_STICKY ??當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后拧烦,即使系統(tǒng)內(nèi)存再次空閑時忘闻,系統(tǒng)也不會嘗試重新創(chuàng)建此Service。除非程序中再次調(diào)用startService啟動此Service恋博,這是最安全的選項(xiàng)齐佳,可以避免在不必要時以及應(yīng)用能夠輕松重啟所有未完成的作業(yè)時運(yùn)行服務(wù)。

      START_REDELIVER_INTENT ??當(dāng)Service因內(nèi)存不足而被系統(tǒng)kill后债沮,則會重建服務(wù)炼吴,并通過傳遞給服務(wù)的最后一個 Intent 調(diào)用 onStartCommand(),任何掛起 Intent均依次傳遞疫衩。與START_STICKY不同的是硅蹦,其中的傳遞的Intent將是非空,是最后一次調(diào)用startService中的intent闷煤。這個值適用于主動執(zhí)行應(yīng)該立即恢復(fù)的作業(yè)(例如下載文件)的服務(wù)童芹。

  • onDestroy() 當(dāng)服務(wù)不再使用且將被銷毀時,系統(tǒng)將調(diào)用此方法鲤拿。服務(wù)應(yīng)該實(shí)現(xiàn)此方法來清理所有資源假褪,如線程、注冊的偵聽器皆愉、接收器等嗜价,這是服務(wù)接收的最后一個調(diào)用

  • [圖片上傳失敗...(image-48132c-1682881853345)]

前臺服務(wù)

通知欄Notification

后臺服務(wù)

后臺服務(wù)優(yōu)先級相對比較低艇抠,當(dāng)系統(tǒng)出現(xiàn)內(nèi)存不足的情況下,它就有可能會被回收掉

IntentService

IntentService是專門用來解決Service中不能執(zhí)行耗時操作這一問題的

系統(tǒng)服務(wù)

broadcastreceiver 監(jiān)視系統(tǒng)服務(wù)

廣播

BroadcastReceiver

  • 普通廣播(Normal Broadcast)

  • 系統(tǒng)廣播(System Broadcast)

  • 有序廣播(Ordered Broadcast)

    • 接收廣播按順序接收

    • 先接收的廣播接收者可以對廣播進(jìn)行截斷久锥,即后接收的廣播接收者不再接收到此廣播家淤;

    • 先接收的廣播接收者可以對廣播進(jìn)行修改,那么后接收的廣播接收者將接收到被修改后的廣播

  • 粘性廣播(Sticky Broadcast)

  • App應(yīng)用內(nèi)廣播(Local Broadcast)

  • 特別注意

    • 對于靜態(tài)注冊(全局+應(yīng)用內(nèi)廣播)瑟由,回調(diào)onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext絮重;

    • 對于全局廣播的動態(tài)注冊,回調(diào)onReceive(context, intent)中的context返回值是:Activity Context歹苦;

    • 對于應(yīng)用內(nèi)廣播的動態(tài)注冊(LocalBroadcastManager方式)青伤,回調(diào)onReceive(context, intent)中的context返回值是:Application Context。

    • 對于應(yīng)用內(nèi)廣播的動態(tài)注冊(非LocalBroadcastManager方式)殴瘦,回調(diào)onReceive(context, intent)中的context返回值是:Activity Context狠角;

  • 本地廣播和全局廣播的區(qū)別

    • 本地廣播

      • 廣播事件的發(fā)送和接收都在本應(yīng)用,不影響其他應(yīng)用也不受其他應(yīng)用影響蚪腋,只能被動態(tài)注冊丰歌,不能靜態(tài)注冊,主要用法都在LocalBroadcastManager類中
    • 全局廣播

      • 可以接收其他應(yīng)用發(fā)的廣播屉凯,也可以發(fā)送廣播讓其他應(yīng)用接收立帖,全局廣播既可以動態(tài)注冊,也可以靜態(tài)注冊悠砚,接受其他應(yīng)用和系統(tǒng)廣播是全局廣播的一個重要應(yīng)用點(diǎn)晓勇。總體來說兩者應(yīng)用場景不同

LiveData

  • 觀察者對象必須得處于主線程中

    • setValue()方法相信大家都很熟悉灌旧,我們必須在主線程中進(jìn)行調(diào)用绑咱,否則會拋出異常。在代碼中體現(xiàn)出來的节榜,正是通過assertMainThread("setValue");來保證方法處于主線程中

    • postValue用于在子線程通知主線程livedata發(fā)生了變化

  • 專用于 Android 的具備自主生命周期感知能力的可觀察的數(shù)據(jù)存儲器類

  • newFixThreadPool(2)

  • 通過對比ObserverWrapperLiveData之間的version羡玛,來判斷是否更新

Databing\ViewModel

  • 綁定的方式分兩種

    • 單向綁定 直接在xml中寫對應(yīng)的參數(shù)名 android:text="@{userInfo.name}"

    • 雙向綁定 android:text="@={userInfo.nickName}"

  • 原理

    1. 首先通過DataBindingUtil.setContentView()來查找對應(yīng)的布局

    2. 然后生成一個全新的tag值并且賦值給每個view

    3. 進(jìn)行臟標(biāo)記

    4. 注冊監(jiān)聽

jni

普通應(yīng)用

java->jni: .so/.dll

jni->java:

  1. jclass cls = (*env)->FindClass(env, "jni/test/Demo"); //把點(diǎn)號換成斜杠

  2. jclass cls = (*env)-> GetObjectClass(env, obj); //其中obj是要引用的對象别智,類型是jobject

  3. Call<TYPE>Method或者 CallStatic<TYPE>Method(訪問類的靜態(tài)方法)

  4. jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig); jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);

  5. (*env)->SetObjectField(env, jobj, fid, new_jstr);

直播

音頻

  1. AudioRecord

  2. 單雙聲道

  3. 采樣率

  4. 緩沖區(qū)

  5. 單幀大小計算

  6. p幀(與前一幀的差值宗苍,較小) i幀(關(guān)鍵幀薄榛,較大)讳窟,B幀(是前一幀及后一幀的差別,但解碼麻煩敞恋,需要先知道前一幀和后一幀)

  7. webrtc - (ns降噪模塊丽啡,vad語音端點(diǎn)識別模塊-可用于人聲識別,aecm回聲消除模塊)

    1. 幀間隔毫秒必須是80的倍數(shù)

    2. 幀大小必須是160的倍數(shù)

視頻

  1. 編碼

  2. 攝像頭(攝像頭數(shù)硬猫,前后补箍,采集高寬改执,閃光燈,對焦方式)

  3. 預(yù)覽幀數(shù)

  4. 縮放和對焦

  5. 軟/硬解碼

  6. h264/h265

    1. 壓縮比接近50%

    2. 解碼cpu負(fù)擔(dān)大

    3. 減少實(shí)時的時延坑雅、減少信道獲取時間和隨機(jī)接入時延辈挂、降低復(fù)雜度

  7. ijkplayer

  8. ffmepg

    • 音視頻解碼器
  9. yuv

    • 負(fù)責(zé)處理圖像顏色

推流

推薦:RTMP RTSP

兩者區(qū)別:

[圖片上傳失敗...(image-1cc5df-1682881853347)]

注: RTP傳輸是指實(shí)時傳輸協(xié)議,是建立在udp協(xié)議之上

udp與tcp區(qū)別

UDP TCP
是否連接 無連接 面向連接
是否可靠 不可靠傳輸裹粤,不使用流量控制和擁塞控制 可靠傳輸终蒂,使用流量控制和擁塞控制
連接對象個數(shù) 支持一對一,一對多遥诉,多對一和多對多交互通信 只能是一對一通信
傳輸方式 面向報文 面向字節(jié)流
首部開銷 首部開銷小拇泣,僅8字節(jié) 首部最小20字節(jié),最大60字節(jié)
適用場景 適用于實(shí)時應(yīng)用(IP電話矮锈、視頻會議霉翔、直播等) 適用于要求可靠傳輸?shù)膽?yīng)用,例如文件傳輸

IM

  • 千人群優(yōu)化

    • 消息合并插入

      • 將單一一條消息合并成一個消息塊進(jìn)行傳輸
    • 并發(fā)限制(?)

      • 當(dāng)進(jìn)入消息未讀較多的群時苞笨,分頁

內(nèi)存

  1. 內(nèi)存泄漏 Memory Leak: 本該回收的對象不能被回收而停留在堆內(nèi)存中早龟,從而產(chǎn)生了內(nèi)存泄漏。

  2. 內(nèi)存溢出 Out Of Memory: 內(nèi)存溢出是指APP向系統(tǒng)申請超過最大閥值的內(nèi)存請求

強(qiáng)引用猫缭、軟引用葱弟、弱引用和虛引用

強(qiáng)引用 垃圾回收器絕不會回收它
軟引用 內(nèi)存空間充足時,垃圾回收器不會回收它猜丹;如果內(nèi)存空間不足了芝加,就會回收這些對象的內(nèi)存
弱引用 不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存射窒,但不一定及時
虛引用 任何時候都可能被垃圾回收器回收

LruCache

LRU是近期最少使用的算法藏杖,它的核心思想是當(dāng)緩存滿時,會優(yōu)先淘汰那些近期最少使用的緩存對象

  • 原理是把最近使用的對象用強(qiáng)引用(即我們平常使用的對象引用方式)存儲在 LinkedHashMap 中脉顿。當(dāng)緩存滿時蝌麸,把最近最少使用的對象從內(nèi)存中移除,并提供了get和put方法來完成緩存的獲取和添加操作艾疟。

  • LinkedHashMap是以訪問順序排序的

Bitmap的三級緩存

  1. 通過BitmapFactory.Options中的inJustDecodeBounds = true 不對bitmap進(jìn)行實(shí)際的內(nèi)存分配来吩,但仍然可以獲得所有屬性

  2. 通過對比bitmap的實(shí)際大小和imageView的大小進(jìn)行二次采樣inSimpleSize

  3. 使用LruCache

Parcelable和Serializable的區(qū)別和比較

Parcelable和Serializable都是實(shí)現(xiàn)序列化并且都可以用于Intent間傳遞數(shù)據(jù),Serializable是Java的實(shí)現(xiàn)方式,可能會頻繁的IO操作,所以消耗比較大,但是實(shí)現(xiàn)方式簡單 Parcelable是Android提供的方式,效率比較高,但是實(shí)現(xiàn)起來復(fù)雜一些 , 二者的選取規(guī)則是:內(nèi)存序列化上選擇Parcelable, 存儲到設(shè)備或者網(wǎng)絡(luò)傳輸上選擇Serializable(當(dāng)然Parcelable也可以但是稍顯復(fù)雜)

啟動模式

standard 模式

這是默認(rèn)模式,每次激活A(yù)ctivity時都會創(chuàng)建Activity實(shí)例蔽莱,并放入任務(wù)棧中弟疆。使用場景:大多數(shù)Activity。

singleTop 模式

如果在任務(wù)的棧頂正好存在該Activity的實(shí)例盗冷,就重用該實(shí)例( 會調(diào)用實(shí)例的 onNewIntent() )怠苔,否則就會創(chuàng)建新的實(shí)例并放入棧頂,即使棧中已經(jīng)存在該Activity的實(shí)例仪糖,只要不在棧頂柑司,都會創(chuàng)建新的實(shí)例迫肖。使用場景如新聞類或者閱讀類App的內(nèi)容頁面。

singleTask 模式

如果在棧中已經(jīng)有該Activity的實(shí)例攒驰,就重用該實(shí)例(會調(diào)用實(shí)例的 onNewIntent() )咒程。重用時,會讓該實(shí)例回到棧頂讼育,因此在它上面的實(shí)例將會被移出棧帐姻。如果棧中不存在該實(shí)例,將會創(chuàng)建新的實(shí)例放入棧中奶段。使用場景如瀏覽器的主界面饥瓷。不管從多少個應(yīng)用啟動瀏覽器,只會啟動主界面一次痹籍,其余情況都會走onNewIntent呢铆,并且會清空主界面上面的其他頁面。

singleInstance 模式

在一個新棧中創(chuàng)建該Activity的實(shí)例蹲缠,并讓多個應(yīng)用共享該棧中的該Activity實(shí)例棺克。一旦該模式的Activity實(shí)例已經(jīng)存在于某個棧中,任何應(yīng)用再激活該Activity時都會重用該棧中的實(shí)例( 會調(diào)用實(shí)例的 onNewIntent() )线定。其效果相當(dāng)于多個應(yīng)用共享一個應(yīng)用娜谊,不管誰激活該 Activity 都會進(jìn)入同一個應(yīng)用中。使用場景如鬧鈴提醒斤讥,將鬧鈴提醒與鬧鈴設(shè)置分離纱皆。singleInstance不要用于中間頁面,如果用于中間頁面芭商,跳轉(zhuǎn)會有問題派草,比如:A -> B (singleInstance) -> C,完全退出后铛楣,在此啟動近迁,首先打開的是B。

View和ViewGroup的繪制

  • 對于 ViewGroup簸州,除了要完成自己的測量鉴竭,還要遍歷調(diào)用子元素的 measure() 方法,而 View 只需要通過 measure() 方法就能確定測量規(guī)格

  • layout() 方法的作用是 ViewGroup 用于確定子元素的位置勿侯,當(dāng) ViewGroup 的位置確定后拓瞪,會在 onLayout() 方法中遍歷所有子 View 并調(diào)用子 View 的 layout() 方法缴罗。

  • View 和 ViewGroup 沒有實(shí)現(xiàn) onDraw() 方法助琐,接下來就是 dispatchDraw() 方法,View 沒有實(shí)現(xiàn)這個方法

View ViewGroup的事件分發(fā)機(jī)制

點(diǎn)擊事件被攔截面氓,但是相傳到下面的view

getParent().requestDisabllowInterceptTouchEvent(true)

  1. 表示事件是否會繼續(xù)分發(fā)出去兵钮,默認(rèn)返回false蛆橡,返回true時表示事件不會再繼續(xù)分發(fā),甚至都不會分發(fā)到自身的onTouchEvent方法掘譬;

  2. dispatchTouchEvent 分發(fā)事件泰演,當(dāng)該方法返回true時,該View不會繼續(xù)分發(fā)事件葱轩,包括該事件不會繼續(xù)分發(fā)到該View的onInterceptTouchEvent方法和onTouchEvent方法睦焕;

  3. onInterceptTouchEvent 攔截事件的傳遞,是否會繼續(xù)向子View靴拱、子ViewGroup傳遞垃喊,當(dāng)該方法返回true時,事件不會繼續(xù)向子View袜炕、子ViewGroup傳遞本谜,相當(dāng)于父級View把事件在此處截斷了;

  4. onTouchEvent 消費(fèi)事件偎窘,對點(diǎn)擊事件做相應(yīng)的點(diǎn)擊響應(yīng)處理乌助,具體執(zhí)行點(diǎn)擊后的操作,如果子View不做處理陌知,即返回false他托,該事件還會繼續(xù)傳遞到父View的onTouchEvent方法去處理,直到傳遞到組外層仆葡; 如果該方法返回true上祈,表示這個事件被消費(fèi)掉了,這個事件就此終止了浙芙,不會再有任何傳遞登刺;

  5. Android事件分發(fā)是先傳遞到ViewGroup,再由ViewGroup傳遞到View的嗡呼。

  6. 在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進(jìn)行攔截纸俭,onInterceptTouchEvent方法返回true代表不允許事件繼續(xù)向子View傳遞,返回false代表不對事件進(jìn)行攔截南窗,默認(rèn)返回false揍很。

  7. 子View中如果將傳遞的事件消費(fèi)掉,ViewGroup中將無法接收到任何事件

  8. [圖片上傳失敗...(image-e2672b-1682881853345)]

android與JS互動

  • android調(diào)用JS: WebView的loadUrl()万伤; WebView的evaluateJavascript()

    • 被調(diào)用的Js方法是有返回值的窒悔,如果是采用loadUrl()調(diào)用,返回值也會用loadUrl()載入敌买,直接顯示在WebView上简珠,這顯然是不對的,我們只想隱形的接收返回值,而evaluateJavascript()就提供了這樣的隱形接收方式聋庵,不會調(diào)用到loadUrl()膘融。
  • JS調(diào)用android: 通過WebView的addJavascriptInterface()進(jìn)行對象映射; WebViewClient 的shouldOverrideUrlLoading ()方法回調(diào)攔截 url; WebChromeClient 的onJsAlert()、onJsConfirm()祭玉、onJsPrompt()方法回調(diào)攔截JS對話框alert()氧映、confirm()、prompt() 消息

kotlin

  1. reified 使得泛型的方法假裝在運(yùn)行時能夠獲取泛型的類信息

  2. 原來在Java中脱货,類處于頂層岛都,類包含屬性和方法,在Kotlin中振峻,函數(shù)站在了類的位置疗绣,我們可以直接把函數(shù)放在代碼文件的頂層,讓它不從屬于任何類,可以通過import 包名.函數(shù)名來

  3. 導(dǎo)入我們將要使用的函數(shù)

    協(xié)程

    關(guān)鍵字 yield resume suspend

    總結(jié)下铺韧,協(xié)程是跑在線程上的多矮,一個線程可以同時跑多個協(xié)程,每一個協(xié)程則代表一個耗時任務(wù)哈打,我們手動控制多個協(xié)程之間的運(yùn)行塔逃、切換,決定誰什么時候掛起料仗,什么時候運(yùn)行湾盗,什么時候喚醒,而不是 Thread 那樣交給系統(tǒng)內(nèi)核來操作去競爭 CPU 時間片

    1. 協(xié)程其實(shí)就相當(dāng)于回調(diào)立轧,利用掛起函數(shù)來等待回調(diào)結(jié)果格粪,不會阻塞線程

    2. 協(xié)程可以用來直接標(biāo)記方法,由程序員自己實(shí)現(xiàn)切換氛改,調(diào)度帐萎,不再采用傳統(tǒng)的時間段競爭機(jī)制

    3. launch - 創(chuàng)建協(xié)程 3個參數(shù)和返回值 Job:

      <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="kotlin" cid="n545" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: "Fira Code", Consolas, "Lucida Console", Courier, monospace, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(40, 44, 52); position: relative !important; padding: 6px 10px; box-shadow: rgba(0, 0, 0, 0.16) 0px 2px 5px 0px, rgba(0, 0, 0, 0.12) 0px 2px 10px 0px; margin-bottom: 2.5rem; border: none; border-radius: 6px; width: inherit;">launch(CoroutineContext,CoroutineStart,block): Job</pre>

      1. CoroutineContext - 可以理解為協(xié)程的上下文

        1. Dispatchers.Default

        2. Dispatchers.IO -

        3. Dispatchers.Main - 主線程

        4. Dispatchers.Unconfined - 沒指定,就是在當(dāng)前線程

      2. CoroutineStart - 啟動模式胜卤,默認(rèn)是DEAFAULT疆导,也就是創(chuàng)建就啟動

        1. DEAFAULT - 模式模式,不寫就是默認(rèn)

        2. ATOMIC -

        3. UNDISPATCHED

        4. LAZY - 懶加載模式葛躏,你需要它的時候澈段,再調(diào)用啟動

      3. block - 閉包方法體饺蔑,定義協(xié)程內(nèi)需要執(zhí)行的操作

      4. Job - 協(xié)程構(gòu)建函數(shù)的返回值肥照,可以把 Job 看成協(xié)程對象本身

        1. job.start() - 啟動協(xié)程,除了 lazy 模式拢锹,協(xié)程都不需要手動啟動

        2. job.join() - 等待協(xié)程執(zhí)行完畢

        3. job.cancel() - 取消一個協(xié)程

        4. job.cancelAndJoin() - 等待協(xié)程執(zhí)行完畢然后再取消

    4. async - 創(chuàng)建帶返回值的協(xié)程摩窃,返回的是 Deferred 類

      • async 同 launch 唯一的區(qū)別就是 async 是有返回值的

      • Deferred 繼承自 Job 接口兽叮,Job有的它都有,增加了一個方法 await ,這個方法接收的是 async 閉包中返回的值充择,async 的特點(diǎn)是不會阻塞當(dāng)前線程德玫,但會阻塞所在協(xié)程匪蟀,也就是掛起

        但是注意啊椎麦,async 并不會阻塞線程,只是阻塞鎖調(diào)用的協(xié)程

    5. withContext - 不創(chuàng)建新的協(xié)程材彪,在指定協(xié)程上運(yùn)行代碼塊

    6. runBlocking - 不是 GlobalScope 的 API观挎,可以獨(dú)立使用,區(qū)別是 runBlocking 里面的 delay 會阻塞線程段化,而 launch 創(chuàng)建的不會

      • 在 runBlocking 閉包里面啟動另外的協(xié)程嘁捷,協(xié)程里面是可以嵌套啟動別的協(xié)程的
    7. 協(xié)程執(zhí)行時, 協(xié)程和協(xié)程显熏,協(xié)程和線程內(nèi)代碼是順序運(yùn)行的

    8. 協(xié)程掛起時雄嚣,就不會執(zhí)行了,而是等待掛起完成且線程空閑時才能繼續(xù)執(zhí)行

    9. relay 和 yield 方法是協(xié)程內(nèi)部的操作喘蟆,可以掛起協(xié)程缓升,區(qū)別是 relay 是掛起協(xié)程并經(jīng)過執(zhí)行時間恢復(fù)協(xié)程,當(dāng)線程空閑時就會運(yùn)行協(xié)程蕴轨;yield 是掛起協(xié)程港谊,讓協(xié)程放棄本次 cpu 執(zhí)行機(jī)會讓給別的協(xié)程,當(dāng)線程空閑時再次運(yùn)行協(xié)程橙弱。

hook

glide

原理

[圖片上傳失敗...(image-b7389a-1682881853347)

  1. Glide.with(context)創(chuàng)建RequestManager

    • RequestManager負(fù)責(zé)管理當(dāng)前context的所有Request

    • Context可以傳Fragment歧寺、Activity或者其他Context,當(dāng)傳Fragment棘脐、Activity時斜筐,當(dāng)前頁面對應(yīng)的Activity的生命周期可以被RequestManager監(jiān)控到,從而可以控制Request的pause蛀缝、resume奴艾、clear。這其中采用的監(jiān)控方法就是在當(dāng)前activity中添加一個沒有view的fragment内斯,這樣在activity發(fā)生onStart onStop onDestroy的時候蕴潦,會觸發(fā)此fragment的onStart onStop onDestroy。

    • RequestManager用來跟蹤眾多當(dāng)前頁面的Request的是RequestTracker類俘闯,用弱引用來保存運(yùn)行中的Request潭苞,用強(qiáng)引用來保存暫停需要恢復(fù)的Request。

  2. Glide.with(context).load(url)創(chuàng)建需要的Request

    • 通常是DrawableTypeRequest真朗,后面可以添加transform此疹、fitCenter、animate、placeholder蝗碎、error湖笨、override、skipMemoryCache蹦骑、signature等等

    • 如果需要進(jìn)行Resource的轉(zhuǎn)化比如轉(zhuǎn)化為Byte數(shù)組等需要慈省,可以加asBitmap來更改為BitmapTypeRequest

    • Request是Glide加載圖片的執(zhí)行單位

  3. Glide.with(context).load(url).into(imageview)

    • 在Request的into方法中會調(diào)用Request的begin方法開始執(zhí)行

    • 在正式生成EngineJob放入Engine中執(zhí)行之前,如果并沒有事先調(diào)用override(width, height)來指定所需要寬高眠菇,Glide則會嘗試去獲取imageview的寬和高边败,如果當(dāng)前imageview并沒有初始化完畢取不到高寬,Glide會通過view的ViewTreeObserver來等View初始化完畢之后再獲取寬高再進(jìn)行下一步

優(yōu)點(diǎn)

  1. 指定圖片大小

    • 自動判斷imageView大小捎废,然后只加載等大的像素笑窜,而不會全部加載進(jìn)imageView
  2. 方便圖片格式的切換

  3. 方便自定義圖片的裁剪等轉(zhuǎn)換(BitmapTransformation

Retrefit

本質(zhì)上 Retrofit 是一個對 OkHttp 進(jìn)行進(jìn)一步封裝的框架

原理

  • 通過 addConverterFactoryaddCallAdapterFactory 進(jìn)行數(shù)據(jù)格式的二次/自定義封裝

  • 利用MainThreadExecutor來進(jìn)行線程之間的切換

  • 核心思想是將 http 請求過程抽象成了一個對象 ServiceMethod, 這個對象的構(gòu)造的時候登疗,會通過 java 反射的方式傳入一個 method 對象排截,而這個對象就是我們在 interface 中定義的請求方法

RxJava

常用操作符

  • map 轉(zhuǎn)換事件,返回普通事件

  • flatMap 轉(zhuǎn)換事件辐益,返回` Observable

  • conactMap concatMap 與 FlatMap 的唯一區(qū)別就是 concatMap 保證了順序

  • subscribeOn 規(guī)定被觀察者所在的線程

  • observeOn 規(guī)定下面要執(zhí)行的消費(fèi)者所在的線程

  • take 接受一個 long 型參數(shù) count 断傲,代表至多接收 count 個數(shù)據(jù)

  • debounce 去除發(fā)送頻率過快的項(xiàng),常用在重復(fù)點(diǎn)擊解決上荷腊,配合 RxBinging 使用效果很好

  • timer 定時任務(wù)艳悔,多少時間以后發(fā)送事件

  • interval 每隔一定時間執(zhí)行一些任務(wù)

  • skip 跳過前多少個事件

  • distinct 去重

  • takeUntil 直到到一定條件的是停下,也可以接受另外一個被觀察者女仰,當(dāng)這個被觀察者結(jié)束之后則停止第一個被觀察者

  • Zip 專用于合并事件猜年,該合并不是連接(連接操作符后面會說),而是兩兩配對疾忍,也就意味著乔外,最終配對出的 Observable 發(fā)射事件數(shù)目只和少的那個相同。不影響Observable的發(fā)射一罩,Observable 被觀察者會一直發(fā)射杨幼,不會停,只是Observer 接收不到

  • merge 多個 Observable 發(fā)射的數(shù)據(jù)隨機(jī)發(fā)射聂渊,不保證先后順序

  • Concat 多個 Observable 組合以后按照順序發(fā)射差购,保證了先后順序,不過最多能組合4個 Observable 汉嗽,多的可以使用 contactArray

  • onErrorReturn 遇到錯誤是發(fā)射指定的數(shù)據(jù)到 onNext欲逃,并正常終止

  • onErrorResumeReturn 遇到錯誤時,發(fā)射設(shè)置好的一個 Observable 饼暑,用來發(fā)送數(shù)據(jù)到 onNext稳析,并正常終止

  • onExceptionResumeReturn 和onErrorResumeReturn 類似洗做,不同之處在于會判斷是否是 Exception。如果是和 onErrorResumeReturn 一樣彰居,不是則會調(diào)用 onError诚纸。不會調(diào)用onNext

Map和flatMap的區(qū)別

  • 前者是嚴(yán)格按照1.2.3.4.5順序發(fā)的,經(jīng)過map以后還是按照這個順序

  • 后者是1.2.3.4.5發(fā)送完到 flatMap 里面陈惰,然后經(jīng)過flatmap進(jìn)行組裝以后再發(fā)出來畦徘,順序可能會打亂

  • 使用 contactMap 可以保證轉(zhuǎn)換后的事件發(fā)射順序。

[圖片上傳失敗...(image-db706b-1682881853

[圖片上傳失敗...(image-bf9436-1682881853347)]

跨進(jìn)程

[圖片上傳失敗...(image-96a68e-1682881853347)]

AIDL

利用了liunx下的Binder機(jī)制 IPC

Linux現(xiàn)有的進(jìn)程通信手段有以下幾種:

  • 管道:在創(chuàng)建時分配一個page大小的內(nèi)存奴潘,緩存區(qū)大小比較有限旧烧;

  • 消息隊(duì)列:信息復(fù)制兩次影钉,額外的CPU消耗画髓;不合適頻繁或信息量大的通信;

  • 共享內(nèi)存:無須復(fù)制平委,共享緩沖區(qū)直接附加到進(jìn)程虛擬地址空間奈虾,速度快;但進(jìn)程間的同步問題操作系統(tǒng)無法實(shí)現(xiàn)廉赔,必須各進(jìn)程利用同步工具解決肉微;

  • 套接字:作為更通用的接口,傳輸效率低蜡塌,主要用于不同機(jī)器或跨網(wǎng)絡(luò)的通信碉纳;

  • 信號量:常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問共享資源時馏艾,其他進(jìn)程也訪問該資源劳曹。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段琅摩。 不適用于信息交換铁孵,更適用于進(jìn)程中斷控制,比如非法內(nèi)存訪問房资,殺死某個進(jìn)程等蜕劝;

Binder來說,數(shù)據(jù)從發(fā)送方的緩存區(qū)拷貝到內(nèi)核的緩存區(qū)轰异,而接收方的緩存區(qū)與內(nèi)核的緩存區(qū)是映射到同一塊物理地址的 岖沛,節(jié)省了一次數(shù)據(jù)拷貝的過程,如圖:

img

一次完整的 Binder IPC 通信過程通常是這樣:

  • 首先 Binder 驅(qū)動在內(nèi)核空間創(chuàng)建一個數(shù)據(jù)接收緩存區(qū)搭独。

  • 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū)婴削,建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系戳稽。

  • 發(fā)送方進(jìn)程通過系統(tǒng)調(diào)用 copyfromuser() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū)馆蠕,由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射期升,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信互躬。

img
img

ContentProvider

通過 ContentResolver 來訪問 ContentProvider 提供的數(shù)據(jù)播赁,而 ContentResolver 通過 uri 來定位自己要訪問的數(shù)據(jù)

  • public boolean **onCreate()**:在創(chuàng)建 ContentProvider 時使用

  • public Cursor **query()**:用于查詢指定 uri 的數(shù)據(jù)返回一個 Cursor

  • public Uri **insert():**用于向指定uri的 ContentProvider 中添加數(shù)據(jù)

  • public int **delete()**:用于刪除指定 uri 的數(shù)據(jù)

  • public int **update()**:用戶更新指定 uri 的數(shù)據(jù)

  • public String **getType()**:用于返回指定的 Uri 中的數(shù)據(jù) MIME 類型

  • 數(shù)據(jù)源可能是數(shù)據(jù)庫,也可以是文件吼渡、xml或網(wǎng)絡(luò)等其他存儲方式容为。

動畫

屬性動畫

  • ValueAnimator

    • 無法像ObjectAnimator一樣直接作用于對象,只能通過添加監(jiān)聽寺酪,獲取動畫過程之坎背,然后手動設(shè)置給對象改變對象的屬性
  • ObjectAnimator

補(bǔ)間動畫TweenAnimation

逐幀動畫FrameAnimation

滑動時不加載圖片

通過RecyclerView的滑動狀態(tài)改變,來對glide的圖片加載請求的暫停/恢復(fù)請求

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n783" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: "Fira Code", Consolas, "Lucida Console", Courier, monospace, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(40, 44, 52); position: relative !important; padding: 6px 10px; box-shadow: rgba(0, 0, 0, 0.16) 0px 2px 5px 0px, rgba(0, 0, 0, 0.12) 0px 2px 10px 0px; margin-bottom: 2.5rem; border: none; border-radius: 6px; width: inherit; color: rgb(40, 44, 52); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"> mRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (getActivity() != null){
Glide.with(getActivity()).resumeRequests();//恢復(fù)Glide加載圖片
}
}else {
if (getActivity() != null){
Glide.with(getActivity()).pauseRequests();//禁止Glide加載圖片
}
}
}
});</pre>

RecyclerView

  • 四級緩存

    • mAttachedScrap:緩存屏幕中可見范圍中的ViewHolder寄雀。

      • mAttachedScrap緩存的是當(dāng)前屏幕上的ViewHolder得滤,對應(yīng)的數(shù)據(jù)結(jié)構(gòu)是ArrayList,沒有大小限制盒犹。在調(diào)用LayoutManager#onLayoutChildren方法時對views進(jìn)行布局

      • 特性是:如果和RecyclerView上的position或者itemId匹配上了懂更,那么就可以直接拿來使用,不需要調(diào)用onBindViewHolder重新綁定數(shù)據(jù)

    • mCachedViews :緩存滑動中即將與RecyclerView分離的ViewHolder

      • mCachedViews緩存滑動時即將與RecyclerView分離的ViewHolder急膀,其數(shù)據(jù)結(jié)構(gòu)為ArrayList沮协,該緩存對大小是有限制的,默認(rèn)為2個

      • 該緩存中的ViewHolder的特性是:只要position和itemId匹配上了卓嫂,則可直接使用慷暂,不需要調(diào)用onBindViewHolder重新綁定數(shù)據(jù)。

    • mViewCacheExtension:自定義實(shí)現(xiàn)的緩存晨雳。

    • mRecyclerPool:ViewHolder緩存池行瑞,可支持不同的ViewType。

      • 本質(zhì)上是一個SparseArray悍募,其中key是ViewType蘑辑,value是ArrayList<ViewHolder>,默認(rèn)每個ArrayList中最多存儲5個

      • ViewHolder存儲在緩存池的前會進(jìn)行重置變成一個干凈的ViewHolder洋魂,所以在復(fù)用時,需要調(diào)用onBindViewHolder重綁數(shù)據(jù)喜鼓。

  • 復(fù)用流程

    • 復(fù)用肯定是在填充子元素過程中完成的

    • 先通過getChangedScrapViewForPosition

      • notifyItemChanged()方法副砍,數(shù)據(jù)發(fā)生變化時,item緩存在mChangedScrap和mAttachedScrap中庄岖,后續(xù)拿到的ViewHolder需要重新綁定數(shù)據(jù)豁翎。此時查找ViewHolder就會通過position和id分別在scrap的mChangedScrap中查找
    • 然后getScrapOrHiddenOrCachedHolderForPosition

      • 沒有找到視圖,根據(jù)position分別在scrap的mAttachedScrap隅忿、mHiddenViews心剥、mCachedViews中查找
    • 再然后RecycledViewPool

    • 最后創(chuàng)建新的ViewHolder

Context

img

首先什么是Context

Context 相當(dāng)于 Application 的大管家邦尊,主要負(fù)責(zé):

  • 四大組件的交互,包括啟動 Activity优烧、Broadcast蝉揍、Service,獲取 ContentResolver 等

  • 獲取系統(tǒng)/應(yīng)用資源畦娄,包括 AssetManager又沾、PackageManager、Resources熙卡、System Service 以及 color杖刷、string、drawable 等文件驳癌,包括獲取緩存文件夾滑燃、刪除文件、SharedPreference 相關(guān)等 數(shù)據(jù)庫(SQLite)相關(guān)喂柒,包括打開數(shù)據(jù)庫不瓶、刪除數(shù)據(jù)庫禾嫉、獲取數(shù)據(jù)庫路徑等

  • 其它輔助功能灾杰,比如設(shè)置 ComponentCallbacks,即監(jiān)聽配置信息改變熙参、內(nèi)存不足等事件的發(fā)生

如果把整個App當(dāng)作一個內(nèi)存塊艳吠,那context相當(dāng)于一個指針

Context實(shí)例個數(shù) = Service個數(shù) + Activity個數(shù) + 1(Application對應(yīng)的Context實(shí)例)

Context的幾種類型

  1. ContextWrapper、ContextThemeWrapper孽椰、ContextImpl 的區(qū)別:

    • ContextWrapper昭娩、ContextThemeWrapper 都是 Context 的代理類,二者的區(qū)別在于 ContextThemeWrapper 有自己的 Theme 以及 Resource黍匾,并且 Resource 可以傳入自己的配置初始化

    • ContextImpl 是 Context 的主要實(shí)現(xiàn)類栏渺,Activity、Service 和 Application 的 BaseContext 都是由它創(chuàng)建的锐涯,即 ContextWrapper 代理的就是 ContextImpl 對象本身

    • ContextImpl 和 ContextThemeWrapper 的主要區(qū)別是磕诊, ContextThemeWrapper 有 Configuration 對象,Resource 可以根據(jù)這個對象來初始化

  2. Activity纹腌、Service 和 Application 的 Base Context 都是由 ContextImpl 創(chuàng)建的霎终,且創(chuàng)建的都是 ContextImpl 對象,即它們都是 ContextImpl 的代理類 Service 和 Application 使用同一個 Recource升薯,和 Activity 使用的 Resource 不同

getApplicationContext 返回的就是 Application 對象本身莱褒,一般情況下它對應(yīng)的是應(yīng)用本身的 Application 對象。

baseContext和applicationContext的區(qū)別

  • getApplicationContext() 是返回應(yīng)用的上下文涎劈,也就是把Application作為Context广凸,生命周期是整個應(yīng)用阅茶,應(yīng)用摧毀它才摧毀

  • Activity的Context,Activity.this的context 返回當(dāng)前Activity的上下文,及把Activity用作Context谅海,生命周期屬于Activity 目派,Activity 摧毀他就摧毀

  • 通常不會建議使用baseContext,是因?yàn)閎aseContext是具體的系統(tǒng)生產(chǎn)的對象胁赢,其中不包含了具體用戶的一些定制信息和內(nèi)容企蹭,所以通過baseContext獲取的Resource/Theme是會與預(yù)期的不符。

ClassLoader及類加載機(jī)制

Android中ClassLoader的種類&特點(diǎn)

  • BootClassLoader(Java的BootStrap ClassLoader): 用于加載Android Framework層class文件智末。

  • PathClassLoader(Java的App ClassLoader): 用于加載已經(jīng)安裝到系統(tǒng)中的apk中的class文件谅摄。

  • DexClassLoader(Java的Custom ClassLoader): 用于加載指定目錄中的class文件。

    1. PathClassLoader :只能加載已經(jīng)安裝到Android系統(tǒng)中的apk文件(/data/app目錄)系馆,是 Android默認(rèn)使用的類加載器.

    2. DexClassLoader :可以加載任意目錄下的dex/jar/apk/zip文件送漠,比 PathClassLoader 更靈活,是 實(shí)現(xiàn)熱修復(fù)的重點(diǎn)由蘑。

  • BaseDexClassLoader: 是PathClassLoader和DexClassLoader的父類闽寡。

各個ClassLoader的加載順序

首先是BootClassLoader在Jvm剛起動的時候去加載java核心API的

然后是PathClassLoader/DexClassLoader加載Apk的應(yīng)用類(熱修復(fù),也就是向PathClassLoader的dexElements進(jìn)行插入新的dex)

最后是CustomerClassLoader加載自定義的class文件

需要注意CLASS_ISPREVERIFIED 標(biāo)記

[圖片上傳失敗...(image-2c4c8b-1682881853346

雙親委托模式

什么是雙親委托模式

當(dāng)某個類加載器需要加載某個.class文件時尼酿,它首先把這個任務(wù)委托給他的上級類加載器爷狈,遞歸這個操作,如果上級的類加載器沒有加載裳擎,自己才會去加載這個類涎永。

流程圖

img

作用

  1. 防止重復(fù)加載同一個.class。通過委托去向上面問一問鹿响,加載過了羡微,就不用再加載一遍。保證數(shù)據(jù)安全惶我。

  2. 保證核心.class不能被篡改妈倔。通過委托方式,不會去篡改核心.clas绸贡,即使篡改也不會去加載盯蝴,即使加載也不會是同一個.class對象了。不同的加載器加載同一個.class也不是同一個Class對象恃轩。這樣保證了Class執(zhí)行安全结洼。

插件化

DexClassLoader去加載外部的apk

  • DexClassLoader重載了findClass方法,在加載類時會調(diào)用其內(nèi)部的DexPathList去加載

  • 插件調(diào)用主工程

    • 在構(gòu)造插件的ClassLoader時會傳入主工程的ClassLoader作為父加載器叉跛,所以插件是可以直接可以通過類名引用主工程的類
  • 主工程調(diào)用插件

    • 若使用多ClassLoader機(jī)制松忍,主工程引用插件中類需要先通過插件的ClassLoader加載該類再通過反射調(diào)用其方法。插件化框架一般會通過統(tǒng)一的入口去管理對各個插件中類的訪問筷厘,并且做一定的限制鸣峭。

    • 若使用單ClassLoader機(jī)制宏所,主工程則可以直接通過類名去訪問插件中的類。該方式有個弊病摊溶,若兩個不同的插件工程引用了一個庫的不同版本爬骤,則程序可能會出錯,所以要通過一些規(guī)范去避免該情況發(fā)生莫换。

熱修復(fù)

熱修復(fù)的原理就是將補(bǔ)丁 dex 文件放到 dexElements 數(shù)組靠前位置霞玄,這樣在加載 class 時,優(yōu)先找到補(bǔ)丁包中的 dex 文件拉岁,加載到 class 之后就不再尋找坷剧,從而原來的 apk 文件中同名的類就不會再使用,從而達(dá)到修復(fù)的目的

  • Android SDK給我們單獨(dú)提供了dex打包工具d8

dex插件

先將修改了的類進(jìn)行打包成dex包喊暖,在將dex進(jìn)行加載惫企,插入到dexElements集合的前面即可。而打包流程是先將.java文件編譯成.class文件陵叽,然后使用SDK工具打包成dex文件并發(fā)布到遠(yuǎn)程服務(wù)端狞尔,然后APP端請求下載

apk插件

重新打了一個新的apk包作為插件,打包很簡單方便巩掺,缺點(diǎn)就是文件大偏序。使用apk的話就沒必要是將dex插入dexElements里面去,直接將之前的dexElements替換就可以了

修復(fù)資源

  • 反射創(chuàng)建新的 AssetManager 對象锌半,反射調(diào)用 addAssetPath 方法加載外部的資源禽车。

  • 將 AssetManager 類型的 mAssets 字段的引用全部替換為新創(chuàng)建的 AssetManager 對象。

組件化

采用接口 + 實(shí)現(xiàn)的結(jié)構(gòu)刊殉。每個組件聲明自己提供的服務(wù) Service,這些 Service 都是一些抽象類或者接口州胳,組件負(fù)責(zé)將這些 Service 實(shí)現(xiàn)并注冊到一個統(tǒng)一的路由 Router 中去记焊。如果要使用某個組件的功能,只需要向 Router 請求這個 Service 的實(shí)現(xiàn)栓撞,具體的實(shí)現(xiàn)細(xì)節(jié)我們?nèi)徊魂P(guān)心遍膜,只要能返回我們需要的結(jié)果就可以了。這與 Binder 的 C/S 架構(gòu)很相像瓤湘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載瓢颅,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末弛说,一起剝皮案震驚了整個濱河市挽懦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌木人,老刑警劉巖信柿,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冀偶,死亡現(xiàn)場離奇詭異,居然都是意外死亡渔嚷,警方通過查閱死者的電腦和手機(jī)进鸠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形病,“玉大人客年,你說我怎么就攤上這事∧牵” “怎么了搀罢?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長侥猩。 經(jīng)常有香客問我榔至,道長,這世上最難降的妖魔是什么欺劳? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任唧取,我火速辦了婚禮,結(jié)果婚禮上划提,老公的妹妹穿的比我還像新娘枫弟。我一直安慰自己,他們只是感情好鹏往,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布淡诗。 她就那樣靜靜地躺著,像睡著了一般伊履。 火紅的嫁衣襯著肌膚如雪韩容。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天唐瀑,我揣著相機(jī)與錄音群凶,去河邊找鬼。 笑死哄辣,一個胖子當(dāng)著我的面吹牛请梢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播力穗,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼毅弧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了当窗?” 一聲冷哼從身側(cè)響起够坐,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后咆霜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邓馒,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年蛾坯,在試婚紗的時候發(fā)現(xiàn)自己被綠了光酣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡脉课,死狀恐怖救军,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倘零,我是刑警寧澤唱遭,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站呈驶,受9級特大地震影響拷泽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袖瞻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一司致、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聋迎,春花似錦脂矫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牺堰,卻和暖如春拄轻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背萌焰。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工哺眯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扒俯。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像一疯,于是被迫代替她去往敵國和親撼玄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容