Android面試題架構(gòu)篇,如果喜歡請持續(xù)關(guān)注和推薦。
如何實(shí)現(xiàn)一個網(wǎng)絡(luò)框架(參考Volley)
1.緩存隊列,以url為key緩存內(nèi)容可以參考Bitmap的處理方式,這里單獨(dú)開啟一個線程。
2.網(wǎng)絡(luò)請求隊列,使用線程池進(jìn)行請求。
3.提供各種不同類型的返回值的解析如String,Json,圖片等等。
mvc、mvp、mvvm:
1.mvc:數(shù)據(jù)、View、Activity,View將操作反饋給Activity,Activitiy去獲取數(shù)據(jù),數(shù)據(jù)通過觀察者模式刷新給View。循環(huán)依賴
1.Activity重,很難單元測試
2.View和Model耦合嚴(yán)重
2.mvp:數(shù)據(jù)、View、Presenter,View將操作給Presenter,Presenter去獲取數(shù)據(jù),數(shù)據(jù)獲取好了返回給Presenter,Presenter去刷新View。PV,PM雙向依賴
1.接口爆炸
2.Presenter很重
3.mvvm:數(shù)據(jù)、View、ViewModel,View將操作給ViewModel,ViewModel去獲取數(shù)據(jù),數(shù)據(jù)和界面綁定了,數(shù)據(jù)更新界面更新。
1.viewModel的業(yè)務(wù)邏輯可以單獨(dú)拿來測試
2.一個view 對應(yīng)一個 viewModel 業(yè)務(wù)邏輯可以分離,不會出現(xiàn)全能類
3.數(shù)據(jù)和界面綁定了,不用寫垃圾代碼,但是復(fù)用起來不舒服
ClassLoader的基礎(chǔ)知識
1.雙親委托:一個ClassLoader類負(fù)責(zé)加載這個類所涉及的所有類,在加載的時候會判斷該類是否已經(jīng)被加載過,然后會遞歸去他父ClassLoader中找。
2.可以動態(tài)加載Jar通過URLClassLoader
3.ClassLoader 隔離問題 JVM識別一個類是由:ClassLoader id+PackageName+ClassName。
4.加載不同Jar包中的公共類:
1.讓父ClassLoader加載公共的Jar,子ClassLoader加載包含公共Jar的Jar,此時子ClassLoader在加載公共Jar的時候會先去父ClassLoader中找。(只適用Java)
2.重寫加載包含公共Jar的Jar的ClassLoader,在loadClass中找到已經(jīng)加載過公共Jar的ClassLoader,也就是把父ClassLoader替換掉。(只適用Java)
3.在生成包含公共Jar的Jar時候把公共Jar去掉。
插件化框架描述:dynamicLoadApk為例子
1.可以通過DexClassLoader來對apk中的dex包進(jìn)行加載訪問
2.如何加載資源是個很大的問題,因?yàn)樗拗鞒绦蛑胁]有apk中的資源,所以調(diào)用R資源會報錯,所以這里使用了Activity中的實(shí)現(xiàn)ContextImpl的getAssets()和getResources()再加上反射來實(shí)現(xiàn)。
3.由于系統(tǒng)啟動Activity有很多初始化動作要做,而我們手動反射很難完成,所以可以采用接口機(jī)制,將Activity的大部分生命周期提取成接口,然后通過代理Activity去調(diào)用插件Activity的生命周期。同時如果像增加一個新生命周期方法的時候,只需要在接口中和代理中聲明一下就行。
4.缺點(diǎn):
1.慎用this,因?yàn)樵赼pk中使用this并不代表宿主中的activity,當(dāng)然如果this只是表示自己的接口還是可以的。除此之外可以使用that代替this。
2.不支持Service和靜態(tài)注冊的Broadcast
3.不支持LaunchMode和Apk中Activity的隱式調(diào)用。
熱修復(fù):Andfix為例子
1.大致原理:apkpatch將兩個apk做一次對比,然后找出不同的部分。可以看到生成的apatch了文件,后綴改成zip再解壓開,里面有一個dex文件。通過jadx查看一下源碼,里面就是被修復(fù)的代碼所在的類文件,這些更改過的類都加上了一個_CF的后綴,并且變動的方法都被加上了一個叫@MethodReplace的annotation,通過clazz和method指定了需要替換的方法。然后客戶端sdk得到補(bǔ)丁文件后就會根據(jù)annotation來尋找需要替換的方法。最后由JNI層完成方法的替換。
2.無法添加新類和新的字段、補(bǔ)丁文件很容易被反編譯、加固平臺可能會使熱補(bǔ)丁功能失效
oKhttp原理:
1.同步和異步:
1.異步使用了Dispatcher來將存儲在 Deque 中的請求分派給線程池中各個線程執(zhí)行。
2.當(dāng)任務(wù)執(zhí)行完成后,無論是否有異常,finally代碼段總會被執(zhí)行,也就是會調(diào)用Dispatcher的finished函數(shù),它將正在運(yùn)行的任務(wù)Call從隊列runningAsyncCalls中移除后,主動的把緩存隊列向前走了一步。
2.連接池:
1.一個Connection封裝了一個socket,ConnectionPool中儲存s著所有的Connection,StreamAllocation是引用計數(shù)的一個單位
2.當(dāng)一個請求獲取一個Connection的時候要傳入一個StreamAllocation,Connection中存著一個弱引用的StreamAllocation列表,每當(dāng)上層應(yīng)用引用一次Connection,StreamAllocation就會加一個。反之如果上層應(yīng)用不使用了,就會刪除一個。
3.ConnectionPool中會有一個后臺任務(wù)定時清理StreamAllocation列表為空的Connection。5分鐘時間,維持5個socket
3.選擇路線與建立連接
1.連接池中已經(jīng)存在連接,就從中取出(get)RealConnection,如果沒有命中就進(jìn)入下一步
2.根據(jù)選擇的路線(Route),調(diào)用Platform.get().connectSocket選擇當(dāng)前平臺Runtime下最好的socket庫進(jìn)行握手
3.將建立成功的RealConnection放入(put)連接池緩存
4.如果存在TLS,就根據(jù)SSL版本與證書進(jìn)行安全握手
5.構(gòu)造HttpStream并維護(hù)剛剛的socket連接,管道建立完成
1.無代理,那么在本地使用DNS查找到ip,注意結(jié)果是數(shù)組,即一個域名有多個IP,這就是自動重連的來源
2.有代理HTTP:設(shè)置socket的ip為代理地址的ip,設(shè)置socket的端口為代理地址的端口
3.代理好處:HTTP代理會幫你在遠(yuǎn)程服務(wù)器進(jìn)行DNS查詢,可以減少DNS劫持。
1.選擇路線有兩種方式:
2.建立連接
4.職責(zé)鏈模式:緩存、重試、建立連接等功能存在于攔截器中網(wǎng)絡(luò)請求相關(guān),主要是網(wǎng)絡(luò)請求優(yōu)化。網(wǎng)絡(luò)請求的時候遇到的問題
retrofit的了解
1.動態(tài)代理創(chuàng)建一個接口的代理類
2.通過反射解析每個接口的注解、入?yún)?gòu)造http請求
3.獲取到返回的http請求,使用Adapter解析成需要的返回值。
Xutils, OKhttp, Volley, Retrofit對比
Xutils:
這個框架非常全面,可以進(jìn)行網(wǎng)絡(luò)請求,可以進(jìn)行圖片加載處理,可以數(shù)據(jù)儲存,還可以對view進(jìn)行注解,使用這個框架非常方便,但是缺點(diǎn)也是非常明顯的,使用這個項(xiàng)目,會導(dǎo)致項(xiàng)目對這個框架依賴非常的嚴(yán)重,一旦這個框架出現(xiàn)問題,那么對項(xiàng)目來說影響非常大的。
OKhttp:
Android開發(fā)中是可以直接使用現(xiàn)成的api進(jìn)行網(wǎng)絡(luò)請求的。就是使用HttpClient,HttpUrlConnection進(jìn)行操作。okhttp針對Java和Android程序,封裝的一個高性能的http請求庫,支持同步,異步,而且okhttp又封裝了線程池,封裝了數(shù)據(jù)轉(zhuǎn)換,封裝了參數(shù)的使用,錯誤處理等。API使用起來更加的方便。但是我們在項(xiàng)目中使用的時候仍然需要自己在做一層封裝,這樣才能使用的更加的順手。
Volley:
Volley是Google官方出的一套小而巧的異步請求庫,該框架封裝的擴(kuò)展性很強(qiáng),支持HttpClient、HttpUrlConnection, 甚至支持OkHttp,而且Volley里面也封裝了ImageLoader,所以如果你愿意你甚至不需要使用圖片加載框架,不過這塊功能沒有一些專門的圖片加載框架強(qiáng)大,對于簡單的需求可以使用,稍復(fù)雜點(diǎn)的需求還是需要用到專門的圖片加載框架。Volley也有缺陷,比如不支持post大數(shù)據(jù),所以不適合上傳文件。不過Volley設(shè)計的初衷本身也就是為頻繁的、數(shù)據(jù)量小的網(wǎng)絡(luò)請求而生。
Retrofit:
Retrofit是Square公司出品的默認(rèn)基于OkHttp封裝的一套RESTful網(wǎng)絡(luò)請求框架,RESTful是目前流行的一套api設(shè)計的風(fēng)格, 并不是標(biāo)準(zhǔn)。Retrofit的封裝可以說是很強(qiáng)大,里面涉及到一堆的設(shè)計模式,可以通過注解直接配置請求,可以使用不同的http客戶端,雖然默認(rèn)是用http ,可以使用不同Json Converter 來序列化數(shù)據(jù),同時提供對RxJava的支持,使用Retrofit + OkHttp + RxJava + Dagger2 可以說是目前比較潮的一套框架,但是需要有比較高的門檻。
Volley VS OkHttp
Volley的優(yōu)勢在于封裝的更好,而使用OkHttp你需要有足夠的能力再進(jìn)行一次封裝。而OkHttp的優(yōu)勢在于性能更高,因?yàn)?OkHttp基于NIO和Okio ,所以性能上要比 Volley更快。IO 和 NIO這兩個都是Java中的概念,如果我從硬盤讀取數(shù)據(jù),第一種方式就是程序一直等,數(shù)據(jù)讀完后才能繼續(xù)操作這種是最簡單的也叫阻塞式IO,還有一種是你讀你的,程序接著往下執(zhí)行,等數(shù)據(jù)處理完你再來通知我,然后再處理回調(diào)。而第二種就是 NIO 的方式,非阻塞式, 所以NIO當(dāng)然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基礎(chǔ)上做的一個更簡單、高效處理數(shù)據(jù)流的一個庫。理論上如果Volley和OkHttp對比的話,更傾向于使用 Volley,因?yàn)閂olley內(nèi)部同樣支持使用OkHttp,這點(diǎn)OkHttp的性能優(yōu)勢就沒了, 而且 Volley 本身封裝的也更易用,擴(kuò)展性更好些。
OkHttp VS Retrofit
毫無疑問,Retrofit 默認(rèn)是基于 OkHttp 而做的封裝,這點(diǎn)來說沒有可比性,肯定首選 Retrofit。 Volley VS Retrofit 這兩個庫都做了不錯的封裝,但Retrofit解耦的更徹底,尤其Retrofit2.0出來,Jake對之前1.0設(shè)計不合理的地方做了大量重構(gòu), 職責(zé)更細(xì)分,而且Retrofit默認(rèn)使用OkHttp,性能上也要比Volley占優(yōu)勢,再有如果你的項(xiàng)目如果采用了RxJava ,那更該使用 Retrofit 。所以這兩個庫相比,Retrofit更有優(yōu)勢,在能掌握兩個框架的前提下該優(yōu)先使用 Retrofit。但是Retrofit門檻要比Volley稍高些,要理解他的原理,各種用法,想徹底搞明白還是需要花些功夫的,如果你對它一知半解,那還是建議在商業(yè)項(xiàng)目使用Volley吧。
Universal-ImageLoader,Picasso,F(xiàn)resco,Glide對比
Fresco
是Facebook推出的開源圖片緩存工具,主要特點(diǎn)包括:兩個內(nèi)存緩存加上 Native 緩存構(gòu)成了三級緩存
優(yōu)點(diǎn):
圖片存儲在安卓系統(tǒng)的匿名共享內(nèi)存, 而不是虛擬機(jī)的堆內(nèi)存中, 圖片的中間緩沖數(shù)據(jù)也存放在本地堆內(nèi)存, 所以, 應(yīng)用程序有更多的內(nèi)存使用, 不會因?yàn)閳D片加載而導(dǎo)致oom, 同時也減少垃圾回收器頻繁調(diào)用回收 Bitmap 導(dǎo)致的界面卡頓, 性能更高。
漸進(jìn)式加載 JPEG 圖片, 支持圖片從模糊到清晰加載。
圖片可以以任意的中心點(diǎn)顯示在 ImageView, 而不僅僅是圖片的中心。
JPEG 圖片改變大小也是在 native 進(jìn)行的, 不是在虛擬機(jī)的堆內(nèi)存, 同樣減少 OOM。
很好的支持 GIF 圖片的顯示。
缺點(diǎn):
框架較大, 影響 Apk 體積
使用較繁瑣
Universal-ImageLoader:
(估計由于HttpClient被Google放棄,作者就放棄維護(hù)這個框架)
優(yōu)點(diǎn):
支持下載進(jìn)度監(jiān)聽
可以在 View 滾動中暫停圖片加載,通過 PauseOnScrollListener 接口可以在 View 滾動中暫停圖片加載。
默認(rèn)實(shí)現(xiàn)多種內(nèi)存緩存算法 這幾個圖片緩存都可以配置緩存算法,不過 ImageLoader 默認(rèn)實(shí)現(xiàn)了較多緩存算法,如 Size 最大先刪除、使用最少先刪除、最近最少使用、先進(jìn)先刪除、時間最長先刪除等。
支持本地緩存文件名規(guī)則定義
Picasso
優(yōu)點(diǎn)
自帶統(tǒng)計監(jiān)控功能。支持圖片緩存使用的監(jiān)控,包括緩存命中率、已使用內(nèi)存大小、節(jié)省的流量等。
支持優(yōu)先級處理。每次任務(wù)調(diào)度前會選擇優(yōu)先級高的任務(wù),比如 App 頁面中 Banner 的優(yōu)先級高于 Icon 時就很適用。
支持延遲到圖片尺寸計算完成加載
支持飛行模式、并發(fā)線程數(shù)根據(jù)網(wǎng)絡(luò)類型而變。 手機(jī)切換到飛行模式或網(wǎng)絡(luò)類型變換時會自動調(diào)整線程池最大并發(fā)數(shù),比如 wifi 最大并發(fā)為 4,4g 為 3,3g 為 2。 這里 Picasso 根據(jù)網(wǎng)絡(luò)類型來決定最大并發(fā)數(shù),而不是 CPU 核數(shù)。
“無”本地緩存。無”本地緩存,不是說沒有本地緩存,而是 Picasso 自己沒有實(shí)現(xiàn),交給了 Square 的另外一個網(wǎng)絡(luò)庫 okhttp 去實(shí)現(xiàn),這樣的好處是可以通過請求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過期時間。
Glide
優(yōu)點(diǎn)
不僅僅可以進(jìn)行圖片緩存還可以緩存媒體文件。Glide 不僅是一個圖片緩存,它支持 Gif、WebP、縮略圖。甚至是 Video,所以更該當(dāng)做一個媒體緩存。
支持優(yōu)先級處理。
與 Activity/Fragment 生命周期一致,支持 trimMemory。Glide 對每個 context 都保持一個 RequestManager,通過 FragmentTransaction 保持與 Activity/Fragment 生命周期一致,并且有對應(yīng)的 trimMemory 接口實(shí)現(xiàn)可供調(diào)用。
支持 okhttp、Volley。Glide 默認(rèn)通過 UrlConnection 獲取數(shù)據(jù),可以配合 okhttp 或是 Volley 使用。實(shí)際 ImageLoader、Picasso 也都支持 okhttp、Volley。
內(nèi)存友好。Glide 的內(nèi)存緩存有個 active 的設(shè)計,從內(nèi)存緩存中取數(shù)據(jù)時,不像一般的實(shí)現(xiàn)用 get,而是用 remove,再將這個緩存數(shù)據(jù)放到一個 value 為軟引用的 activeResources map 中,并計數(shù)引用數(shù),在圖片加載完成后進(jìn)行判斷,如果引用計數(shù)為空則回收掉。內(nèi)存緩存更小圖片,Glide 以 url、view_width、view_height、屏幕的分辨率等做為聯(lián)合 key,將處理后的圖片緩存在內(nèi)存緩存中,而不是原始圖片以節(jié)省大小與 Activity/Fragment 生命周期一致,支持 trimMemory。圖片默認(rèn)使用默認(rèn) RGB_565 而不是 ARGB_888,雖然清晰度差些,但圖片更小,也可配置到 ARGB_888。
Glide 可以通過 signature 或不使用本地緩存支持 url 過期