Andriod事件分發(fā)事件由來初識
Android事件分發(fā)的事件從何而來
事件分發(fā)一直以來都是一個android知識的重點。從應(yīng)用開發(fā)角度和用戶的交互就是在處理事件。
Activity的事件分發(fā)
事件分發(fā)一般情況都會講view的分發(fā)過程,他的過程縮略起來就可以這樣表示。
public boolean diapatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
這里就有一個問題,最早的事件是從哪里來的。根據(jù)Android的視圖模型知道最外層的view就是DecorView ,而它的外面是一個PhoneWindow。所以最初的事件就是從PhoneWindow進入了view的事件分發(fā),而PhoneWindow的事件又是Activity中來的.
//frameworks/base/core/java/android/app/Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {//這里獲取的PhoneWindow
return true;
}
return onTouchEvent(ev);
}
那么問題又來了,activity的事件是哪里來的呢。
ViewRootImpl事件分發(fā)
熟悉Android的Window創(chuàng)建流程的話就知道ViewRootImpl是所有view的最頂層。也是ViewRootImpl在setView中實現(xiàn)了View和WindowManager之間的交互。這個方法里有一個在Window創(chuàng)建流程的時候沒有關(guān)注的InputChannel,事件真正的來源就是它,在
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
.........
InputChannel inputChannel = null;//創(chuàng)建InputChannel
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);//將InputChannel傳給WMS
if (inputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());//創(chuàng)建mInputEventReceiver
}
//這里創(chuàng)建了各種事件處理器
// 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;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
}
}
}
從名字也能猜出mInputEventReceiver就是接收事件的對象了,這是一個ViewRootImpl的內(nèi)部類看下它的實現(xiàn)。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {//通過名字就知道這應(yīng)該是事件接收的回調(diào)
List<InputEvent> processedEvents;
try {
processedEvents =
mInputCompatProcessor.processInputEventForCompatibility(event);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (processedEvents != null) {
if (processedEvents.isEmpty()) {
// InputEvent consumed by mInputCompatProcessor
finishInputEvent(event, true);
} else {
for (int i = 0; i < processedEvents.size(); i++) {
enqueueInputEvent(
processedEvents.get(i), this,
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
}
}
} else {
enqueueInputEvent(event, this, 0, true);
}
}
.......
}
如果processedEvents不為空都是調(diào)用了enqueueInputEvent,不然就直接調(diào)用finishInputEvent。
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//這里做了區(qū)分是觸摸事件還是按鍵事件
if (event instanceof MotionEvent) {
MotionEvent me = (MotionEvent) event;
} else if (event instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) event;
}
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
private void scheduleProcessInputEvents() {
if (!mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = true;
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
private void handleMessageImpl(Message msg) {
switch (msg.what) {
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
}
}
這里判斷了是否要立即消費,如果立即消費doProcessInputEvents,不然調(diào)用scheduleProcessInputEvents。而scheduleProcessInputEvents很簡單就是handle發(fā)送了一個異步消息。最后handle執(zhí)行的時候還是會調(diào)用到doProcessInputEvents。所以就來詳細(xì)看下doProcessInputEvents。
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {//循環(huán)獲取InputEvent并處理
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
deliverInputEvent(q);
}
//移除異步消息
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
可以看到真實的處理都是deliverInputEvent來處理。
private void deliverInputEvent(QueuedInputEvent q) {
try {
if (mInputEventConsistencyVerifier != null) {
InputStage stage;//在ViewRootImpl的setView中初始化的處理器
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
try {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
} finally {
}
}
在deliverInputEvent中出現(xiàn)了stage,這就是在setView初始化的那些處理器,處理通過stage.deliver(q)來實現(xiàn)。 InputStage 還是ViewRootImpl的一個內(nèi)部類。
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;
private String mTracePrefix;
public InputStage(InputStage next) {
mNext = next;
}
public final void deliver(QueuedInputEvent q) {
//分發(fā)事件
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
traceEvent(q, Trace.TRACE_TAG_VIEW);
final int result;
try {
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
apply(q, result);
}
}
//處理事件由子類改寫
protected int onProcess(QueuedInputEvent q) {
return FORWARD;
}
protected void finish(QueuedInputEvent q, boolean handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
if (handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
}
forward(q);
}
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
protected void onDeliverToNext(QueuedInputEvent q) {
//向后一個 InputStage 傳遞事件
if (mNext != null) {
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
}
熟悉okhttp的話很容易就發(fā)現(xiàn)這里也是一個責(zé)任鏈模式。從setView中 InputStage 子類的初始化也能看到,其中和view相關(guān)的是ViewPostImeInputStage。
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
//判斷事件類型,觸摸事件會進入processPointerEvent
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);
}
}
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mHandwritingInitiator.onTouchEvent(event);
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
//通過mView的dispatchPointerEvent來分發(fā)事件
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
ViewRootImpl的事件就交給mView來繼續(xù)分發(fā)了,這里mView是DecorView,也是在setView中傳進來的。
DecorView事件處理
//frameworks/base/core/java/android/view/View.java
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
//frameworks/base/core/java/com/android/internal/policy/DecorView.java
@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);
}
這里通過dispatchTouchEvent將事件交給了Window.Callback,而這里的Window.Callback就是Activity,兜兜轉(zhuǎn)轉(zhuǎn)終于回到了Activity的dispatchTouchEvent中。
通過這個流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,這里有一個疑問就是為什么不直接從DecorView開始分發(fā)。我猜測是為了方便在應(yīng)用層重寫Activity中的onTouch來消費沒有view處理的事件。
現(xiàn)在還有一個疑問是WMS的事件是怎么來的,這個留著后續(xù)再分析。
以上就是Android事件分發(fā)事件由來初識的詳細(xì)內(nèi)容,更多關(guān)于Android事件分發(fā)事件由來的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android系統(tǒng)默認(rèn)對話框添加圖片功能
這篇文章主要介紹了Android系統(tǒng)默認(rèn)對話框添加圖片的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-01-01
Android使用Walle實現(xiàn)多渠道打包功能的實現(xiàn)示例
這篇文章主要介紹了Android使用Walle實現(xiàn)多渠道打包功能的實現(xiàn)示例,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-04-04
Android TextView實現(xiàn)多文本折疊、展開效果
這篇文章主要為大家詳細(xì)介紹了Android TextView實現(xiàn)多文本折疊、展開效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05
Android CardView+ViewPager實現(xiàn)ViewPager翻頁動畫的方法
本篇文章主要介紹了Android CardView+ViewPager實現(xiàn)ViewPager翻頁動畫的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
android為ListView每個Item上面的按鈕添加事件
本篇文章主要介紹了android為ListView每個Item上面的按鈕添加事件,有興趣的同學(xué)可以了解一下。2016-11-11
android Setting中隱藏項實現(xiàn)原理與代碼
我們都知道做程序員有時會就像android中,程序員在setting中就隱藏這樣一項,接下來將詳細(xì)介紹,感興趣的朋友可以了解下哦2013-01-01
Android編程實現(xiàn)獲得手機屏幕真實寬高的方法
這篇文章主要介紹了Android編程實現(xiàn)獲得手機屏幕真實寬高的方法,以兩個實例形式分析了獲取手機屏幕信息的相關(guān)技巧,需要的朋友可以參考下2015-10-10

