Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析1——Activity
Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析2——ViewGroup
Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析3——View
一 產(chǎn)生點(diǎn)擊事件
當(dāng)用戶(hù)用點(diǎn)擊顯示屏產(chǎn)生一個(gè)點(diǎn)擊事件,本章討論點(diǎn)擊事件是如何傳到Activity上的,Android 源碼為sdk25。
二 系統(tǒng)如何將點(diǎn)擊事件派發(fā)給Activity
1. handleLaunchActivity
啟動(dòng)一個(gè)新的Activity的時(shí)候,會(huì)調(diào)用ActivityThread的handleLaunchActivity方法,我們從這里開(kāi)始看:
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)部通過(guò)Java反射機(jī)制創(chuàng)建目標(biāo)Activity,然后調(diào)用它的onCreate及onStart函數(shù)。
- 調(diào)用handleResumeActivity,會(huì)在其內(nèi)部調(diào)用目標(biāo)Activity的onResume函數(shù)。
1.1 進(jìn)入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);
}
// 通過(guò)反射創(chuàng)建activity實(shí)例
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對(duì)象
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;
//最終會(huì)調(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)建一個(gè)activity實(shí)例
- 調(diào)用activity的attach方法
- 通過(guò)mInstrumentation來(lái)回調(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,可以看出來(lái)實(shí)際上是創(chuàng)建一個(gè)PhoneWindow
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
//設(shè)置mwindow回調(diào),會(huì)讓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 它的實(shí)現(xiàn)是PhoneWindow
- 給window設(shè)置回調(diào)接口,這個(gè)接口的實(shí)現(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實(shí)現(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里面有一個(gè)dispatchTouchEvent方法,actiivty里的dispatchTouchEvent就是對(duì)這個(gè)方法的具體實(shí)現(xiàn)。那么這個(gè)window.callback里的點(diǎn)擊事件又是從哪里傳過(guò)來(lái)的,dispatchTouchEvent又是什么時(shí)候被調(diào)用的呢?繼續(xù)看
1.1.2 調(diào)用activity的onCreate方法
看下mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);方法的內(nèi)部實(shí)現(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) {
....
// 最終會(huì)調(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();
//獲得一個(gè)View對(duì)象
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 獲得ViewManager對(duì)象
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對(duì)象加入到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();
}
它會(huì)調(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();最終會(huì)調(diào)用activity的onStart方法
- 使用mInstrumentation 來(lái)控制activity的 onResume執(zhí)行
3 將xml文件添加到decorView
通過(guò)上面的分析可以知道,在activity的onResume方法執(zhí)行之后,會(huì)將decorView添加到窗口,那么我們自己寫(xiě)的布局是什么時(shí)候添加到窗口上面取得呢?我們一般都是在onCreate方法中調(diào)用setContentView方法來(lái)設(shè)置自己寫(xiě)的布局的。那么就先分析一下setContentView
3.1 setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()返回的就是在attach方法里面創(chuàng)建的PhoneWindow。
看一下官方是怎么解釋W(xué)indow這個(gè)類(lèi)的:
· 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是一個(gè)被加到window manager的頂層view。
再看一下View這個(gè)類(lèi)官方的解釋?zhuān)?/p>
· 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是一個(gè)基本的用于用戶(hù)交互的不見(jiàn),它占據(jù)了屏幕上的一塊區(qū)域,用于繪制和事件處理
我們進(jìn)入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類(lèi)型,它的初值為null
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
主要做了以下事情:
- 創(chuàng)建一個(gè)DecorView,并初始化這個(gè)decorView
- 將我們自己的布局添加到mContentParent
3.1.1 創(chuàng)建并初始化DecorView
看一下installDecor的內(nèi)部實(shí)現(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)建一個(gè)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)建一個(gè)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ù)情況取得對(duì)應(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”
這個(gè)contentParent由findViewById返回,實(shí)際上就是mDecorView的一部分。
*/
ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
mDecor.finishChanging();
return contentParent;
}
主要做了兩件事:
- decorView設(shè)置標(biāo)題欄的信息
- 初始化contentParent,contentParent是decorView的一部分,其實(shí)decorview內(nèi)部有兩個(gè)子元素:titlebar和contentParent
4 DecorView和Window
再回到AcrivityThread的handleResumeActivity方法,會(huì)調(diào)用
wm.addView(decor, l);
decor就是decorview,他是自定義布局的頂層布局,我們?cè)趕etContentView的時(shí)候已經(jīng)把自己寫(xiě)的布局添加到decorView里了,所以它現(xiàn)在代表的就是我們的自定義布局,現(xiàn)在調(diào)用addView方法的目的就是把我們自己的布局添加到窗口上。
wm是WindowManager實(shí)例,它的具體實(shí)現(xiàn)類(lèi)是WindowManagerImpl,WindowManagerImpl將實(shí)際的操作委托給一個(gè)名為mGlobal的成員來(lái)完成,mGlobal的類(lèi)型是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)建一個(gè)ViewRootImpl實(shí)例,然后將DecorView、viewRootImpl、wparams方別放入mViews、mRoots和mParams中,完成窗口信息的添加工作,然后通過(guò)ViewRoot和WMS通信,完成新窗口的添加工作。
5 ViewRootImpl
下面看一下root.setView(view, wparams, panelParentView)方法的內(nèi)部實(shí)現(xiàn),因?yàn)楸疚闹魂P(guān)注點(diǎn)擊事件分發(fā)機(jī)制,所以只保留setView方法中和點(diǎn)擊事件相關(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);
}
}
}
和點(diǎn)擊事件相關(guān)的流程如下:
- 當(dāng)窗口不包含INPUT_FEATURE_NO_INPUT_CHANNEL屬性的時(shí)候,創(chuàng)建并初始化InputChannel,InputChannel是窗口接收來(lái)自InputDispatcher輸入的管道。
- 會(huì)調(diào)用 mWindowSession.addToDisplay方法將窗口添加到WMS中,之后InputChannel就可以準(zhǔn)備接收各種事件了。
- 最后會(huì)創(chuàng)建一個(gè)WindowInputEventReceiver用于接收并處理輸入事件。
當(dāng)一個(gè)輸入時(shí)間被派發(fā)給ViewRootImpl所在的窗口時(shí),Looper就會(huì)喚醒并觸發(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);
}
不過(guò)finishInputEvent方法并不會(huì)被實(shí)現(xiàn),因?yàn)楸徽{(diào)用的對(duì)象實(shí)際上是WindowInputEventReceiver,而WindowInputEventReceiver內(nèi)部會(huì)重寫(xiě)onInputEvent方法:
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
它會(huì)調(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,所以會(huì)執(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);
}
在這個(gè)方法中會(huì)處理所有的輸入事件,它會(huì)遍歷調(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é)任鏈模式,點(diǎn)擊事件最終會(huì)由ViewPostImeInputStage的onProcess來(lái)處理:
@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ù)不同的事件類(lèi)型會(huì)回調(diào)不同的方法,如果是觸摸事件會(huì)回調(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);也就是會(huì)回調(diào)DecorView的dispatchPointerEvent,DecorView沒(méi)有重寫(xiě)這個(gè)方法,最終會(huì)調(diào)用它的父類(lèi)View中的dispatchPointerEvent:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
因?yàn)槭怯|摸事件所以會(huì)調(diào)用dispatchTouchEvent,而DecorView又重寫(xiě)了該方法:
@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中去的。所以最終會(huì)回調(diào)的是Activity的dispatchTouchEvent方法。
(完)
參考: