[TOC]
HASH算法
常用的摘要算法包括MD5鉴吹,SHA1,SHA256
-
消息摘要算法的特點(diǎn):
無論輸入的消息有多長惩琉,計算出來的消息摘要的長度總是固定的豆励。
消息摘要看起來是“隨機(jī)的”。這些比特看上去是胡亂的雜湊在一起的瞒渠。
一般地良蒸,只要輸入的消息不同,對其進(jìn)行摘要以后產(chǎn)生的摘要消息也必不相同在孝;但相同的輸入必會產(chǎn)生相同的輸出诚啃。
消息摘要函數(shù)是無陷門的單向函數(shù),即只能進(jìn)行正向的信息摘要私沮,而無法從摘要中恢復(fù)出任何的消息始赎,甚至根本就找不到任何與原信息相關(guān)的信息。
好的摘要算法仔燕,無法找到兩條消息造垛,是它們的摘要相同。
主要特征是加密過程不需要密鑰晰搀,并且經(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請求
客戶端向服務(wù)器發(fā)起HTTPS請求,連接到服務(wù)器的443端口癣疟。
服務(wù)器端有一個密鑰對挣柬,即公鑰和私鑰,是用來進(jìn)行非對稱加密使用的睛挚,服務(wù)器端保存著私鑰邪蛔,不能將其泄露,公鑰可以發(fā)送給任何人扎狱。
服務(wù)器將自己的公鑰發(fā)送給客戶端店溢。
客戶端收到服務(wù)器端的公鑰之后,會對公鑰進(jìn)行檢查委乌,驗(yàn)證其合法性床牧,如果公鑰合格,那么客戶端會生成一個隨機(jī)值遭贸,這個隨機(jī)值就是用于進(jìn)行對稱加密的密鑰戈咳,我們將該密鑰稱之為client key,即客戶端密鑰壕吹,這樣在概念上和服務(wù)器端的密鑰容易進(jìn)行區(qū)分著蛙。然后用服務(wù)器的公鑰對客戶端密鑰進(jìn)行非對稱加密,這樣客戶端密鑰就變成密文了耳贬,至此踏堡,HTTPS中的第一次HTTP請求結(jié)束。
第二次HTTP請求
客戶端會發(fā)起HTTPS中的第二個HTTP請求咒劲,將加密之后的客戶端密鑰發(fā)送給服務(wù)器顷蟆。
服務(wù)器接收到客戶端發(fā)來的密文之后,會用自己的私鑰對其進(jìn)行非對稱解密腐魂,解密之后的明文就是客戶端密鑰帐偎,然后用客戶端密鑰對數(shù)據(jù)進(jìn)行對稱加密,這樣數(shù)據(jù)就變成了密文蛔屹。
然后服務(wù)器將加密后的密文發(fā)送給客戶端削樊。
客戶端收到服務(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
-
newCachedThreadPool
建一個可緩存線程池么伯,如果線程池長度超過處理需要宾毒,可靈活回收空閑線程迂猴,若無可回收,則新建線程名扛。
****線程池為無限大****
-
newFixedThreadPool
- 可控制線程最大并發(fā)數(shù)晰赞,超出的線程會在隊(duì)列中等待。
-
newScheduledThreadPool
- 支持定時纽甘,延遲及周期性任務(wù)執(zhí)行
-
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
先注冊監(jiān)聽觀察Activity/Fragment
在Activity destroy后將Activity的弱引用關(guān)聯(lián)到ReferenceQueue中姻氨,這樣Activity將要被GC前,會出現(xiàn)在ReferenceQueue中剪验。
隨后肴焊,會向主線程的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)
通過對比ObserverWrapper和LiveData之間的version羡玛,來判斷是否更新
Databing\ViewModel
-
綁定的方式分兩種
單向綁定 直接在xml中寫對應(yīng)的參數(shù)名
android:text="@{userInfo.name}"
雙向綁定
android:text="@={userInfo.nickName}"
-
原理
首先通過
DataBindingUtil.setContentView()
來查找對應(yīng)的布局然后生成一個全新的tag值并且賦值給每個view
進(jìn)行臟標(biāo)記
注冊監(jiān)聽
jni
普通應(yīng)用
java->jni: .so/.dll
jni->java:
jclass cls = (*env)->FindClass(env, "jni/test/Demo"); //把點(diǎn)號換成斜杠
jclass cls = (*env)-> GetObjectClass(env, obj); //其中obj是要引用的對象别智,類型是jobject
Call<TYPE>Method或者 CallStatic<TYPE>Method(訪問類的靜態(tài)方法)
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);
(*env)->SetObjectField(env, jobj, fid, new_jstr);
直播
音頻
AudioRecord
單雙聲道
采樣率
緩沖區(qū)
單幀大小計算
p幀(與前一幀的差值宗苍,較小) i幀(關(guān)鍵幀薄榛,較大)讳窟,B幀(是前一幀及后一幀的差別,但解碼麻煩敞恋,需要先知道前一幀和后一幀)
-
webrtc - (ns降噪模塊丽啡,vad語音端點(diǎn)識別模塊-可用于人聲識別,aecm回聲消除模塊)
幀間隔毫秒必須是80的倍數(shù)
幀大小必須是160的倍數(shù)
視頻
編碼
攝像頭(攝像頭數(shù)硬猫,前后补箍,采集高寬改执,閃光燈,對焦方式)
預(yù)覽幀數(shù)
縮放和對焦
軟/硬解碼
-
h264/h265
壓縮比接近50%
解碼cpu負(fù)擔(dān)大
減少實(shí)時的時延坑雅、減少信道獲取時間和隨機(jī)接入時延辈挂、降低復(fù)雜度
ijkplayer
-
ffmepg
- 音視頻解碼器
-
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)存
內(nèi)存泄漏 Memory Leak: 本該回收的對象不能被回收而停留在堆內(nèi)存中早龟,從而產(chǎn)生了內(nèi)存泄漏。
內(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的三級緩存
通過
BitmapFactory.Options
中的inJustDecodeBounds = true
不對bitmap進(jìn)行實(shí)際的內(nèi)存分配来吩,但仍然可以獲得所有屬性通過對比bitmap的實(shí)際大小和imageView的大小進(jìn)行二次采樣
inSimpleSize
使用
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)
表示事件是否會繼續(xù)分發(fā)出去兵钮,默認(rèn)返回false蛆橡,返回true時表示事件不會再繼續(xù)分發(fā),甚至都不會分發(fā)到自身的onTouchEvent方法掘譬;
dispatchTouchEvent 分發(fā)事件泰演,當(dāng)該方法返回true時,該View不會繼續(xù)分發(fā)事件葱轩,包括該事件不會繼續(xù)分發(fā)到該View的onInterceptTouchEvent方法和onTouchEvent方法睦焕;
onInterceptTouchEvent 攔截事件的傳遞,是否會繼續(xù)向子View靴拱、子ViewGroup傳遞垃喊,當(dāng)該方法返回true時,事件不會繼續(xù)向子View袜炕、子ViewGroup傳遞本谜,相當(dāng)于父級View把事件在此處截斷了;
onTouchEvent 消費(fèi)事件偎窘,對點(diǎn)擊事件做相應(yīng)的點(diǎn)擊響應(yīng)處理乌助,具體執(zhí)行點(diǎn)擊后的操作,如果子View不做處理陌知,即返回false他托,該事件還會繼續(xù)傳遞到父View的onTouchEvent方法去處理,直到傳遞到組外層仆葡; 如果該方法返回true上祈,表示這個事件被消費(fèi)掉了,這個事件就此終止了浙芙,不會再有任何傳遞登刺;
Android事件分發(fā)是先傳遞到ViewGroup,再由ViewGroup傳遞到View的嗡呼。
在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進(jìn)行攔截纸俭,onInterceptTouchEvent方法返回true代表不允許事件繼續(xù)向子View傳遞,返回false代表不對事件進(jìn)行攔截南窗,默認(rèn)返回false揍很。
子View中如果將傳遞的事件消費(fèi)掉,ViewGroup中將無法接收到任何事件
[圖片上傳失敗...(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
reified 使得泛型的方法假裝在運(yùn)行時能夠獲取泛型的類信息
原來在Java中脱货,類處于頂層岛都,類包含屬性和方法,在Kotlin中振峻,函數(shù)站在了類的位置疗绣,我們可以直接把函數(shù)放在代碼文件的頂層,讓它不從屬于任何類,可以通過import 包名.函數(shù)名來
-
導(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 時間片
協(xié)程其實(shí)就相當(dāng)于回調(diào)立轧,利用掛起函數(shù)來等待回調(diào)結(jié)果格粪,不會阻塞線程
協(xié)程可以用來直接標(biāo)記方法,由程序員自己實(shí)現(xiàn)切換氛改,調(diào)度帐萎,不再采用傳統(tǒng)的時間段競爭機(jī)制
-
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>
-
CoroutineContext - 可以理解為協(xié)程的上下文
Dispatchers.Default
Dispatchers.IO -
Dispatchers.Main - 主線程
Dispatchers.Unconfined - 沒指定,就是在當(dāng)前線程
-
CoroutineStart - 啟動模式胜卤,默認(rèn)是DEAFAULT疆导,也就是創(chuàng)建就啟動
DEAFAULT - 模式模式,不寫就是默認(rèn)
ATOMIC -
UNDISPATCHED
LAZY - 懶加載模式葛躏,你需要它的時候澈段,再調(diào)用啟動
block - 閉包方法體饺蔑,定義協(xié)程內(nèi)需要執(zhí)行的操作
-
Job - 協(xié)程構(gòu)建函數(shù)的返回值肥照,可以把 Job 看成協(xié)程對象本身
job.start() - 啟動協(xié)程,除了 lazy 模式拢锹,協(xié)程都不需要手動啟動
job.join() - 等待協(xié)程執(zhí)行完畢
job.cancel() - 取消一個協(xié)程
job.cancelAndJoin() - 等待協(xié)程執(zhí)行完畢然后再取消
-
-
async - 創(chuàng)建帶返回值的協(xié)程摩窃,返回的是 Deferred 類
async 同 launch 唯一的區(qū)別就是 async 是有返回值的
-
Deferred 繼承自 Job 接口兽叮,Job有的它都有,增加了一個方法 await ,這個方法接收的是 async 閉包中返回的值充择,async 的特點(diǎn)是不會阻塞當(dāng)前線程德玫,但會阻塞所在協(xié)程匪蟀,也就是掛起
但是注意啊椎麦,async 并不會阻塞線程,只是阻塞鎖調(diào)用的協(xié)程
withContext - 不創(chuàng)建新的協(xié)程材彪,在指定協(xié)程上運(yùn)行代碼塊
-
runBlocking - 不是 GlobalScope 的 API观挎,可以獨(dú)立使用,區(qū)別是 runBlocking 里面的 delay 會阻塞線程段化,而 launch 創(chuàng)建的不會
- 在 runBlocking 閉包里面啟動另外的協(xié)程嘁捷,協(xié)程里面是可以嵌套啟動別的協(xié)程的
協(xié)程執(zhí)行時, 協(xié)程和協(xié)程显熏,協(xié)程和線程內(nèi)代碼是順序運(yùn)行的
協(xié)程掛起時雄嚣,就不會執(zhí)行了,而是等待掛起完成且線程空閑時才能繼續(xù)執(zhí)行
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)
-
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。
-
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í)行單位
-
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)
-
指定圖片大小
- 自動判斷imageView大小捎废,然后只加載等大的像素笑窜,而不會全部加載進(jìn)imageView
方便圖片格式的切換
方便自定義圖片的裁剪等轉(zhuǎn)換(BitmapTransformation)
Retrefit
本質(zhì)上 Retrofit 是一個對 OkHttp 進(jìn)行進(jìn)一步封裝的框架
原理
通過 addConverterFactory 和 addCallAdapterFactory 進(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ù)拷貝的過程,如圖:
一次完整的 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)程間的通信互躬。
ContentProvider
通過 ContentResolver 來訪問 ContentProvider 提供的數(shù)據(jù)播赁,而 ContentResolver 通過 uri 來定位自己要訪問的數(shù)據(jù)
public boolean **onCreate()**:
在創(chuàng)建 ContentProvider 時使用public Cursor **query()**:
用于查詢指定 uri 的數(shù)據(jù)返回一個 Cursorpublic 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
首先什么是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的幾種類型
-
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ù)這個對象來初始化
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文件。
PathClassLoader :只能加載已經(jīng)安裝到Android系統(tǒng)中的apk文件(/data/app目錄)系馆,是 Android默認(rèn)使用的類加載器.
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ù)委托給他的上級類加載器爷狈,遞歸這個操作,如果上級的類加載器沒有加載裳擎,自己才會去加載這個類涎永。
流程圖
作用
防止重復(fù)加載同一個
.class
。通過委托去向上面問一問鹿响,加載過了羡微,就不用再加載一遍。保證數(shù)據(jù)安全惶我。保證核心
.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)很相像瓤湘。