Android點擊事件分發(fā)機制源碼分析1——Activity
Android點擊事件分發(fā)機制源碼分析2——ViewGroup
Android點擊事件分發(fā)機制源碼分析3——View
一 產(chǎn)生點擊事件
當(dāng)用戶用點擊顯示屏產(chǎn)生一個點擊事件吟孙,本章討論點擊事件是如何傳到Activity上的,Android 源碼為sdk25妙蔗。
二 系統(tǒng)如何將點擊事件派發(fā)給Activity
1. handleLaunchActivity
啟動一個新的Activity的時候云稚,會調(diào)用ActivityThread的handleLaunchActivity方法蔬捷,我們從這里開始看:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
unscheduleGcIdler();
mSomeActivitiesChanged = true;
//最終回調(diào)目標(biāo)Activity的onConfigurationChanged()
handleConfigurationChanged(null, null);
//初始化wms
WindowManagerGlobal.initialize();
//最終回調(diào)目標(biāo)Activity的onCreate
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
//最終回調(diào)目標(biāo)Activity的onStart,onResume.
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
r.paused = true;
}
} else {
//存在error則停止該Activity
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
}
}
handleLaunchActivity主要做兩件事:
- 首先調(diào)用performLaunchActivity肠鲫,該在函數(shù)內(nèi)部通過Java反射機制創(chuàng)建目標(biāo)Activity牡整,然后調(diào)用它的onCreate及onStart函數(shù)次坡。
- 調(diào)用handleResumeActivity钩乍,會在其內(nèi)部調(diào)用目標(biāo)Activity的onResume函數(shù)辞州。
1.1 進入performLaunchActivity:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
// 通過反射創(chuàng)建activity實例
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
//創(chuàng)建Application對象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
//調(diào)用activity的attach方法
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
//最終會調(diào)用Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
...
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (Exception e) {
...
}
return activity;
}
主要做了三件事:
- 創(chuàng)建一個activity實例
- 調(diào)用activity的attach方法
- 通過mInstrumentation來回調(diào)activity的onCreate方法
1.1.1 activity的attach方法
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// 創(chuàng)建window,可以看出來實際上是創(chuàng)建一個PhoneWindow
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
//設(shè)置mwindow回調(diào)寥粹,會讓window持有activity的引用
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
// 設(shè)置windowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
主要做三件事:
- 創(chuàng)建創(chuàng)建出window 它的實現(xiàn)是PhoneWindow
- 給window設(shè)置回調(diào)接口变过,這個接口的實現(xiàn)是activity自己
- 給window設(shè)置windowManager
1.1.1.1 activity的callback方法
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback {
activity實現(xiàn)了Window.Callback,看下Callback接口做了什么:
public interface Callback {
/**
* Called to process key events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchKeyEvent} to do the
* standard key processing.
*
* @param event The key event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchKeyEvent(KeyEvent event);
/**
* Called to process a key shortcut event.
* At the very least your implementation must call
* {@link android.view.Window#superDispatchKeyShortcutEvent} to do the
* standard key shortcut processing.
*
* @param event The key shortcut event.
* @return True if this event was consumed.
*/
public boolean dispatchKeyShortcutEvent(KeyEvent event);
/**
* Called to process touch screen events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchTouchEvent} to do the
* standard touch screen processing.
*
* @param event The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent event);
//代碼省略
}
可以看到callback里面有一個dispatchTouchEvent方法涝涤,actiivty里的dispatchTouchEvent就是對這個方法的具體實現(xiàn)媚狰。那么這個window.callback里的點擊事件又是從哪里傳過來的,dispatchTouchEvent又是什么時候被調(diào)用的呢阔拳?繼續(xù)看
1.1.2 調(diào)用activity的onCreate方法
看下mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);方法的內(nèi)部實現(xiàn):
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
activity.performCreate的內(nèi)部:
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
//調(diào)用activity的 onCreate
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
2. handleResumeActivity
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
....
// 最終會調(diào)用activity.onResume()方法
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//獲得一個View對象
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 獲得ViewManager對象
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把剛才的decor對象加入到ViewManager中
wm.addView(decor, l);
}
...
}
主要做了兩件事:
- 調(diào)用activity的onResume方法
- 將view和window關(guān)聯(lián)在一塊
2.1 調(diào)用activity的onResume方法
在handleResumeActivity里面調(diào)用了performResumeActivity:
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
...
//調(diào)用 activity.perforResume()
r.activity.performResume();
}
它會調(diào)用activity里的performResume:
final void performResume() {
performRestart();
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onResume()");
}
// invisible activities must be finished before onResume() completes
if (!mVisibleFromClient && !mFinished) {
Log.w(TAG, "An activity without a UI must call finish() before onResume() completes");
if (getApplicationInfo().targetSdkVersion
> android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
throw new IllegalStateException(
"Activity " + mComponent.toShortString() +
" did not call finish() prior to onResume() completing");
}
}
// Now really resume, and install the current status bar and menu.
mCalled = false;
mFragments.dispatchResume();
mFragments.execPendingActions();
onPostResume();
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
}
主要做了兩件事:
- 調(diào)用 performRestart();最終會調(diào)用activity的onStart方法
- 使用mInstrumentation 來控制activity的 onResume執(zhí)行
3 將xml文件添加到decorView
通過上面的分析可以知道崭孤,在activity的onResume方法執(zhí)行之后,會將decorView添加到窗口,那么我們自己寫的布局是什么時候添加到窗口上面取得呢辨宠?我們一般都是在onCreate方法中調(diào)用setContentView方法來設(shè)置自己寫的布局的遗锣。那么就先分析一下setContentView
3.1 setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()返回的就是在attach方法里面創(chuàng)建的PhoneWindow。
看一下官方是怎么解釋W(xué)indow這個類的:
· Window:abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
指的是window是一個被加到window manager的頂層view嗤形。
再看一下View這個類官方的解釋:
· View:This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling.
View是一個基本的用于用戶交互的不見精偿,它占據(jù)了屏幕上的一塊區(qū)域,用于繪制和事件處理
我們進入PhoneWindow的setContentView方法:
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//mContentParent為ViewGroup類型赋兵,它的初值為null
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
主要做了以下事情:
- 創(chuàng)建一個DecorView笔咽,并初始化這個decorView
- 將我們自己的布局添加到mContentParent
3.1.1 創(chuàng)建并初始化DecorView
看一下installDecor的內(nèi)部實現(xiàn):
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
它做了下面的事情:
- 調(diào)用generateDecor創(chuàng)建一個DecorView,并初始化
- 初始化mContentParent
3.1.1.1 初始化DecorView
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
創(chuàng)建一個DecorView
3.1.1.2 初始化mContentParent
protected ViewGroup generateLayout(DecorViewdecor){
......
intlayoutResource;
intfeatures = getLocalFeatures();
if((features & ((1 << FEATURE_LEFT_ICON) |(1 <<FEATURE_RIGHT_ICON))) != 0) {
if(mIsFloating) {
//根據(jù)情況取得對應(yīng)標(biāo)題欄的資源id
layoutResource = com.android.internal.R.layout.dialog_title_icons;
}
......
}
mDecor.startChanging();
View in =mLayoutInflater.inflate(layoutResource, null);
//加入標(biāo)題欄
decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
/*
ID_ANDROID_CONTENT的值為”com.android.internal.R.id.content”
這個contentParent由findViewById返回霹期,實際上就是mDecorView的一部分叶组。
*/
ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
mDecor.finishChanging();
return contentParent;
}
主要做了兩件事:
- decorView設(shè)置標(biāo)題欄的信息
- 初始化contentParent,contentParent是decorView的一部分经伙,其實decorview內(nèi)部有兩個子元素:titlebar和contentParent
4 DecorView和Window
再回到AcrivityThread的handleResumeActivity方法扶叉,會調(diào)用
wm.addView(decor, l);
decor就是decorview,他是自定義布局的頂層布局帕膜,我們在setContentView的時候已經(jīng)把自己寫的布局添加到decorView里了,所以它現(xiàn)在代表的就是我們的自定義布局溢十,現(xiàn)在調(diào)用addView方法的目的就是把我們自己的布局添加到窗口上垮刹。
wm是WindowManager實例,它的具體實現(xiàn)類是WindowManagerImpl张弛,WindowManagerImpl將實際的操作委托給一個名為mGlobal的成員來完成荒典,mGlobal的類型是WindowManagerGlobal。現(xiàn)在看下WindowManagerGlobal的addview方法吞鸭。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
上面代碼先創(chuàng)建一個ViewRootImpl實例寺董,然后將DecorView、viewRootImpl刻剥、wparams方別放入mViews遮咖、mRoots和mParams中,完成窗口信息的添加工作造虏,然后通過ViewRoot和WMS通信御吞,完成新窗口的添加工作。
5 ViewRootImpl
下面看一下root.setView(view, wparams, panelParentView)方法的內(nèi)部實現(xiàn)漓藕,因為本文只關(guān)注點擊事件分發(fā)機制陶珠,所以只保留setView方法中和點擊事件相關(guān)的代碼:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
}
}
}
和點擊事件相關(guān)的流程如下:
- 當(dāng)窗口不包含INPUT_FEATURE_NO_INPUT_CHANNEL屬性的時候,創(chuàng)建并初始化InputChannel享钞,InputChannel是窗口接收來自InputDispatcher輸入的管道揍诽。
- 會調(diào)用 mWindowSession.addToDisplay方法將窗口添加到WMS中,之后InputChannel就可以準(zhǔn)備接收各種事件了。
- 最后會創(chuàng)建一個WindowInputEventReceiver用于接收并處理輸入事件暑脆。
當(dāng)一個輸入時間被派發(fā)給ViewRootImpl所在的窗口時交排,Looper就會喚醒并觸發(fā)InputEventReceiver的onInputEvent()回調(diào)(引用自《深入理解Android卷3》)。
我們看下其源碼:
/**
* Called when an input event is received.
* The recipient should process the input event and then call {@link #finishInputEvent}
* to indicate whether the event was handled. No new input events will be received
* until {@link #finishInputEvent} is called.
*
* @param event The input event that was received.
*/
public void onInputEvent(InputEvent event) {
finishInputEvent(event, false);
}
不過finishInputEvent方法并不會被實現(xiàn)饵筑,因為被調(diào)用的對象實際上是WindowInputEventReceiver埃篓,而WindowInputEventReceiver內(nèi)部會重寫onInputEvent方法:
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
它會調(diào)用:
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
被傳入的processImmediately值為true,所以會執(zhí)行doProcessInputEvents:
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
deliverInputEvent(q);
}
在這個方法中會處理所有的輸入事件根资,它會遍歷調(diào)用deliverInputEvent:
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
stage.deliver(q);這里用到了責(zé)任鏈模式架专,點擊事件最終會由ViewPostImeInputStage的onProcess來處理:
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
根據(jù)不同的事件類型會回調(diào)不同的方法,如果是觸摸事件會回調(diào)processPointerEvent:
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
final View eventTarget =
(event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
mCapturingView : mView;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = eventTarget.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
7 從ViewRootImpl派發(fā)到View系統(tǒng)
方法里的mView就是DecorView玄帕,eventTarget.dispatchPointerEvent(event);也就是會回調(diào)DecorView的dispatchPointerEvent部脚,DecorView沒有重寫這個方法,最終會調(diào)用它的父類View中的dispatchPointerEvent:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
因為是觸摸事件所以會調(diào)用dispatchTouchEvent裤纹,而DecorView又重寫了該方法:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
這里的cb就是Activity委刘,是在Activity的attach方法中傳到mWindow中去的。所以最終會回調(diào)的是Activity的dispatchTouchEvent方法鹰椒。
(完)
參考: