您好,登錄后才能下訂單哦!
又到年底了,每到這個(gè)時(shí)候,我們都會(huì)慢慢反思,這一年都做了什么?有什么進(jìn)步?年初的計(jì)劃都實(shí)現(xiàn)了嗎?明年年初有跳槽的底氣了嗎?況且今年的互聯(lián)網(wǎng)環(huán)境太差,需要自己有足夠的知識(shí)儲(chǔ)備,才能夠應(yīng)對(duì)這凌冽的寒風(fēng)。
本文主要是整理了中高級(jí)安卓需要會(huì)的(或者說(shuō)面試被頻繁問(wèn)到的內(nèi)容),主要作為參考大綱,之后會(huì)陸續(xù)更新每個(gè)詳細(xì)部分,供大家參考,互相學(xué)習(xí)。
- BAT面試合集(Binder,組件化插件化,熱修復(fù),AOP,QQ換膚,虛擬機(jī),https,線程池原理,音視頻原理)
- 算法合集(Hash,KMP 等)
- 中小廠面試合集(內(nèi)存泄漏,Handler,View,MVC.MVP.MVVM,)
- 大廠相關(guān)更新技術(shù)(Glide,數(shù)據(jù)庫(kù),NDK)
- 面試小知識(shí)(java小知識(shí))
- 設(shè)計(jì)模式(設(shè)計(jì)模式原則和分類)
- 數(shù)據(jù)結(jié)構(gòu)(數(shù)據(jù)結(jié)構(gòu)等等)
- 網(wǎng)絡(luò)編程(三次握手和四次握手,Volley,OKHttps,Retrofit)
- 源碼解析(屬性動(dòng)畫實(shí)現(xiàn)原理等)
- 多線程解析(線程同步,進(jìn)程線程)
- 性能優(yōu)化(Webview,內(nèi)存泄漏和內(nèi)存溢出等)
(順手留下GitHub鏈接,需要獲取相關(guān)面試或者面試寶典核心筆記PDF等內(nèi)容的可以自己去找)
https://github.com/xiangjiana/Android-MS
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。
使用場(chǎng)景比如最常見(jiàn)的 AlertDialog
,拿我們開(kāi)發(fā)過(guò)程中舉例,比如 Camera 開(kāi)發(fā)過(guò)程中,可能需要設(shè)置一個(gè)初始化的相機(jī)配置,設(shè)置攝像頭方向,閃光燈開(kāi)閉,成像質(zhì)量等等,這種場(chǎng)景下就可以使用建造者模式
裝飾者模式: 動(dòng)態(tài)的給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來(lái)說(shuō),裝飾模式比生成子類更為靈活。裝飾者模式可以在不改變?cè)蓄惤Y(jié)構(gòu)的情況下曾強(qiáng)類的功能,比如 Java 中的 BufferedInputStream 包裝 FileInputStream,舉個(gè)開(kāi)發(fā)中的例子,比如在我們現(xiàn)有網(wǎng)絡(luò)框架上需要增加新的功能,那么再包裝一層即可,裝飾者模式解決了繼承存在的一些問(wèn)題,比如多層繼承代碼的臃腫,使代碼邏輯更清晰
觀察者模式,代理模式,門面模式,單例模式,生產(chǎn)者消費(fèi)者模式
這個(gè)通過(guò)對(duì)比來(lái)描述,比如面向?qū)ο蠛兔嫦蜻^(guò)程的對(duì)比,針對(duì)這兩種思想的對(duì)比,還可以舉個(gè)開(kāi)發(fā)中的例子,比如播放器的實(shí)現(xiàn),面向過(guò)程的實(shí)現(xiàn)方式就是將播放視頻的這個(gè)功能分解成多個(gè)過(guò)程,比如,加載視頻地址,獲取視頻信息,初始化解碼器,選擇合適的解碼器進(jìn)行解碼,讀取解碼后的幀進(jìn)行視頻格式轉(zhuǎn)換和音頻重采樣,然后讀取幀進(jìn)行播放,這是一個(gè)完整的過(guò)程,這個(gè)過(guò)程中不涉及類的概念,而面向?qū)ο笞畲蟮奶攸c(diǎn)就是類,封裝繼承和多態(tài)是核心,同樣的以播放器為例,一面向?qū)ο蟮姆绞絹?lái)實(shí)現(xiàn),將會(huì)針對(duì)每一個(gè)功能封裝出一個(gè)對(duì)象,吧如說(shuō)Muxer,獲取視頻信息,Decoder,解碼,格式轉(zhuǎn)換器,視頻播放器,音頻播放器等,每一個(gè)功能對(duì)應(yīng)一個(gè)對(duì)象,由這個(gè)對(duì)象來(lái)完成對(duì)應(yīng)的功能,并且遵循單一職責(zé)原則,一個(gè)對(duì)象只做它相關(guān)的事情
1.繼承 Thread 類實(shí)現(xiàn)多線程
2.實(shí)現(xiàn) Runnable 接口
3.實(shí)現(xiàn) Callable 接口
4.通過(guò)線程池
線程池的工作原理: 線程池可以減少創(chuàng)建和銷毀線程的次數(shù),從而減少系統(tǒng)資源的消耗,當(dāng)一個(gè)任務(wù)提交到線程池時(shí)
a. 首先判斷核心線程池中的線程是否已經(jīng)滿了,如果沒(méi)滿,則創(chuàng)建一個(gè)核心線程執(zhí)行任務(wù),否則進(jìn)入下一步
b. 判斷工作隊(duì)列是否已滿,沒(méi)有滿則加入工作隊(duì)列,否則執(zhí)行下一步
c. 判斷線程數(shù)是否達(dá)到了最大值,如果不是,則創(chuàng)建非核心線程執(zhí)行任務(wù),否則執(zhí)行飽和策略,默認(rèn)拋出異常
Handler,Message,looper 和 MessageQueue
構(gòu)成了安卓的消息機(jī)制,handler 創(chuàng)建后可以通過(guò) sendMessage
將消息加入消息隊(duì)列,然后 looper
不斷的將消息從MessageQueue
中取出來(lái),回調(diào)到 Hander
的handleMessage
方法,從而實(shí)現(xiàn)線程的通信。
從兩種情況來(lái)說(shuō),第一在 UI 線程創(chuàng)建 Handler,此時(shí)我們不需要手動(dòng)開(kāi)啟 looper
,因?yàn)樵趹?yīng)用啟動(dòng)時(shí),在 ActivityThread
的 main 方法中就創(chuàng)建了一個(gè)當(dāng)前主線程的looper
,并開(kāi)啟了消息隊(duì)列,消息隊(duì)列是一個(gè)無(wú)限循環(huán),為什么無(wú)限循環(huán)不會(huì) ANR?因?yàn)榭梢哉f(shuō),應(yīng)用的整個(gè)生命周期就是運(yùn)行在這個(gè)消息循環(huán)中的,安卓是由事件驅(qū)動(dòng)的,Looper.loop
不斷的接收處理事件,每一個(gè)點(diǎn)擊觸摸或者 Activity 每一個(gè)生命周期都是在 Looper.loop
的控制之下的,looper.loop
一旦結(jié)束,應(yīng)用程序的生命周期也就結(jié)束了。我們可以想想什么情況下會(huì)發(fā)生 ANR,第一,事件沒(méi)有得到處理,第二,事件正在處理,但是沒(méi)有及時(shí)完成,而對(duì)事件進(jìn)行處理的就是looper,所以只能說(shuō)事件的處理如果阻塞會(huì)導(dǎo)致 ANR,而不能說(shuō) looper 的無(wú)限循環(huán)會(huì) ANR
另一種情況就是在子線程創(chuàng)建Handler,此時(shí)由于這個(gè)線程中沒(méi)有默認(rèn)開(kāi)啟的消息隊(duì)列,所以我們需要手動(dòng)調(diào)用 looper.prepare()
,并通過(guò) looper.loop
開(kāi)啟消息主線程 Looper
從消息隊(duì)列讀取消息,當(dāng)讀完所有消息時(shí),主線程阻塞。子線程往消息隊(duì)列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠。因此 loop的循環(huán)并不會(huì)對(duì) CPU 性能有過(guò)多的消耗。
非靜態(tài)內(nèi)部類會(huì)持有外部類的引用,如果非靜態(tài)內(nèi)部類的實(shí)例是靜態(tài)的,就會(huì)長(zhǎng)期的維持著外部類的引用,組織被系統(tǒng)回收,解決辦法是使用靜態(tài)內(nèi)部類
匿名內(nèi)部類同樣會(huì)持有外部類的引用,如果在線程中執(zhí)行耗時(shí)操作就有可能發(fā)生內(nèi)存泄漏,導(dǎo)致外部類無(wú)法被回收,直到耗時(shí)任務(wù)結(jié)束,解決辦法是在頁(yè)面退出時(shí)結(jié)束線程中的任務(wù)
Handler 導(dǎo)致的內(nèi)存泄漏也可以被歸納為非靜態(tài)內(nèi)部類導(dǎo)致的,Handler 內(nèi)部
message 是被存儲(chǔ)在 MessageQueue
中的,有些 message 不能馬上被處理,存在的時(shí)間會(huì)很長(zhǎng),導(dǎo)致 handler 無(wú)法被回收,如果 handler 是非靜態(tài)的,就會(huì)導(dǎo)致它的外部類無(wú)法被回收,解決辦法是 1.使用靜態(tài) handler,外部類引用使用弱引用處理 2.在退出頁(yè)面時(shí)移除消息隊(duì)列中的消息
根據(jù)場(chǎng)景確定使用Activity的Context還是Application的Context,因?yàn)槎呱芷诓煌?,?duì)于不必須使用 Activity 的 Context 的場(chǎng)景(Dialog),一律采用 Application
的 Context
,單例模式是最常見(jiàn)的發(fā)生此泄漏的場(chǎng)景,比如傳入一個(gè) Activity 的Context 被靜態(tài)類引用,導(dǎo)致無(wú)法回收
使用靜態(tài) View 可以避免每次啟動(dòng) Activity 都去讀取并渲染 View,但是靜態(tài) View會(huì)持有 Activity 的引用,導(dǎo)致無(wú)法回收,解決辦法是在 Activity 銷毀的時(shí)候?qū)㈧o態(tài)View 設(shè)置為 null(View 一旦被加載到界面中將會(huì)持有一個(gè) Context 對(duì)象的引用,在這個(gè)例子中,這個(gè) context 對(duì)象是我們的 Activity,聲明一個(gè)靜態(tài)變量引用這個(gè)View,也就引用了 activity)
WebView
只要使用一次,內(nèi)存就不會(huì)被釋放,所以 WebView
都存在內(nèi)存泄漏的問(wèn)題,通常的解決辦法是為 WebView
單開(kāi)一個(gè)進(jìn)程,使用 AIDL
進(jìn)行通信,根據(jù)業(yè)務(wù)需求在合適的時(shí)機(jī)釋放掉
如 Cursor,F(xiàn)ile 等,內(nèi)部往往都使用了緩沖,會(huì)造成內(nèi)存泄漏,一定要確保關(guān)閉它并將引用置為 null
集合用于保存對(duì)象,如果集合越來(lái)越大,不進(jìn)行合理的清理,尤其是入股集合是靜態(tài)的
bitmap 是比較占內(nèi)存的,所以一定要在不使用的時(shí)候及時(shí)進(jìn)行清理,避免靜態(tài)變量持有大的 bitmap 對(duì)象
很多需要register和unregister
的系統(tǒng)服務(wù)要在合適的時(shí)候進(jìn)行unregister
,手動(dòng)添加的 listener 也需要及時(shí)移除
1.使用更加輕量的數(shù)據(jù)結(jié)構(gòu): 如使用 ArrayMap/SparseArray
替代HashMap
,HashMap
更耗內(nèi)存,因?yàn)樗枰~外的實(shí)例對(duì)象來(lái)記錄 Mapping 操作,SparseArray
更加高效,因?yàn)樗苊饬?Key Value 的自動(dòng)裝箱,和裝箱后的解箱操作
2.便面枚舉的使用,可以用靜態(tài)常量或者注解@IntDef
替代
3.Bitmap 優(yōu)化:
a.尺寸壓縮: 通過(guò) InSampleSize 設(shè)置合適的縮放
b.顏色質(zhì)量: 設(shè)置合適的format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異
c.inBitmap: 使用inBitmap
屬性可以告知 Bitmap 解碼器去嘗試使用已經(jīng)存在的內(nèi)存區(qū)域,新解碼的Bitmap會(huì)嘗試去使用之前那張Bitmap在Heap中所占據(jù)的pixeldata
內(nèi)存區(qū)域,而不是去問(wèn)內(nèi)存重新申請(qǐng)一塊區(qū)域來(lái)存放 Bitmap。利用這種特性,即使是上千張的圖片,也只會(huì)僅僅只需要占用屏幕所能夠顯示的圖片數(shù)量的內(nèi)存大小,但復(fù)用存在一些限制,具體體現(xiàn)在:在 Android 4.4 之前只能重用相同大小的 Bitmap 的內(nèi)存,而 Android 4.4 及以后版本則只要后來(lái)的 Bitmap 比之前的小即可。使用inBitmap
參數(shù)前,每創(chuàng)建一個(gè) Bitmap 對(duì)象都會(huì)分配一塊內(nèi)存供其使用,而使用了inBitmap
參數(shù)后,多個(gè) Bitmap 可以復(fù)用一塊內(nèi)存,這樣可以提高性能
4.StringBuilder 替代 String: 在有些時(shí)候,代碼中會(huì)需要使用到大量的字符串拼接的操作,這種時(shí)候有必要考慮使用 StringBuilder
來(lái)替代頻繁的“+”
5.避免在類似 onDraw
這樣的方法中創(chuàng)建對(duì)象,因?yàn)樗鼤?huì)迅速占用大量?jī)?nèi)存,引起頻繁的 GC
甚至內(nèi)存抖動(dòng)
6.減少內(nèi)存泄漏也是一種避免 OOM 的方法
a: Service 設(shè)置成 START_STICKY kill 后會(huì)被重啟(等待 5 秒左右),重傳 Intent,保持與重啟前一樣
b: 通過(guò) startForeground 將進(jìn)程設(shè)置為前臺(tái)進(jìn)程, 做前臺(tái)服務(wù),優(yōu)先級(jí)和前臺(tái)應(yīng)用一個(gè)級(jí)別,除非在系統(tǒng)內(nèi)存非常缺,否則此進(jìn)程不會(huì)被 kill
c: 雙進(jìn)程 Service: 讓 2 個(gè)進(jìn)程互相保護(hù)對(duì)方,其中一個(gè) Service 被清理后,另外沒(méi)被清理的進(jìn)程可以立即重啟進(jìn)程
d: 用 C 編寫守護(hù)進(jìn)程(即子進(jìn)程) : Android 系統(tǒng)中當(dāng)前進(jìn)程(Process)fork 出來(lái)的子進(jìn)程,被系統(tǒng)認(rèn)為是兩個(gè)不同的進(jìn)程。當(dāng)父進(jìn)程被殺死的時(shí)候,子進(jìn)程仍然可以存活,并不受影響(Android5.0 以上的版本不可行)聯(lián)系廠商,加入白名單
e. 鎖屏狀態(tài)下,開(kāi)啟一個(gè)一像素 Activity
app 冷啟動(dòng): 當(dāng)應(yīng)用啟動(dòng)時(shí),后臺(tái)沒(méi)有該應(yīng)用的進(jìn)程,這時(shí)系統(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給該應(yīng)用, 這個(gè)啟動(dòng)方式就叫做冷啟動(dòng)(后臺(tái)不存在該應(yīng)用進(jìn)程)。冷啟動(dòng)因?yàn)橄到y(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給它,所以會(huì)先創(chuàng)建和初始化 Application 類,再創(chuàng)建和初始化 MainActivity
類(包括一系列的測(cè)量、布局、繪制),最后顯示在界面上。
app 熱啟動(dòng): 當(dāng)應(yīng)用已經(jīng)被打開(kāi), 但是被按下返回鍵、Home 鍵等按鍵時(shí)回到桌面或者是其他程序的時(shí)候,再重新打開(kāi)該 app 時(shí), 這個(gè)方式叫做熱啟動(dòng)(后臺(tái)已經(jīng)存在該應(yīng)用進(jìn)程)。熱啟動(dòng)因?yàn)闀?huì)從已有的進(jìn)程中來(lái)啟動(dòng),所以熱啟動(dòng)就不會(huì)走 Application 這步了,而是直接走 MainActivity
(包括一系列的測(cè)量、布局、繪制),所以熱啟動(dòng)的過(guò)程只需要?jiǎng)?chuàng)建和初始化一個(gè) MainActivity
就行了,而不必創(chuàng)建和初始化Application
當(dāng)點(diǎn)擊 app 的啟動(dòng)圖標(biāo)時(shí),安卓系統(tǒng)會(huì)從 Zygote 進(jìn)程中 fork 創(chuàng)建出一個(gè)新的進(jìn)程分配給該應(yīng)用,之后會(huì)依次創(chuàng)建和初始化 Application 類、創(chuàng)建MainActivity
類、加載主題樣式 Theme 中的 windowBackground
等屬性設(shè)置給 MainActivity
以及配置 Activity 層級(jí)上的一些屬性、再 inflate 布局、當(dāng) onCreate/onStart/onResume
方法都走完了后最后才進(jìn)行 contentView
的 measure/layout/draw
顯示在界面上
Application
構(gòu)造方法 –> attachBaseContext()
–>onCreate
–>Activity 構(gòu)造方法 –>onCreate()
–> 配置主體中的背景等操作 –>onStart()
–> onResume()
–> 測(cè)量、布局、繪制顯示
冷啟動(dòng)的優(yōu)化主要是視覺(jué)上的優(yōu)化,解決白屏問(wèn)題,提高用戶體驗(yàn),所以通過(guò)上面冷啟動(dòng)的過(guò)程。能做的優(yōu)化如下:
1、減少 onCreate()方法的工作量
2、不要讓 Application 參與業(yè)務(wù)的操作
3、不要在 Application 進(jìn)行耗時(shí)操作
4、不要以靜態(tài)變量的方式在 Application 保存數(shù)據(jù)
5、減少布局的復(fù)雜度和層級(jí)
6、減少主線程耗時(shí)
原因在于加載主題樣式 Theme 中的 windowBackground
等屬性設(shè)置給MainActivity
發(fā)生在 inflate 布局當(dāng) onCreate/onStart/onResume
方法之前,而windowBackground
背景被設(shè)置成了白色或者黑色,所以我們進(jìn)入 app 的第一個(gè)界面的時(shí)候會(huì)造成先白屏或黑屏一下再進(jìn)入界面。解決思路如下
1. 給他設(shè)置 windowBackground
背景跟啟動(dòng)頁(yè)的背景相同,如果你的啟動(dòng)頁(yè)是張圖片那么可以直接給 windowBackground
這個(gè)屬性設(shè)置該圖片那么就不會(huì)有一閃的效果了
<style name=``"Splash_Theme"`
parent=``"@android:style/Theme.NoTitleBar"``>`
<item
name=``"android:windowBackground"``>@drawable/splash_bg</item>`
<item name=``"android:windowNoTitle"``>``true``</item>`</style> `
2. 采用世面的處理方法,設(shè)置背景是透明的,給人一種延遲啟動(dòng)的感覺(jué)。,將背景顏色設(shè)置為透明色,這樣當(dāng)用戶點(diǎn)擊桌面 APP 圖片的時(shí)候,并不會(huì)"立即"進(jìn)入APP,而且在桌面上停留一會(huì),其實(shí)這時(shí)候 APP 已經(jīng)是啟動(dòng)的了,只是我們心機(jī)的把 Theme 里的 windowBackground
的顏色設(shè)置成透明的,強(qiáng)行把鍋甩給了手機(jī)應(yīng)用廠商(手機(jī)反應(yīng)太慢了啦)
<style name=``"Splash_Theme"`
parent=``"@android:style/Theme.NoTitleBar"``>`
<item name=``"android:windowIsTranslucent"``>``true``</item>`
<item name=``"android:windowNoTitle"``>``true``</item>`</style> `
3. 以上兩種方法是在視覺(jué)上顯得更快,但其實(shí)只是一種表象,讓應(yīng)用啟動(dòng)的更快,有一種思路,將 Application
中的不必要的初始化動(dòng)作實(shí)現(xiàn)懶加載,比如,在SpashActivity
顯示后再發(fā)送消息到 Application,去初始化,這樣可以將初始化的動(dòng)作放在后邊,縮短應(yīng)用啟動(dòng)到用戶看到界面的時(shí)間
AsyncTask
,HandlerThread
,IntentService
AsyncTask
原理: 內(nèi)部是 Handler 和兩個(gè)線程池實(shí)現(xiàn)的,Handler 用于將線程切換到主線程,兩個(gè)線程池一個(gè)用于任務(wù)的排隊(duì),一個(gè)用于執(zhí)行任務(wù),當(dāng) AsyncTask
執(zhí)行 execute 方法時(shí)會(huì)封裝出一個(gè) FutureTask
對(duì)象,將這個(gè)對(duì)象加入隊(duì)列中,如果此時(shí)沒(méi)有正在執(zhí)行的任務(wù),就執(zhí)行它,執(zhí)行完成之后繼續(xù)執(zhí)行隊(duì)列中下一個(gè)任務(wù),執(zhí)行完成通過(guò) Handler 將事件發(fā)送到主線程。AsyncTask
必須在主線程初始化,因?yàn)閮?nèi)部的 Handler 是一個(gè)靜態(tài)對(duì)象,在 AsyncTask
類加載的時(shí)候他就已經(jīng)被初始化了。在 Android3.0 開(kāi)始,execute 方法串行執(zhí)行任務(wù)的,一個(gè)一個(gè)來(lái),3.0 之前是并行執(zhí)行的。如果要在 3.0 上執(zhí)行并行任務(wù),可以調(diào)用executeOnExecutor 方法
HandlerThread
原理: 繼承自 Thread,start 開(kāi)啟線程后,會(huì)在其 run 方法中會(huì)通過(guò) Looper
創(chuàng)建消息隊(duì)列并開(kāi)啟消息循環(huán),這個(gè)消息隊(duì)列運(yùn)行在子線程中,所以可以將 HandlerThread
中的 Looper
實(shí)例傳遞給一個(gè) Handler,從而保證這個(gè)Handler 的 handleMessage
方法運(yùn)行在子線程中,Android 中使用 HandlerThread
的一個(gè)場(chǎng)景就是 IntentService
IntentService
原理: 繼承自 Service,它的內(nèi)部封裝了 HandlerThread
和 Handler,可以執(zhí)行耗時(shí)任務(wù),同時(shí)因?yàn)樗且粋€(gè)服務(wù),優(yōu)先級(jí)比普通線程高很多,所以更適合執(zhí)行一些高優(yōu)先級(jí)的后臺(tái)任務(wù),HandlerThread
底層通過(guò) Looper
消息隊(duì)列實(shí)現(xiàn)的,所以它是順序的執(zhí)行每一個(gè)任務(wù)??梢酝ㄟ^(guò) Intent 的方式開(kāi)啟IntentService
,IntentService
通過(guò) handler 將每一個(gè) intent 加入 HandlerThread
子線程中的消息隊(duì)列,通過(guò) looper 按順序一個(gè)個(gè)的取出并執(zhí)行,執(zhí)行完成后自動(dòng)結(jié)束自己,不需要開(kāi)發(fā)者手動(dòng)關(guān)閉
1.耗時(shí)的網(wǎng)絡(luò)訪問(wèn)
2.大量的數(shù)據(jù)讀寫
3.數(shù)據(jù)庫(kù)操作
4.硬件操作(比如 camera)
5.調(diào)用 thread 的 join()方法、sleep()方法、wait()方法或者等待線程鎖的時(shí)候
6.service binder 的數(shù)量達(dá)到上限
7.system server 中發(fā)生 WatchDog ANR
8.service 忙導(dǎo)致超時(shí)無(wú)響應(yīng)
9.其他線程持有鎖,導(dǎo)致主線程等待超時(shí)
10.其它線程終止或崩潰導(dǎo)致主線程一直等待
當(dāng) Android 端需要獲得數(shù)據(jù)時(shí)比如獲取網(wǎng)絡(luò)中的圖片,首先從內(nèi)存中查(按鍵查找),內(nèi)存中沒(méi)有的再?gòu)拇疟P文件或 sqlite
中去查找,若磁盤中也沒(méi)有才通過(guò)網(wǎng)絡(luò)獲取
LruCache
中 Lru
算法的實(shí)現(xiàn)就是通過(guò) LinkedHashMap
來(lái)實(shí)現(xiàn)的。LinkedHashMap
繼承于HashMap
,它使用了一個(gè)雙向鏈表來(lái)存儲(chǔ) Map 中的 Entry 順序關(guān)系,對(duì)于 get、put、remove 等操作,LinkedHashMap
除了要做 HashMap
做的事情,還做些調(diào)整 Entry 順序鏈表的工作。
LruCache
中將 LinkedHashMap
的順序設(shè)置為 LRU 順序來(lái)實(shí)現(xiàn) LRU 緩存,每次調(diào)用 get(也就是從內(nèi)存緩存中取圖片),則將該對(duì)象移到鏈表的尾端。調(diào)用 put 插入新的對(duì)象也是存儲(chǔ)在鏈表尾端,這樣當(dāng)內(nèi)存緩存達(dá)到設(shè)定的最大值時(shí),將鏈表頭部的對(duì)象(近期最少用到的)移除。
JavaVM 是虛擬機(jī)在 JNI 層的代表,一個(gè)進(jìn)程只有一個(gè) JavaVM,所有的線程共用一個(gè) JavaVM
。
JNIEnv
表示 Java 調(diào)用 native 語(yǔ)言的環(huán)境,是一個(gè)封裝了幾乎全部 JNI 方法的指針。JNIEnv
只在創(chuàng)建它的線程生效,不能跨線程傳遞,不同線程的 JNIEnv
彼此獨(dú)立。native 環(huán)境中創(chuàng)建的線程,如果需要訪問(wèn) JNI,必須要調(diào)用 AttachCurrentThread
關(guān)聯(lián),并使用 DetachCurrentThread
解除鏈接。
方法: 對(duì)象繼承 Serializable 類即可實(shí)現(xiàn)序列化,就是這么簡(jiǎn)單,也是它最吸引我們的地方
Parcelable
方式的實(shí)現(xiàn)原理是將一個(gè)完整的對(duì)象進(jìn)行分解,用起來(lái)比較麻煩
1)在使用內(nèi)存的時(shí)候,Parcelable 比 Serializable 性能高,所以推薦使用 Parcelable。
2)Serializable 在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的 GC。
3)Parcelable
不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤上的情況,因?yàn)?Parcelable
不能很好的保證數(shù)據(jù)的持續(xù)性,在外界有變化的情況下。盡管 Serializable
效率低點(diǎn),但此時(shí)還是建議使用 Serializable
。
4)android 上應(yīng)該盡量采用 Parcelable
,效率至上,效率遠(yuǎn)高于 Serializable
(順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找)
https://github.com/xiangjiana/Android-MS
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。