Handler
handler在android應(yīng)用
1.在卡頓監(jiān)測會用到消息機制脉课;主要是發(fā)送一個延時消息來監(jiān)測是否,在執(zhí)行時間內(nèi)沒有remove該消息就代碼APP發(fā)生卡頓财异;
2.ANR監(jiān)測也是通過發(fā)送一個延時消息來監(jiān)測是否發(fā)生ANR倘零;ANR是APP卡頓的極端情況;
3.View監(jiān)測事件是否長按也用到消息機制戳寸,在發(fā)生Down的時候會發(fā)送一個延時消息呈驶,在Up的時候會將該消息Remove掉,如果指定的時間沒有發(fā)生UP就會觸發(fā)長按事件疫鹊;
4.Activity生命周期的控制也是在ActivityThread發(fā)送不同的消息來切換Activity生命周期袖瞻;
5.消息機制可以將一個任務(wù)切換到其它指定的線程,如AsyncTask拆吆;
6.Android動畫每一次刷新也需要Handler聋迎;
Android是靠消息驅(qū)動的,沒有消息機制手機根本無法工作枣耀,以上這些場景都用到Android消息機制霉晕,還有很多其他未知的場景可能也會用到Android消息機制,所以消息機制在Android中具有很重要的地位捞奕;
handler原理
Looper:所有的人機交互代碼牺堰,都得有一個死循環(huán)去持續(xù)處理人機交互代碼÷В總不能一個main跑到底就結(jié)束了伟葫。所以Looper就出現(xiàn)了。Looper存在第一理由就是提供死循環(huán)院促。Looper.loop()后會讓當(dāng)前線程變成looper線程筏养。那APP主線程總不能死循環(huán)了什么都不做吧胯盯?所以兼丰,Looper存在的第二個理由就是有M(Message)則處理,無則阻塞蜈漓。(pipe/epoll機制墩邀,IO多路復(fù)用掌猛,阻塞在IO的讀端,不耗費CPU資源。)
MessageQueue:它存在的目的很簡單荔茬。提供消息接收的并發(fā)功能废膘。一般被Looper持有引用。
Handler:google工程師是為什么設(shè)計它的呢慕蔚?首先得說一下原理丐黄,A線程怎么跟UI(looper)線程通信呢?我們知道linux進(jìn)程內(nèi)資源是共享的孔飒,所以線程之間資源也是共享的灌闺。所以A線程只要拿到主線程的MQ實例,就能跟主線程通信了坏瞄。所以Handler存在的第一個目的就是同一進(jìn)程的線程間通信桂对。第二個功能是消息回調(diào);Handler對象既自己發(fā)消息鸠匀,又自己處理消息蕉斜。 AB線程只要拿到handler引用,A就可以發(fā)消息缀棍,B就可以處理消息宅此。Handler在哪里創(chuàng)建,就獲得對應(yīng)線程的Loooper爬范,這是前提父腕。
Message:消息載體。
主線程Loop是死循環(huán)坦敌,為什么還能跑生命周期
死循環(huán)Looper.loop()前侣诵,一定要做點什么痢法,留個入口狱窘,這樣別人才能有途徑通知我做事。就是那一句thread.attch(false)财搁;這句話會創(chuàng)建一個binder線程(ApplicationThread)蘸炸,該線程不做什么,就是system_server中AMS的bp端尖奔。通知方式是什么搭儒?就是本題主題Handler。Binder線程內(nèi)外都持有該mH的引用提茁。
Looper 死循環(huán)為什么不會導(dǎo)致應(yīng)用卡死淹禾,會消耗大量資源嗎?
looper.loop本身不會導(dǎo)致應(yīng)用卡死茴扁。主線程的死循環(huán)一直運行是不是特別消耗CPU資源呢铃岔? 其實不然,這里就涉及到Linux pipe/epoll機制峭火,簡單說就是在主線程的MessageQueue沒有消息時毁习,便阻塞在loop的queue.next()中的nativePollOnce()方法里智嚷,此時主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到下個消息到達(dá)或者有事務(wù)發(fā)生纺且,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作盏道。這里采用的epoll機制,是一種IO多路復(fù)用機制载碌,可以同時監(jiān)控多個描述符猜嘱,當(dāng)某個描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮骷尥В举|(zhì)同步I/O泉坐,即讀寫是阻塞的。 所以說裳仆,主線程大多數(shù)時候都是處于休眠狀態(tài)腕让,并不會消耗大量CPU資源。
Activity生命周期歧斟,比如暫停Activity纯丸,流程如下:
1.線程1的AMS中調(diào)用線程2的ATP;(由于同一個進(jìn)程的線程間資源共享静袖,可以相互直接調(diào)用觉鼻,但需要注意多線程并發(fā)問題)
2.線程2通過binder傳輸?shù)紸pp進(jìn)程的線程4;
3.線程4通過handler消息機制队橙,將暫停Activity的消息發(fā)送給主線程坠陈;
4.主線程在looper.loop()中循環(huán)遍歷消息,當(dāng)收到暫停Activity的消息時捐康,便將消息分發(fā)給?ActivityThread.H.handleMessage()方法仇矾,再經(jīng)過方法的調(diào)用,
5.最后便會調(diào)用到Activity.onPause()解总,當(dāng)onPause()處理完后贮匕,繼續(xù)循環(huán)loop下去。
Handler 是如何能夠線程切換
?Handler創(chuàng)建的時候會采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng)花枫,Looper在哪個線程創(chuàng)建刻盐,就跟哪個線程綁定,并且Handler是在他關(guān)聯(lián)的Looper對應(yīng)的線程中處理消息的劳翰。那么Handler內(nèi)部如何獲取到當(dāng)前線程的Looper呢—–ThreadLocal敦锌。ThreadLocal可以在不同的線程中互不干擾的存儲并提供數(shù)據(jù),通過ThreadLocal可以輕松獲取每個線程的Looper佳簸。
能不能讓一個Message加急被處理乙墙?/ 什么是Handler同步屏障?
可以 / 一種使得異步消息可以被更快處理的機制。
如果向主線程發(fā)送了一個UI更新的操作Message伶丐,而此時消息隊列中的消息非常多悼做,那么這個Message的處理就會變得緩慢,造成界面卡頓哗魂。所以通過同步屏障肛走,可以使得UI繪制的Message更快被執(zhí)行。
什么是同步屏障录别?這個“屏障”其實是一個Message朽色,插入在MessageQueue的鏈表頭,且其target==null组题。Message入隊的時候不是判斷了target不能為null嗎葫男?不不不,添加同步屏障是另一個方法:
HandlerThread
HandlerThread繼承Thread崔列,它是一種可以使用Handler的Thread梢褐,它的實現(xiàn)也很簡單,在run方法中也是通過Looper.prepare()來創(chuàng)建消息隊列赵讯,并通過Looper.loop()來開啟消息循環(huán)(與我們手動創(chuàng)建方法基本一致)盈咳,這樣在實際的使用中就允許在HandlerThread中創(chuàng)建Handler了。
由于HandlerThread的run方法是一個無限循環(huán)边翼,因此當(dāng)不需要使用的時候通過quit或者quitSafely方法來終止線程的執(zhí)行鱼响。
Handler.postDelayed的原理
message會按最終執(zhí)行的時間順序插入到messagequeue的指定位置。如果延遲消息消費到隊頭组底,looper發(fā)現(xiàn)消息延遲時間未到會繼續(xù)休眠等待執(zhí)行丈积。
Activity.runOnUIThread、Handler债鸡、View.post
最終底層都是用的handler機制
runOnUiThread:如果在主線程調(diào)用江滨,直接執(zhí)行。如果在子線程調(diào)用就用主線程handler往主線程消息隊列里發(fā)送消息娘锁。
Handler:
View.post:需要在view attach 到 Window 后牙寞,通過ViewRootImpl的ViewRootHandler執(zhí)行請求。如果view沒有attach到window則不會執(zhí)行莫秆。
Activity啟動模式
默認(rèn)啟動模式standard
棧頂復(fù)用模式singleTop
棧內(nèi)復(fù)用模式singleTask
全局唯一模式singleInstance
https://blog.csdn.net/zy_jibai/article/details/80587083
Android的進(jìn)程管理
前臺進(jìn)程:正在屏幕上顯示的進(jìn)程和一些系統(tǒng)進(jìn)程
可見進(jìn)程:不在前臺,但用戶依然可見的進(jìn)程悔详,舉個例來說:widget镊屎、輸入法等
服務(wù)進(jìn)程:通過 startService() 方法啟動的進(jìn)程,但不屬于前臺進(jìn)程和可見進(jìn)程茄螃。例如缝驳,在后臺播放音樂或者在后臺下載就是服務(wù)進(jìn)程。
后臺進(jìn)程:目前對用戶不可見的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)。入A應(yīng)用目前為前臺進(jìn)程,按下Home鍵回到桌面用狱,A應(yīng)用就變?yōu)榱撕笈_進(jìn)程
空進(jìn)程:沒有任何東西在內(nèi)運行的進(jìn)程运怖。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短該應(yīng)用下次在其中運行組件所需的啟動時間夏伊。
https://zhuanlan.zhihu.com/p/145245419
冷啟動熱啟動
冷啟動熱啟動區(qū)別
啟動方式分為兩種:冷啟動和熱啟動摇展。
冷啟動:當(dāng)啟動應(yīng)用時,后臺沒有該應(yīng)用的進(jìn)程溺忧,這時系統(tǒng)會重新創(chuàng)建一個新的進(jìn)程分配給該應(yīng)用咏连,這個啟動方式就是冷啟動。:冷啟動因為系統(tǒng)會重新創(chuàng)建一個新的進(jìn)程分配給它鲁森,所以會先創(chuàng)建和初始化Application類祟滴,再創(chuàng)建和初始化MainActivity類(包括一系列的測量、布局歌溉、繪制)垄懂,最后顯示在界面上。
熱啟動:當(dāng)啟動應(yīng)用時痛垛,后臺已有該應(yīng)用的進(jìn)程(例:按back鍵埠偿、home鍵,應(yīng)用雖然會退出榜晦,但是該應(yīng)用的進(jìn)程是依然會保留在后臺冠蒋,可進(jìn)入任務(wù)列表查看),所以在已有進(jìn)程的情況下乾胶,這種啟動會從已有的進(jìn)程中來啟動應(yīng)用抖剿,這個方式叫熱啟動。熱啟動因為會從已有的進(jìn)程中來啟動识窿,所以熱啟動就不會走Application這步了斩郎,而是直接走M(jìn)ainActivity(包括一系列的測量、布局喻频、繪制)缩宜,所以熱啟動的過程只需要創(chuàng)建和初始化一個MainActivity就行了,而不必創(chuàng)建和初始化Application甥温,
冷啟動過程
Application的構(gòu)造器方法——>attachBaseContext()——>onCreate()——>Activity的構(gòu)造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量布局繪制顯示在界面上锻煌。
1、點擊桌面圖標(biāo)姻蚓,Launcher會啟動程序默認(rèn)的Acticity宋梧,之后再按照程序的邏輯啟動各種Activity
2、啟動Activity都需要借助應(yīng)用程序框架層的ActivityManagerService服務(wù)進(jìn)程(Service也是由ActivityManagerService進(jìn)程來啟動的)狰挡;在Android應(yīng)用程序框架層中捂龄,ActivityManagerService是一個非常重要的接口释涛,它不但負(fù)責(zé)啟動Activity和Service,還負(fù)責(zé)管理Activity和Service倦沧。
? ?Step 1. 無論是通過Launcher來啟動Activity唇撬,還是通過Activity內(nèi)部調(diào)用startActivity接口來啟動新的Activity,都通過Binder進(jìn)程間通信進(jìn)入? ? ? ?????????????????????????到ActivityManagerService進(jìn)程中展融,并且調(diào)用ActivityManagerService.startActivity接口窖认;
Step 2. ActivityManagerService調(diào)用ActivityStack.startActivityMayWait來做準(zhǔn)備要啟動的Activity的相關(guān)信息;
Step 3. ActivityStack通知ApplicationThread要進(jìn)行Activity啟動調(diào)度了愈污,這里的ApplicationThread代表的是調(diào)用? ? ? ? ? ? ? ?????????????????????????????ActivityManagerService.startActivity接口的進(jìn)程耀态,對于通過點擊應(yīng)用程序圖標(biāo)的情景來說,這個進(jìn)程就是Launcher了暂雹,
? ? ? ? ? ? ? ?Step 4. ApplicationThread不執(zhí)行真正的啟動操作首装,它通過調(diào)用ActivityManagerService.activityPaused接口進(jìn)入到ActivityManagerService? ? ? ?????????????????????????進(jìn)程中,看看是否需要創(chuàng)建新的進(jìn)程來啟動Activity杭跪;
Step 5. 對于通過點擊應(yīng)用程序圖標(biāo)來啟動Activity的情景來說仙逻,ActivityManagerService在這一步中,會調(diào)用startProcessLocked來創(chuàng)建一個 ????????????????????????????新的進(jìn)程涧尿,而對于通過在Activity內(nèi)部調(diào)用startActivity來啟動新的Activity來說系奉,這一步是不需要執(zhí)行的,
Step 6. ActivityManagerServic調(diào)用ApplicationThread.scheduleLaunchActivity接口姑廉,通知相應(yīng)的進(jìn)程執(zhí)行啟動Activity的操作缺亮;
Step 7. ApplicationThread把這個啟動Activity的操作轉(zhuǎn)發(fā)給ActivityThread,ActivityThread通過ClassLoader導(dǎo)入相應(yīng)的Activity類桥言,然后把? ? ? ?????????????????????????它啟動起來萌踱。
冷啟動優(yōu)化
白屏問題:設(shè)置透明背景
冷啟動時間優(yōu)化:減少在Application和第一個Activity的onCreate()方法的工作量;不要讓Application參與業(yè)務(wù)的操作号阿;不要在Application進(jìn)行耗時操作并鸵;不要以靜態(tài)變量的方式在Application中保存數(shù)據(jù);減少布局的復(fù)雜性和深度扔涧;
View的繪制流程
最終performTraversals()方法觸發(fā)了View的繪制园担。該方法內(nèi)部,依次調(diào)用了performMeasure(),performLayout(),performDraw(),將View的measure枯夜,layout弯汰,draw過程,從頂層View分發(fā)了下去卤档。
Dialog蝙泼,PopupWindow中View的繪制過程也是一樣的,只是觸發(fā)的方式不同劝枣。例如Dialog中汤踏,是調(diào)用dialog.show()時,觸發(fā)了WindowManagerImpl的addView(),后面的流程就一樣了舔腾。
自定義View的幾種方式
1.繼承自View溪胶,重寫onDraw方法
場景:顯示的內(nèi)容需要高度定制,如圖表
2.繼承自ViewGroup
場景:流式布局等稳诚,對于內(nèi)容布局由特殊要求的哗脖。通過重寫onLayout方法達(dá)到目的。
3.繼承自特定的View(如TextView扳还,ImageView)
場景:需要擴展系統(tǒng)控件功能才避,如圓角ImageView
4.繼承自特定的ViewGroup(如LinearLayout)
Recyclerview
recyclerview緩存機制
關(guān)于RecyclerView的緩存分為四級,Scrap氨距、Cache桑逝、ViewCacheExtension和RecycledViewPool。Scrap是屏幕內(nèi)的緩存一般我們不怎么需要特別注意俏让;Cache可直接拿來復(fù)用的緩存楞遏,性能高效;ViewCacheExtension需要開發(fā)者自定義的緩存首昔,API設(shè)計比較奇怪寡喝,慎用;RecycledViewPool四級緩存勒奇,可以避免用戶調(diào)用onCreateViewHolder()方法预鬓,提高性能,在ViewPager+RecyclerView的應(yīng)用場景下可以大有作為赊颠。
recyclerview和listview比較
ListView有兩級緩存格二,分別是Active View和Scrap View,緩存的對象僅僅是ItemView巨税,viewholder每次還是需要重新綁定數(shù)據(jù)到ItemView蟋定;而RecyclerView有四級緩存,分別是Scrap草添、Cache驶兜、ViewCacheExtension和RecycledViewPool,緩存的對象是ViewHolder远寸。Scrap和Cache分別是通過position去找ViewHolder可以直接復(fù)用抄淑;ViewCacheExtension自定義緩存,目前來說應(yīng)用場景比較少卻需慎用驰后;RecycledViewPool通過type來獲取ViewHolder肆资,獲取的ViewHolder是個全新,需要重新綁定數(shù)據(jù)灶芝。
Window
windowmanagerservice 統(tǒng)一管理window郑原,activity中的phonewindow也是一種window唉韭,phonewindow的view就是decorview,decorview內(nèi)包括了contentview(activity里setContentView對應(yīng))。dialog犯犁,popupwindow属愤,toast等都是window。自定義懸浮窗也是window(type層級較高的顯示在最上面的window)酸役,如果需要在全系統(tǒng)上顯示6.0版本開始要打開權(quán)限住诸。
Window:屏幕上的某塊顯示區(qū)域,用來承載 View涣澡。
WindowManagerService(WMS):Android 框架層的一個服務(wù)進(jìn)程贱呐,用來管理 Window。
Surface:對應(yīng)一塊屏幕緩沖區(qū)入桂,每個 window 對應(yīng)一個 Surface奄薇。
Canvas:提供了一系列繪圖接口,用來在 Surface 上進(jìn)行繪制操作事格。
SurfaceFlinger:Android 的一個服務(wù)進(jìn)程惕艳,負(fù)責(zé)管理 Surface。
window的type屬性:決定了window的顯示次序驹愚。window是有分類的远搪,不同類別的顯示高度范圍不同,例如我把1-1000m高度稱為低空逢捺,1001-2000m高度稱為中空谁鳍,2000以上稱為高空。window也是一樣按照高度范圍進(jìn)行分類劫瞳,他也有一個變量Z-Order倘潜,決定了window的高度。window一共可分為三類:應(yīng)用級window志于、子window(dialog)涮因、系統(tǒng)級window(Toast)
Window的flags參數(shù):flag標(biāo)志控制window的東西比較多,很多資料的描述是“控制window的顯示”伺绽,但我覺得不夠準(zhǔn)確养泡。flag控制的范圍包括了:各種情景下的顯示邏輯(鎖屏,游戲等)還有觸控事件的處理邏輯奈应±窖冢控制顯示確實是他的很大部分功能,但是并不是全部杖挣。
window的solfInputMode屬性:這一部分就是當(dāng)軟件盤彈起來的時候肩榕,window的處理邏輯,這在日常中也經(jīng)常遇到惩妇,如:我們在微信聊天的時候株汉,點擊輸入框筐乳,當(dāng)軟鍵盤彈起來的時候輸入框也會被頂上去。如果你不想被頂上去郎逃,也可以設(shè)置為被軟鍵盤覆蓋哥童。
事件分發(fā)機制
主要分事件分發(fā)和事件處理挺份,
事件分發(fā):Activity(不處理)-> 根View -> 一層一層ViewGroup(如果有的話) -> 子View
事件處理 :Activity(不處理則丟棄) <- 根View <- 一層一層ViewGroup(如果有的話) <- 子View
具體在傳遞事件的時候褒翰,是由以下三個方法來控制的:
dispatchTouchEvent : 分發(fā)事件
onInterceptTouchEvent : 攔截事件
onTouchEvent : 消費事件
viewgroup才有onInterceptTouchEvent攔截事件,有view消費了action_down事件匀泊,后續(xù)的action_move和action_up事件都會傳給他优训。
Android虛擬機
Dalvik虛擬機、Art虛擬機
4.4版本前各聘,使用的是Dalvik虛擬機
5.0版本以后揣非,使用的是Art虛擬機
Dalvik虛擬機
1.Dalvik是基于寄存器的虛擬機,讀取和保存數(shù)據(jù)會比基于棧的JVM在運行時快很多
2.基于寄存器的虛擬機允許更快的執(zhí)行時間躲因,但代價是編譯后的程序更大
4.新的Dex字節(jié)碼格式:合并多個class字節(jié)碼文件早敬、減少常量池大小、減少文件的IO操作大脉,提高類的查找速度搞监、減少文件大小
4.dex的優(yōu)化格式odex:在App安裝的過程和DexClassloader加載過程中會對Dex文件進(jìn)行優(yōu)化成odex
Dalvik虛擬機中使用JIT編譯
在運行時對dex的指令進(jìn)行intercept,解釋成機器碼
虛擬機根據(jù)函數(shù)調(diào)用的次數(shù)镰矿,來決定熱點代碼
以函數(shù)為維度將熱點代碼的機器碼進(jìn)行緩存琐驴,在下一次調(diào)用時直接調(diào)用該機器碼
JIT編譯的優(yōu)點與缺點
優(yōu)點:安裝速度超快、存儲空間小
缺點:Multidex加載的時候會非常慢秤标,因為在dex加載時會進(jìn)行dexopt绝淡。JIT中需要解釋器,解釋器解釋的字節(jié)碼會帶來CPU和時間的消耗苍姜。由于熱點代碼的Monitor一直在運行牢酵,也會帶來電量的損耗
5.0-7.0的Art虛擬機
在5.0-7.0之間,Android使用ART虛擬機衙猪,使用AOT編譯馍乙,運行的文件格式也從odex轉(zhuǎn)換成了oat格式。
在APK安裝的時候屈嗤,PackageManagerService會調(diào)用dex2oat通過靜態(tài)編譯的方式潘拨,來將所有的dex文件(包括Multidex)編譯oat文件。
在運行的時候饶号,就直接運行oat的代碼铁追。而其中的Dex文件的內(nèi)容也就是為了DexClassLoader在動態(tài)加載其他的Dex文件時,在鏈接的過程中可以找到對應(yīng)的meta-data茫船,正確的鏈接到引用的類文件與函數(shù)琅束。
AOT編譯得優(yōu)點與缺點
優(yōu)點:運行時會超級快扭屁、在運行時省電,也節(jié)省各種資源
缺點:在系統(tǒng)更新的時候涩禀,所有app都需要進(jìn)行dex2oat的操作料滥,耗費的時間太長。在app安裝的過程中艾船,所耗費的時間也越來越長葵腹,因為apk包越來越大。由于oat文件中包含dex文件與編譯后的Native Code屿岂,導(dǎo)致占用空間也越來越大践宴。
7.0至今的Art虛擬機
由于之前版本ART的缺點,7.0之后的采用了Hybrid Mode的ART虛擬機:解釋器爷怀、JIT阻肩、OAT,將這三種方案進(jìn)行混合編譯运授,來從運行時的性能烤惊、存儲、安裝吁朦、加載時間進(jìn)行平衡柒室。
在第一次啟動的時候,系統(tǒng)會以JIT的方式來運行App喇完,在系統(tǒng)空閑的時候會在后臺對App進(jìn)行AOT靜態(tài)編譯伦泥,并且會根據(jù)JIT運行時所收集的運行時函數(shù)調(diào)用的信息生成的Profile文件來進(jìn)行參考 。
? glide的緩存機制
從glide緩存的lruCache出發(fā)讓我講一下自己怎么實現(xiàn)锦溪,一開始只說到了鏈表不脯,后來面試官提醒效率,于是回答到了linkedHashmap
glide緩存的弱引用說到安卓四大引用還有在項目中的使用
Bitmap圖片處理刻诊,下采樣防楷、編碼、超大圖片保證原圖加載且如何防止OOM则涯?
安卓 ANR原因复局?如何判斷?
數(shù)據(jù)持久化&對比:SP粟判、SQLite亿昏、File
App上線后用戶使用時卡頓怎么查看是什么原因
看視頻的時候網(wǎng)絡(luò)請求很慢怎么優(yōu)化
SP的內(nèi)部實現(xiàn)
SP多進(jìn)程不安全要怎么解決?(這個當(dāng)時答的ContentProvider档礁,但是面試官不滿意角钩,后面引導(dǎo)我mmap,然鵝我只知道個大概,沒跟上思路递礼,后面查了下發(fā)現(xiàn)騰訊的 MMKV 框架茅塞頓開
Android相關(guān)優(yōu)化(如內(nèi)存優(yōu)化惨险、網(wǎng)絡(luò)優(yōu)化、布局優(yōu)化脊髓、電量優(yōu)化辫愉、業(yè)務(wù)優(yōu)化)
熱修復(fù)的原理