面試專(zhuān)題我放在git上了图甜,地址Github 歡迎fork然后一起更新
0,Okhttp相關(guān)
操作:
- 創(chuàng)建一個(gè)OkhttpClient對(duì)象
- 然后創(chuàng)建一個(gè)request對(duì)象,通過(guò)內(nèi)部類(lèi)Builder調(diào)用生成的Request對(duì)象丽惶,對(duì)象攜帶請(qǐng)求參數(shù)
- 創(chuàng)建一個(gè)Call對(duì)象譬胎,調(diào)用execute(同步差牛,線程阻塞)/enqueue(異步)(回調(diào)方法是運(yùn)行在子線程中,記住無(wú)法更新UI堰乔,得通過(guò)handler)
優(yōu)點(diǎn):
1偏化,Okhttp的最大并發(fā)量為64,比volley的只有4個(gè)強(qiáng)很多倍
2镐侯,使用連接池技術(shù)侦讨,支持5個(gè)并發(fā)的socket連接,默認(rèn)keepAlive時(shí)間為5分鐘苟翻,解決TCP握手揮手的效率問(wèn)題韵卤,減少握手次數(shù)
3,支持Gzip壓縮崇猫,且操作對(duì)用戶(hù)透明沈条,可以通過(guò)header設(shè)置,在發(fā)起請(qǐng)求的時(shí)候自動(dòng)加入header诅炉,Accept-Encoding:gzip,而我們的服務(wù)器返回的時(shí)候header中有Content-Encoding:gzip
4拍鲤,利用響應(yīng)緩存來(lái)避免重復(fù)的網(wǎng)絡(luò)請(qǐng)求
5贴谎,很方便的添加攔截器,通常情況下季稳,攔截器用來(lái)添加擅这,移除,轉(zhuǎn)換請(qǐng)求和響應(yīng)的頭部信息景鼠,比如添加公共參數(shù)等
6仲翎,請(qǐng)求失敗自動(dòng)重連,發(fā)生異常時(shí)重連铛漓,看源碼調(diào)用recover方法重連了一次
7. 支持SPDY協(xié)議(SPDY是Google開(kāi)發(fā)的基于TCP的應(yīng)用層協(xié)議溯香,用以最小化網(wǎng)絡(luò)延遲,提升網(wǎng)絡(luò)速度浓恶,優(yōu)化用戶(hù)的網(wǎng)絡(luò)使用體驗(yàn)玫坛。SPDY并不是一種用于替代HTTP的協(xié)議,而是對(duì)HTTP協(xié)議的增強(qiáng)包晰。新協(xié)議的功能包括數(shù)據(jù)流的多路復(fù)用湿镀、請(qǐng)求優(yōu)先級(jí)以及HTTP報(bào)頭壓縮。谷歌表示伐憾,引入SPDY協(xié)議后勉痴,在實(shí)驗(yàn)室測(cè)試中頁(yè)面加載速度比原先快64%)
8.使用Okio來(lái)簡(jiǎn)化數(shù)據(jù)的訪問(wèn)與存儲(chǔ),提高性能
缺點(diǎn):
1.消息回來(lái)需要切到主線程树肃,主線程要自己去寫(xiě)蒸矛。
2.調(diào)用比較復(fù)雜,需要自己進(jìn)行封裝胸嘴。
3.緩存失效:網(wǎng)絡(luò)請(qǐng)求時(shí)一般都會(huì)獲取手機(jī)的一些硬件或網(wǎng)絡(luò)信息雏掠,比如使用的網(wǎng)絡(luò)環(huán)境。同時(shí)為了信息傳輸?shù)陌踩粤酉瘢赡苓€會(huì)對(duì)請(qǐng)求進(jìn)行加密乡话。在這些情況下OkHttp的緩存系統(tǒng)就會(huì)失效了,導(dǎo)致用戶(hù)在無(wú)網(wǎng)絡(luò)情況下不能訪問(wèn)緩存
框架中用到了那些設(shè)計(jì)模式:
1.最明顯的Builder設(shè)計(jì)模式驾讲,如構(gòu)建對(duì)象OkHttpClient蚊伞,還有單利模式
2.工廠方法模式,如源碼中的接口Call
3.觀察者模式如EventListener吮铭,監(jiān)聽(tīng)請(qǐng)求和響應(yīng)
4.策略模式
5.責(zé)任鏈模式时迫,如攔截器
OK的網(wǎng)絡(luò)請(qǐng)求緩存如何處理?
網(wǎng)絡(luò)緩存優(yōu)先考慮強(qiáng)制緩存谓晌,再考慮對(duì)比緩存
首先判斷強(qiáng)制緩存中的數(shù)據(jù)是否在有效期內(nèi)掠拳,如果在,就直接用緩存纸肉,如過(guò)了有效期溺欧,則進(jìn)入對(duì)比緩存
在對(duì)比緩存過(guò)程中喊熟,判斷ETag是否有變動(dòng),如果服務(wù)端返回沒(méi)有變動(dòng)姐刁,說(shuō)明資源未改變芥牌,使用緩存。如果有變動(dòng)聂使,判斷Last-Modified壁拉。
判斷Last-Modified,如果服務(wù)端對(duì)比資源的上次修改時(shí)間沒(méi)有變化柏靶,則用緩存弃理,否則重新請(qǐng)求服務(wù)端的數(shù)據(jù),并作緩存工作屎蜓。
Okhttp源碼解析
Okhttp構(gòu)造的時(shí)候會(huì)new 一個(gè)dipatcher對(duì)象
同步請(qǐng)求:dispatcher(分發(fā)器--保存同步請(qǐng)求痘昌,移除異步請(qǐng)求),execute
異步請(qǐng)求:dispatcher(一個(gè)等待請(qǐng)求隊(duì)列炬转,一個(gè)正在請(qǐng)求隊(duì)列辆苔,一個(gè)線程池),enqueue
判斷當(dāng)前call返吻,封裝一個(gè)AsyncCall對(duì)象姑子,通過(guò)client.dispatcher().enqueue()請(qǐng)求
通過(guò) getResponseWithInterceptorChain()
dispatcher的作用為維護(hù)請(qǐng)求的狀態(tài)乎婿,并維護(hù)一個(gè)線程池测僵,用于執(zhí)行請(qǐng)求。
ok相對(duì)其他的來(lái)說(shuō)高效的原因:內(nèi)部有個(gè)異步的線程池用來(lái)維護(hù)
同步請(qǐng)求直接一個(gè)隊(duì)列谢翎?
直接runningSyncCalls.add(call)捍靠;不再判斷,直接添加森逮,因?yàn)橥秸?qǐng)求不需要像異步還要判斷
異步請(qǐng)求為什么需要2個(gè)隊(duì)列榨婆?
類(lèi)似生產(chǎn)者消費(fèi)者模型,生產(chǎn)者只生產(chǎn)褒侧,消費(fèi)者只消費(fèi)良风,因?yàn)楫惒接凶畲筮\(yùn)行數(shù)64限制,所以超過(guò)數(shù)量后需要有緩存?zhèn)}庫(kù)闷供;
- Dispatcher 生產(chǎn)者烟央,
- ExecutorService 消費(fèi)者池,
- Deque<readyAsyncCalls>緩存歪脏,
-
Deque<runningAsyncCalls>正在運(yùn)行的任務(wù)
開(kāi)啟一個(gè)線程池疑俭,然后
不管同步請(qǐng)求還是異步請(qǐng)求都是通過(guò)攔截器鏈發(fā)起請(qǐng)求
然后通過(guò)HttpEngine發(fā)起請(qǐng)求并讀取結(jié)果,有緩存就進(jìn)緩存文件系統(tǒng)婿失,無(wú)緩存就走網(wǎng)絡(luò)請(qǐng)求钞艇,然后讀取Response最后獲取結(jié)果
1啄寡,retrofit源碼解析
Retrofit底層是基于Okhttp實(shí)現(xiàn)的,使用運(yùn)行時(shí)注解的方式
1哩照、 原理
通過(guò)java接口以及注解來(lái)描述網(wǎng)絡(luò)請(qǐng)求挺物,并用動(dòng)態(tài)代理的方式生成網(wǎng)絡(luò)請(qǐng)求的request,然后通過(guò)client調(diào)用相應(yīng)的網(wǎng)絡(luò)框架(默認(rèn)okhttp)去發(fā)起網(wǎng)絡(luò)請(qǐng)求飘弧,并將返回的response通過(guò)converterFactorty轉(zhuǎn)換成相應(yīng)的數(shù)據(jù)model姻乓,最后通過(guò)call adapter轉(zhuǎn)換成其他數(shù)據(jù)方式(如rxjava Observable)
2、 Retrofit流程
(1)通過(guò)解析 網(wǎng)絡(luò)請(qǐng)求接口的注解 配置 網(wǎng)絡(luò)請(qǐng)求參數(shù)
(2)通過(guò) 動(dòng)態(tài)代理 生成 網(wǎng)絡(luò)請(qǐng)求對(duì)象
(3)通過(guò) 網(wǎng)絡(luò)請(qǐng)求適配器 將 網(wǎng)絡(luò)請(qǐng)求對(duì)象 進(jìn)行平臺(tái)適配
(4)通過(guò) 網(wǎng)絡(luò)請(qǐng)求執(zhí)行器 發(fā)送網(wǎng)絡(luò)請(qǐng)求
(5)通過(guò) 數(shù)據(jù)轉(zhuǎn)換器 解析服務(wù)器返回的數(shù)據(jù)
(6)通過(guò) 回調(diào)執(zhí)行器 切換線程(子線程 ->>主線程)
(7)用戶(hù)在主線程處理返回結(jié)果
3眯牧、 Retrofit優(yōu)點(diǎn)
1.可以配置不同HTTP client來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求蹋岩,如okhttp、httpclient等学少;
2.請(qǐng)求的方法參數(shù)注解都可以定制剪个;
3.支持同步、異步和RxJava版确;
4.超級(jí)解耦扣囊;
5.可以配置不同的反序列化工具來(lái)解析數(shù)據(jù),如json绒疗、xml等
6.框架使用了很多設(shè)計(jì)模式
如何使用:
- 在retrofit中通過(guò)一個(gè)接口作為http請(qǐng)求的api接口
- 創(chuàng)建一個(gè)Retrofit實(shí)例
- 調(diào)用api接口
Retrofit動(dòng)態(tài)代理:
- 首先侵歇,通過(guò)method把它轉(zhuǎn)換成ServiceMethod
- 然后,通過(guò)serviceMethod吓蘑,args獲取到okHttpCall對(duì)象
- 最后惕虑,再吧okHttpCall進(jìn)一步封裝并返回Call對(duì)象
源碼:
- 創(chuàng)建一個(gè)retrofit對(duì)象
- 通過(guò)Retrofit.create()方法把定義的接口轉(zhuǎn)換成接口實(shí)例,并使用接口中的方法
- 最終的網(wǎng)絡(luò)請(qǐng)求調(diào)用okhttp
2磨镶,volley源碼解析
eg:https://a.codekk.com/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90
通過(guò)new RequestQueue() 函數(shù)新建并啟動(dòng)一個(gè)請(qǐng)求隊(duì)列RequestQueue后溃蔫,只需往這個(gè)RequestQueue不斷add Request即可。
2個(gè)請(qǐng)求隊(duì)列 CacheDispatcher琳猫,NetworkDispatcher
先判斷緩存伟叛,如果有且沒(méi)過(guò)期,就從緩存拿數(shù)據(jù)
沒(méi)有緩存脐嫂,就從NetworkDispatcher中獲取并保持在hashmap
默認(rèn)情況下统刮,volley中開(kāi)啟四個(gè)網(wǎng)絡(luò)調(diào)度線程和一個(gè)緩存調(diào)度線程,首先請(qǐng)求會(huì)加入緩存隊(duì)列账千,緩存調(diào)度線程從緩存隊(duì)列中取出線程侥蒙,如果找到該請(qǐng)求的緩存就
直接讀取該緩存并解析,然后回調(diào)給主線程蕊爵,如果沒(méi)有找到緩存的響應(yīng)辉哥,則將這個(gè)請(qǐng)求加入網(wǎng)絡(luò)隊(duì)列,然后網(wǎng)絡(luò)調(diào)度線程會(huì)輪詢(xún)?nèi)〕鼍W(wǎng)絡(luò)隊(duì)列中的請(qǐng)求,發(fā)起http請(qǐng)求醋旦,解析響應(yīng)并將響應(yīng)存入緩存恒水,回調(diào)給主線程。
volley基于請(qǐng)求隊(duì)列饲齐,volley的網(wǎng)絡(luò)請(qǐng)求線程池默認(rèn)大小為4钉凌,意味著只能并發(fā)進(jìn)行4個(gè)請(qǐng)求,大于4個(gè)會(huì)排在隊(duì)列中捂人,并發(fā)量太小御雕,所以適合請(qǐng)求高頻率
volley下載文件會(huì)將流入內(nèi)存中(是一個(gè)小于4k的緩存池),大文件會(huì)導(dǎo)致內(nèi)存溢出滥搭,所以不能下載大文件不能上傳大文件酸纲,比如上傳了四個(gè)大文件,同時(shí)占用了volley的四個(gè)線程導(dǎo)致其他網(wǎng)絡(luò)請(qǐng)求都阻塞在隊(duì)列中瑟匆,造成反應(yīng)慢的現(xiàn)象闽坡。
3,Butterknife原理
依托Java的注解機(jī)制來(lái)實(shí)現(xiàn)輔助代碼生成的框架愁溜。沒(méi)有采用反射機(jī)制疾嗅,用的是編譯時(shí)再生成代碼
- 開(kāi)始 會(huì)掃描Java代碼中所有的ButterKnife注解
- ButterKnifeProcessor 生成-><className>$$ViewBinder
- 調(diào)用bind方法加載深沉的ViewBinder類(lèi)
注意:注解的方法必須是public,protected冕象,不能是private代承,會(huì)影響性能,而且private是反射注入的
4渐扮,Glide源碼解析
1)Glide.with(context)創(chuàng)建了一個(gè)RequestManager论悴,同時(shí)實(shí)現(xiàn)加載圖片與組件生命周期綁定:在Activity上創(chuàng)建一個(gè)透明的ReuqestManagerFragment加到FragmentManager中,通過(guò)添加的Fragment感知Activty\Fragment的生命周期席爽。因?yàn)樘砑拥紸ctivity中的Fragment會(huì)跟隨Activity的生命周期意荤。在RequestManagerFragment中的相應(yīng)生命周期方法中通過(guò)liftcycle傳遞給在lifecycle中注冊(cè)的LifecycleListener
2)RequestManager.load(url) 創(chuàng)建了一個(gè)RequestBuilder<T>對(duì)象 T可以是Drawable對(duì)象或是ResourceType等
- RequestBuilder.into(view)
-->into(glideContext.buildImageViewTarget(view, transcodeClass))返回的是一個(gè)DrawableImageViewTarget, Target用來(lái)最終展示圖片的啊片,buildImageViewTarget-->ImageViewTargetFactory.buildTarget()根據(jù)傳入class參數(shù)不同構(gòu)建不同的Target對(duì)象只锻,這個(gè)Class是根據(jù)構(gòu)建Glide時(shí)是否調(diào)用了asBitmap()方法,如果調(diào)用了會(huì)構(gòu)建出BitmapImageViewTarget紫谷,否則構(gòu)建的是GlideDrawableImageViewTarget對(duì)象齐饮。
-->GenericRequestBuilder.into(Target),該方法進(jìn)行了構(gòu)建Request,并用RequestTracker.runRequest()
Request request = buildRequest(target);//構(gòu)建Request對(duì)象笤昨,Request是用來(lái)發(fā)出加載圖片的祖驱,它調(diào)用了buildRequestRecursive()方法以,內(nèi)部調(diào)用了GenericRequest.obtain()方法
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);//判斷Glide當(dāng)前是不是處于暫停狀態(tài)瞒窒,若不是則調(diào)用Request.begin()方法來(lái)執(zhí)行Request捺僻,否則將Request添加到待執(zhí)行隊(duì)列里,等暫停態(tài)解除了后再執(zhí)行
-->GenericRequest.begin()
1)onSizeReady()--> Engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this) --> a)先構(gòu)建EngineKey; b) loadFromCache從緩存中獲取EngineResource,如果緩存中獲取到cache就調(diào)用cb.onResourceReady(cached)匕坯; c)如果緩存中不存在調(diào)用loadFromActiveResources從active中獲取束昵,如果獲取到就調(diào)用cb.onResourceReady(cached);d)如果active中也不存在葛峻,調(diào)用EngineJob.start(EngineRunnable), 從而調(diào)用decodeFromSource()/decodeFromCache()-->如果是調(diào)用decodeFromSource()-->ImageVideoFetcher.loadData()-->HttpUrlFetcher()調(diào)用HttpUrlConnection進(jìn)行網(wǎng)絡(luò)請(qǐng)求資源-->得于InputStream()后,調(diào)用decodeFromSourceData()-->loadProvider.getSourceDecoder().decode()方法解碼-->GifBitmapWrapperResourceDecoder.decode()-->decodeStream()先從流中讀取2個(gè)字節(jié)判斷是GIF還是普通圖锹雏,若是GIF調(diào)用decodeGifWrapper()來(lái)解碼,若是普通靜圖則調(diào)用decodeBitmapWrapper()來(lái)解碼-->bitmapDecoder.decode()
6术奖、Glide使用什么緩存礁遵?
- 內(nèi)存緩存:LruResourceCache(memory)+弱引用activeResources
Map<Key, WeakReference<EngineResource<?>>> activeResources正在使用的資源,當(dāng)acquired變量大于0采记,說(shuō)明圖片正在使用佣耐,放到activeResources弱引用緩存中,經(jīng)過(guò)release()后唧龄,acquired=0,說(shuō)明圖片不再使用晰赞,會(huì)把它放進(jìn)LruResourceCache中
2)磁盤(pán)緩存:DiskLruCache,這里分為Source(原始圖片)和Result(轉(zhuǎn)換后的圖片)
第一次獲取圖片,肯定網(wǎng)絡(luò)取选侨,然后存active\disk中掖鱼,再把圖片顯示出來(lái),第二次讀取相同的圖片援制,并加載到相同大小的imageview中戏挡,會(huì)先從memory中取,沒(méi)有再去active中獲取晨仑。如果activity執(zhí)行到onStop時(shí)褐墅,圖片被回收,active中的資源會(huì)被保存到memory中洪己,active中的資源被回收妥凳。當(dāng)再次加載圖片時(shí),會(huì)從memory中取答捕,再放入active中逝钥,并將memory中對(duì)應(yīng)的資源回收。
之所以需要activeResources拱镐,它是一個(gè)隨時(shí)可能被回收的資源艘款,memory的強(qiáng)引用頻繁讀寫(xiě)可能造成內(nèi)存激增頻繁GC,而造成內(nèi)存抖動(dòng)沃琅。資源在使用過(guò)程中保存在activeResources中哗咆,而activeResources是弱引用,隨時(shí)被系統(tǒng)回收益眉,不會(huì)造成內(nèi)存過(guò)多使用和泄漏晌柬。
7姥份、Glide內(nèi)存緩存如何控制大小年碘?
Glide內(nèi)存緩存最大空間(maxSize)=每個(gè)進(jìn)程可用最大內(nèi)存0.4(低配手機(jī)是 每個(gè)進(jìn)程可用最大內(nèi)存0.33)
磁盤(pán)緩存大小是250MB int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
5殿衰,leakcanary、blockcanary
6盛泡,eventbus
Android事件發(fā)布訂閱框架
事件傳遞既可以用于Android四大組件間的通訊
EventBus
1闷祥,定義事件Event
2,準(zhǔn)備訂閱者
3傲诵,訂閱者同時(shí)需要在總線上注冊(cè)和注銷(xiāo)自己
4凯砍,發(fā)送事件
通過(guò)構(gòu)造者模式構(gòu)建EventBusBuilder,ThreadMode有四個(gè)線程拴竹,POSTING悟衩,MAIN,BACKGROUND栓拜,ASYNC座泳,
7,dagger2
8幕与,rxjava
Rxjava是基于響應(yīng)式編程挑势,基于事件流,實(shí)現(xiàn)異步操作的庫(kù)啦鸣。
RxJava原理是基于一種擴(kuò)展的觀察者模式潮饱,有四種角色:被觀察者Observable 觀察者Observer 訂閱subscribe 事件Event。RxJava原理可總結(jié)為:被觀察者Observable通過(guò)訂閱(subscribe)按順序發(fā)送事件(Emitter)給觀察者(Observer)诫给, 觀察者按順序接收事件&作出相應(yīng)的響應(yīng)動(dòng)作香拉。