Android中點擊事件的來源
本文將分以下在步驟尋找android中事件的來源:
一、activity啟動時 創建window以及windowManager的過程。
二、調用setContentView設置布局的過程
三、Activity在onResume之后創建ViewRootImp將window和DecorView關聯的過程
四、創建InputChanel 發送給InputManagerService進行注冊的過程
五、WindowInputEventReceiver 接受到點擊事件傳遞到Actvity的過程。
六、Activity將事件傳遞給view的過程。
對于底層c++代碼如何使用inputChannel來傳遞事件,目前小弟不懂,所有不敢胡謅鄒。所以本文只分析java代碼。
下面進入正文:
一、activity啟動之后發生了什么?
activity啟動將調用很多方法,本文我們著重看activity的 attach方法(即創建window,和windowManager的發法)。
我們直接從 ActivityThread的 handleLaunchActivity開始看吧。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
可以看到先調用了 performLaunchActivity,在這個方法之后會調用handleResumeActivity,從名字上可以大概猜出來handleResumeActivity 最終會執行activiyt的onResume方法。我們先分析第一個方法performLaunchActivity,看看它到底怎么啟動。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
//使用反射創建activity
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) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
//調用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.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;
if (r.isPersistable()) {
//最終會執行activity的 onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
ok,這里只要有3塊,第一塊很容易看出來 是使用反射創建Activity的實例
第二塊是調用 activity的attach我們一會著重看一下,第三塊是使用Instrumentation執行一個犯法,這個方法最終會走activiy.onCreate(),
我們先看 第三塊吧:
Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
activity.java
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
restoreHasCurrentPermissionRequest(icicle);
//調用activity的 onCreate
onCreate(icicle, persistentState);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
好了,第三塊就看完了。這下來看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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//這里直接new PhoneWindow 我貼出的是6.0 的代碼
//5.0 這塊是 mWindow = PolicyManager.makeNewWindow(this);
//實質是一樣的。
mWindow = new PhoneWindow(this);
//給window 設置callback ,最后分發事件的時候 會回調
//因為activity剛好實現了 callback接口,decorView最后會回調它,將事件傳給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());
}
}
//設置windowManager,其實他的實現是windowManagerImpl
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;
}
好了,這里主要有3個功能:
1、創建創建出window 它的實現是PhoneWindow
2、給window設置回調接口,這個接口的實現是activity自己
3、給window設置windowManager
下面先看看 Callback接口,然后繼續分析activity的啟動過程
// 它實現了 Callback接口
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback {
接口的主要方法:
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);
...
好了,到這里activity的創建、執行attach 、onCreate方法我們就已經分析了。
接下來我們就回過頭來看handleLaunchAcitivity在調用handleResumeActivity之后發生了什么。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
....
// 最終會調用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 decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
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;
//調用windowManagerImp的 addView方法
wm.addView(decor, l);
}
這里可以看到,在調用performResumeActivity(token, clearHide);
之后 會調用 wm.addView(decor, l);
performResumeActivity 這個方法將使得activity調用onResume方法,而wm.addView(decor,l)將使得創建一個viewRootImpl 將decorView和window關聯起來,具體怎么關聯?文章后面會繼續講解。現在先來看 執行performResumeActivity 發生了什么?
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
...
//調用 activity.perforResume()
r.activity.performResume();
activity.java
final void performResume() {
//最終會調用activity.onStart
performRestart();
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
mCalled = false;
// mResumed is set by the instrumentation
//使用mInstrumentation 來控制activity的 onResume執行新
mInstrumentation.callActivityOnResume(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onResume()");
}
// invisible activities must be finished be
Instrumentation.java
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
//調用onResume
activity.onResume();
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
am.match(activity, activity, activity.getIntent());
}
}
}
}
至此,activity的啟動就看的差不多了,主要還是開 activity.attach()的執行內容,因為創建PhoneWindow、WindowManagerImpl 、設置Callback才是本文繼續分析的核心。
下面貼時序圖:
時序圖有些不標準的大神勿噴~,好接著看我們分析 調用setContentView設置布局的過程。
二、調用setContentView之后發生了什么?
我們經常在activiy的onCreate方法執行時,調用setContentView,來設置布局。
我從網上搞來一張圖,幫助大家理解一下:
我們設置的其實就是那個 ContentView。
下面我門進入分析:
Activity#setContentView
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以看到 getWindow升級就是PhoneWindow ,也就是調用了 phoneWindow的 setContentView方法
PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
// 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)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
可以看到他調用了installDecor 初始化DecorView 并初始化 mContentParent
并將我們設置的layoutResId 加載到 mContentParent
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
主要調用了generateDecor 、generateLayout(mDecor);
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
...
其實就主要功能就是 創建DecorView 并將我們自己設置的布局加載到DecorView中。這里我們就不著重分析了。
時序圖:
我們繼續分析:
三、Activity在onResume之后創建ViewRootImp將window和DecorView關聯的過程
我們在講第一塊的時候,我們講過ActivityThead在 onResume()之后會調用windowManagerImpl的addView犯法。
ActivityThread#handleResumeActivity
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//這里
wm.addView(decor, l);
}
這里可以看到,他調用 wm.addView(decor, l);將decorVIiew傳遞了進去。
我們進入windowManagerImp進行分析
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
可以看到他調用了 mGlobal.addView(view, params, mDisplay, mParentWindow);
這里實際是個橋接模式
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
那我們繼續查看。
WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
//創建viewRootImpl
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 {
//調用viewRootImpl的 setView
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;
}
}
可以看到,這里創建了ViewRootImpl ,并通過root.setView將 docorVIew和window關聯起來。
四、創建InputChanel 發送給InputManagerService進行注冊的過程
我們接著上面的分析,查看VIewRootImpl的構造方法、以及setView里面發生了什么。
VIewRootImp:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
...
}
這里主要看他調用了WindowManagerGlobal.getWindowSession(); 獲得了一個Session
我們點進去查看:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
//這里實際調用了windowManagerService.openSession方法
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
這里使用windowManager.openSession() 實際是調用windowManagerService的openSession()方法,他是個夸進程的調用。這點從getWindowManagerService方法中可以看出:
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
}
return sWindowManagerService;
}
}
他實際就是aidl . 實際Session的實現也是aidl.
客戶端這邊的進程到時會用這個Session來遠程調用 WindowManagerServices所在的進程。
好了,我們來看WindowManagerService的openSession方法。
WindowManagerService#openSession
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
這里直接new 了一個Session進行返回,注意通過構造方法還把 windowManagerService自身傳了進去。
好了ViewRootImpl的構造方法就看完了,接下來看他的setView方法。
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//創建出了InputChanel
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//遠程調用,出傳遞window 和 inputChannel
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
...
//創建 WindowInputEventReceiver
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
...
好了,終于看到InputChannel了,這里通過mWindowSession.addToDisplay()
將它傳遞到了windowManagerService那個進程
并隨后在這里創建了WindowInputEventReceiver 來接受事件。
我們先看mWindowSession.addToDisplay() 這個方法,這個方法發生在Session中
Session#addToDisplay
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
可以看到這里又調用mService.addWindow() 這個方法,mService正是在 windowManagerService的openSeesion中通過構造方法傳遞過來的。
好了接著看 windowManagerService的addWindow()
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
//注冊inputChannel
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
可以看到調用了mInputManager.registerInputChannel()
他的實現在InputManagerService
InputManagerService#registerInputChannel
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
可以看到它用到了jni 將 inputChannel傳遞了進去。好了我們就只能分析到這里了。
我們回過頭繼續看 客戶端進程,那邊創建WindowInputEventReceiver的事吧
我們看它的構造方法:
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
它調用了父類的構造方法,它的父類是InputEventReceiver
我們來看它的父類:
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
可以看到他在構造方法調用了 nativeInit將 InputChannel傳遞了下去。
我們來看他一個重要的方法:
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
從注釋中可以看出,底層的代碼將調用這個方法,那么必然事件會從這里出來。
下面我們先附上這一部分的時序圖,然后我們繼續
五、WindowInputEventReceiver 接受到點擊事件分發的過程。
最后一部分了,我們來看事件出來之后怎么分發。
我們繼續上面的,從上文中可以看出,事件出來之后會先調用dispatchInputEvent
然后這個方法又會調用onInputEvent。
那我們來看onInputEvent在WindowInputEventReceiver中的實現
WindowInputEventReceiver#onInputEvent
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
可以看到它調用了VIewRootImpl的enqueueInputEvent 方法
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
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();
}
}
接著調用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);
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
繼續調用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);
}
}
這里我們可以看到他出現一個InputStage,他是一個鏈表結構:
abstract class InputStage {
private final InputStage mNext;
protected static final int FORWARD = 0;
protected static final int FINISH_HANDLED = 1;
protected static final int FINISH_NOT_HANDLED = 2;
/**
他的實現類有:AsyncInputStage、EarlyPostImeInputStage、SyntheticInputStage、ViewPreImeInputStage、ViewPostImeInputStage
對于我們的觸摸事件來說他的實現就是 ViewPostImeInputStage 。
好了,我們接著看,上文看到它執行了stage.deliver(q);
那我們接著看:
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
//調用了onProcess處理事件
apply(q, onProcess(q));
}
}
我們來看onProcess在ViewPostImeInputStage的實現:
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
// If delivering a new non-key event, make sure the window is
// now allowed to start updating.
handleDispatchDoneAnimating();
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);
}
}
}
可以看到它掉用了processPointerEvent
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
//這里的 mView 設置viewRootImp.setView時傳進來的,即DecorView
boolean handled = mView.dispatchPointerEvent(event);
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
dispatchPointerEvent 的實現在 View中
View#dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
view 中的dispatchPointerEvent 又調用了dispatchTouchEvent
然而dispatchTouchEvent 在DecorView 中被重寫了
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//調用phoneWindow的 getCallback()
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
因為DecorView是 PhoneWindow的內部類,所以調用的是 PhoneWindow的getCallback()
/** * Return the current Callback interface for this window. */
public final Callback getCallback() {
return mCallback;
}
那么還記得 第一部分講的 activity 的attach中做了什么嗎? 那么這個callback就是 activity.
到這里事件就傳遞到了Activity
好了,繼續來看最后一節.
六、Activity將事件傳遞給view的過程。
繼續上文的,它代用餓了callback的dispatchTouchEvent ,也就是調用了
activity的dispatchTouchEvent 方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//調用了 PhoneWindow的 superDispatchTouchEvent()
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
可以看到它調用了PhoneWindow的superDispatchTouchEvent
PhoneWindow#superDispatchTouchEvent
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
又繼續調用了DecorView的superDispatchTouchEvent 方法
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
可以看到DecorView直接調用了父類的dispatchTouchEvent方法
也即是VIewGroup的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
...
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
...
return handled;
}
最終,ViewGroup會遍歷自己的孩子進行事件傳遞。
至此,我們就分析完了。
本文只從java代碼中分析了事件的來源,其中若是有不嚴密的地方還請諒解。
來自: http://blog.csdn.net/a992036795/article/details/51690303