android原始碼分析之View的事件分發(上)
1、View的繼承關係圖
View的繼承關係圖如下:
其中最重要的子類為ViewGroup,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,同時它也是繼承於View類。而UI元件的繼承關係如上圖,比較常用的元件類用紅色字型標出。
2、事件
2.1 事件型別
當用戶觸控式螢幕幕,根據不同的動作會產生不同的按鍵事件,如OnClick, OnLongClick, OnTouchEvent等。每個View會重寫相應的回撥方法,而具體的回撥方法則是在View類中進行了定義,如OnLongClick見View的原始碼如下:
public interface OnLongClickListener {
/**
* Called when a view has been clicked and held.
*
* @param v The view that was clicked and held.
*
* @return true if the callback consumed the long click,
* false otherwise.
*/
boolean onLongClick(View v);
}
而具體的事件型別主要有如下三類:
MotionEvent.ACTION_DOWN //按下View,是所有事件的開始
MotionEvent.ACTION_MOVE //滑動事件
MotionEvent.ACTION_UP //與down對應,表示擡起
2.2 事件響應機制
1>註冊一個監聽物件。
2>實現監聽物件的監聽事件,即事件分發時的回撥方法。
3>當某一觸發事件到來,在觸發事件中通過註冊過的監聽物件,回撥註冊物件的響應事件,來完成使用者自定義實現。
注:具體的實現過程見第三節。
3、View的事件分發
3.1 ViewRootImpl的建立過程分析
這裡介紹一個重要的類:ViewRootImpl類
簡單來說,ViewRootImpl相當於是視窗系統中的MVC模型中的Controller,它的主要職責為:
1. 負責為應用程式視窗檢視建立Surface。
2. 配合WindowManagerService來管理系統的應用程式視窗。
3. 負責管理、佈局和渲染應用程式視窗檢視的UI。
ViewRootImpl有兩個建立時機:
1>Activity元件在啟動的時候,系統會為它建立視窗物件(Window),同時,系統也會為這個視窗物件建立ViewRootImpl物件。
2>當Activity元件被啟用的時候,系統如果發現與它的應用程式視窗檢視物件所關聯的ViewRoot物件還沒有建立,那麼就會先建立這個ViewRoot物件,以便接下來可以將它的UI渲染出來。
ViewRootImpl的建立時序圖如下:
此處不做詳細分析,由時序圖即可看出大概流程。
3.2 View事件處理物件ViewPostImeInputStage的註冊過程分析
此處先分析ViewRootImpl類的setView方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
res = mWindowSession.addToDisplay(mWindow, mSeq,
mWindowAttributes,getHostVisibility(),
mDisplay.getDisplayId(),
mAttachInfo.mContentInsets,
mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage =
new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage =
new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage =
new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage =
new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage =
new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage =
new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
...
由以上程式碼可知,setView方法中會建立輸入管道,此處採用責任鏈模式,即觸控事件最終還是要分發給具體的View來處理的,所以最後對事件的處理會由此責任鏈來負責,而此責任鏈在此預先註冊了的InputStage主要有SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage、EarlyPostImeInputStage、ImeInputStage、ViewPreImeInputStage、NativePreImeInputStage等。此處只分析ViewPostImeInputStage,至於其他的自行分析。
本文假設讀者已經預先了解了android的輸入子系統,所以本文不解釋輸入子系統的工作原理。
在Input子系統的native層socket客戶端讀取輸入事件,最終呼叫InputEventReceiver類子類的onInputEvent()方法,ViewRootImpl的setView方法中初始化了WindowInputEventReceiver物件,它繼承自類InputEventReceiver。
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
其中mInputChannel與Input輸入子系統建立了聯絡,所以最終會呼叫WindowInputEventReceiver的onInputEvent()方法。
3.3 View的事件分發流程分析
在3.2節中分析了處理ViewPostImeInputStage的註冊,以及ViewRootImpl中響應輸入事件的入口,本小節將繼續分析事件的分發流程:
此時序圖分析了View事件中的OnClick事件的分發過程,由圖可知,ViewRootImpl擷取到事件後會分發給ViewPostImeInputStage處理,而ViewPostImeInputStage接著將事件分發給具體的View,此時根據View裡面註冊的監聽事件,呼叫其回撥函式,最終響應我們自定義的操作。至此,View的事件分發分析結束。