1. Android中的事件傳遞機制?
答:當我們的手指觸碰到屏幕,事件是按照Activity->ViewGroup->View這樣的流程到達最終響應觸摸事件的View的严望。而在事件分發(fā)過程中虏缸,涉及到三個最重要的方法:dispatchTouchEvent()、onInterceptTouchEvent()肠虽、onTouchEvent。我們的手指觸摸到屏幕的時候玛追,會觸發(fā)一個Action_Down類型的事件税课,當前頁面的Activity會首先做出相應,也就是說會走到Activity的dispatchTouchEvent()方法內痊剖。在這個方法內部有下面兩個邏輯:
調用getWindow.superDispatchTouchEvent()韩玩。
如果上一步返回true,則直接返回true陆馁;否則return自己的onTouchEvent()找颓。顯然,當getWindow.superDispatchTouchEvent()返回true叮贩,表示當前事件已經被消費掉击狮,無需調用onTouchEvent;否則代表事件并沒有被處理益老,因此需要調用Activity的onTouchEvent進行處理彪蓬。
我們都知道,getWindow()返回的是PhoneWindow捺萌,因此這句代碼本質上調用了PhoneWindow中的superDispatchTouchEvent()档冬。而后者實際上調用了mDecor.superDispatchTouchEvent(event)。這個mDecor也就是DecorView,它是FrameLayout的一個子類捣郊。在DecorView中的superDispatchTouchEvent(event)中調用的是super.dispatchTouchEvent()辽狈。因此,本質上調用的是ViewGroup的dispatchTouchEvent()呛牲。
到這里刮萌,事件已經從Activity傳遞到ViewGroup了。接下來我們分析ViewGroup娘扩。
在ViewGroup的dispatchTouchEvent()中邏輯大致如下:
通過onInterceptTouchEvent()判斷當前ViewGroup是否攔截着茸,默認的ViewGroup都是不攔截的;
如果攔截琐旁,則return自己的onTouchEvent()涮阔;
如果不攔截,則根據child.dispatchTouchEvent()的返回值判斷灰殴。如果返回true敬特,則return true;否則return自身的onTouchEvent()牺陶,在這里實現了未處理事件的向上傳遞伟阔。
通常情況下,ViewGroup 的 onInterceptTouchEvent() 都返回 false掰伸,表示不攔截皱炉。這里需要注意的是事件序列,比如Down事件狮鸭、Move事件…Up事件合搅,從 Down到 Up 是一個完整的事件序列,對應著手指從按下到抬起這一系列事件歧蕉,如果ViewGroup 攔截了 Down 事件灾部,那么后續(xù)事件都會交給這個 ViewGroup 的onTouchEvent。如果 ViewGroup 攔截的不是 Down 事件惯退,那么會給之前處理這個Down 事件的 View發(fā)送一個Action_Cancel 類型的事件梳猪,通知子View這個后續(xù)的事件序列已經被 ViewGroup 接管了,子 View 恢復之前的狀態(tài)即可蒸痹。
這里舉一個常見的例子:在一個 Recyclerview 中有很多的 Button,我們首先按下了一個 button呛哟,然后滑動一段距離再松開叠荠,這時候 Recyclerview 會跟著滑動,并不會觸發(fā)這個 button 的點擊事件扫责。這個例子中榛鼎,當我們按下 button 時,這個 button 接收到了 Action_Down 事件,正常情況下后續(xù)的事件序列應該由這個 button處理者娱。但我們滑動了一段距離抡笼,這時 Recyclerview 察覺到這是一個滑動操作,攔截了這個事件序列黄鳍,走了自身的 onTouchEvent()方法推姻,反映在屏幕上就是列表的滑動。而這時 button 仍然處于按下的狀態(tài)框沟,所以在攔截的時候需要發(fā)送一個 Action_Cancel 來通知 button 恢復之前狀態(tài)藏古。
事件分發(fā)最終會走到View的dispatchTouchEvent()中。在View的dispatchTouchEvent()中沒有onInterceptTouchEvent()忍燥,這里很容易理解拧晕,View沒有child,也就不存在攔截梅垄。View的dispatchTouchEvent()直接return了自己的onTouchEvent()厂捞。如果onTouchEvent()返回true代表事件被消費,否則未消費的事件會向上傳遞队丝,直到有View處理了事件或一直沒有消費靡馁,最終回到Activity的onTouchEvent()終止。
有時候會有人混淆onTouchEvent和onTouch炭玫。首先奈嘿,這兩個方法都在View的dispatchTouchEvent()中:
如果touchListener不為null,并且這個View是enable的吞加,而且onTouch返回true裙犹,都滿足時直接return true,走不到onTouchEvent()方法衔憨。
否則叶圃,就會觸發(fā)onTouchEvent()。因此onTouch優(yōu)先于onTouchEvent獲得事件處理權践图。
最后附上流程圖總結:
touch事件傳遞流程
參考:https://juejin.im/entry/58df5b33570c35005798493c
https://juejin.im/post/5b8f15e26fb9a01a031b12d9#heading-3
2. Handler的原理掺冠?
答:與Handler密切相關的還有Message、MessageQueue码党、Looper德崭。
Message。Message有兩個關鍵的成員變量:target揖盘、callback:
(1) target眉厨。就是發(fā)送消息的Handler
(2) callback。調用Handler.post(Runnable)時傳入的Runnable類型的任務兽狭。post事件的本質也是創(chuàng)建了一個Message憾股,將我們傳入的這個runnable賦值給創(chuàng)建的Message的callback這個成員變量鹿蜀。
MessageQueue。消息隊列用于存放消息服球,其中重點關注next()方法茴恰,它會返回下一個待處理的消息。
Looper斩熊。Looper消息輪詢器其實是連接Handler和消息隊列的核心往枣。想要在一個線程中創(chuàng)建一個Handler,首先要通過Looper.prepare()創(chuàng)建Looper座享,之后還得調用Looper.loop()開啟輪詢婉商。
(1) prepare()。這個方法做了兩件事:首先通過ThreadLocal.get()獲取當前線程中的Looper渣叛,如果不為空則拋出RuntimeException丈秩。否則創(chuàng)建Looper,并通過ThreadLocal.set(looper)將當前線程與剛剛創(chuàng)建的Looper綁定淳衙。值得注意的是蘑秽,上面的消息隊列的創(chuàng)建其實就是發(fā)生在Looper的構造函數中。
(2)loop()箫攀。這個方法開啟了整個事件機制的輪詢肠牲。其本質是開啟一個死循環(huán),不斷地通過MessageQueue的next()方法獲取消息msg靴跛。拿到消息后會調用msg.target.dispatchMessage()來做處理缀雳。綜上也就是調用handler.dispatchMessage()。
Handler梢睛。Handler重點在于發(fā)送消息和處理消息肥印。
(1)發(fā)送消息。其實發(fā)送消息除了 sendMessage 之外還有 sendMessageDelayed 和 post 以及 postDelayed 等等不同的方式绝葡。但它們的本質都是調用了 sendMessageAtTime深碱。在 sendMessageAtTime 這個方法中調用了 enqueueMessage。在 enqueueMessage 這個方法中做了兩件事:通過 msg.target = this 實現了消息與當前 handler 的綁定藏畅。然后通過 queue.enqueueMessage 實現了消息入隊敷硅。
(2)處理消息。 消息處理的核心其實就是dispatchMessage()這個方法愉阎。這個方法里面的邏輯很簡單绞蹦,先判斷 msg.callback 是否為 null,如果不為空則執(zhí)行這個 runnable榜旦。如果為空則會執(zhí)行我們的handleMessage方法坦辟。
3. ANR出現的情況有幾種? 怎么分析解決ANR問題章办?
答:ANR(Application Not responding)。Android中,主線程(UI線程)如果在規(guī)定時內沒有處理完相應工作藕届,就會出現ANR挪蹭。具體來說,ANR會在以下幾種情況中出現:
(1) 輸入事件(按鍵和觸摸事件)5s內沒被處理
(2) BroadcastReceiver的事件(onRecieve方法)在規(guī)定時間內沒處理完(前臺廣播為10s休偶,后臺廣播為60s)
(3) service 前臺20s后臺200s未完成啟動
(4) ContentProvider的publish在10s內沒進行完
分析ANR問題梁厉,需要結合Log以及trace文件。具體分析流程踏兜,可參照以下兩篇文章:
http://www.reibang.com/p/fa962a5fd939
https://blog.csdn.net/droyon/article/details/51099826
4. 內存泄露的場景有哪些词顾?內存泄漏分析工具使用方法?
答:常見的內存泄露有:
單例模式引起的內存泄露碱妆。
靜態(tài)變量導致的內存泄露肉盹。
非靜態(tài)內部類引起的內存泄露。
使用資源時疹尾,未及時關閉引起內存泄露上忍。
使用屬性動畫引起的內存泄露。
Webview導致的內存泄露纳本。
而對于內存泄露的檢測窍蓝,常用的工具有LeakCanary、MAT(Memory Analyer Tools)繁成、Android Studio自帶的Profiler吓笙。關于用法,網上教程很多巾腕,可自行查閱面睛,下面兩個經供參考:
三種用法、MAT
同時附上官方Android Profiler教程
5. 常用的設計模式有哪些祠墅?是否了解責任鏈模式侮穿?
答:單例模式,觀察者模式毁嗦,工廠模式亲茅,建造者模式,構造者模式狗准,中間者模式克锣,橋接模式,適配器模式等等腔长。