中国开源软件网

当前位置: 首页 > 热点 >

所以至于是谁问的我,Android,也就是说事件是怎么来的

时间:2021-01-27 11:43来源:互联网 作者:小狐

本文主要针对的是 我们认为的事件分发 之前的流程,也就是说事件是怎么来的?一路又是怎么走下去的?

那为什么要研究事件的来龙去脉呢?其实我写本文之前是知道事件是以下的这种走向传递的。

Avtivity->PhoneWindow->DecorView->ViewGroup->View

直到有一天, 有人问我那Activity是怎么接到事件的?我当时是一脸懵逼,就没有了,所以至于是谁问的我,估计你们也知道了。

本文争取以极简的方式来描述,如有问题欢迎指正。

我们开始表演

首先整个过程中会涉及以下几个类

Activity

PhoneWindow extends Window

WindowManager extends ViewManager

WindowManagerImpl implements WindowManager

WindowManagerGlobal

ViewRootImpl implements ViewParent

WindowManagerService extends IWindowManager.Stub

DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks

InputChannel,InputQueue,WindowInputEventReceiver extends InputEventReceiver

ViewRootHandler extends Handler

OK,我们先上结论,后展开说明,以下部分纯干货,如之前看过部分源码可直接看这部分,对源码不是很熟悉的也没有关系,下一个环节就是展开说明。

所以至于是谁问的我,Android,也就是说事件是怎么来的(图1)

1. ActivityThread.performLaunchActivity 中调用 Activity.attach,创建PhoneWindow,PhoneWindow创建DecorView。

2. ActivityThread.handleResumeActivity中调用Activity.makeVisible,初始化WindowManager并调用WindowManager.addView

3. 因为WindowManager是接口,找到其实现类并调用WindowManagerImpl.addView。

4. 调用WindowManagerGlobal.addView(View view, ..., Window parentWindow, ...)

此时需要注意view为DecorView对象,该对象是通过ActivityThread.handleResumeActivity中的PhoneWindow.getDecorView获取,并直接赋值给Activity变量的。

5. WindowManagerGlobal.addView创建了ViewRootImpl,并调用了ViewRootImpl.setView(view,...,...)

6. 连带创建InputChannel,InputQueue以及WindowInputEventReceiver对象并传入InputChannel和Looper

7. 其实Android事件的源头来自于用户输入行为,由硬件进行捕获,一般会保存在dev/input节点下,后续组装成KeyEvent/MotionEvent对象,经Native进入Java的InputEventReceiver.dispatchInputEvent中。

8. ViewRootImpl.WindowInputEventReceiver extends InputEventReceiver,连带调用了enqueueInputEvent->doProcessInputEvents-> deliverInputEvent(q)方法中获取到mFirstPostImeInputStage对象其实为ViewPostImeInputStage。

9. ViewPostImeInputStage extends InputStage 因此执行onProcess,判断如果是触摸事件,调用processPointerEvent,内部调用mView.dispatchPointerEvent,此时的mView为DecorView,辗转调用到了DecorView.dispatchTouchEvent

10. 通过 mWindow.getCallback获取Window.Callback调用Window.Callback.dispatchTouchEvent,这个Callback就是PhoneWindow里的mCallback,而mCallback则是Activity的attach赋值的,此处也就自然调用到了Activity中,后续就是我们都知道的事件分发了,一个完整的闭环就结束了。

ActivityThread.performLaunchActivity

privateActivity performLaunchActivity( ActivityClientRecord r, Intent customIntent)

...

if(activity != null)

...

//可以看到此处调用了Activity的attach方法

activity.attachappContext, this, getInstrumentation, r.token。

r.ident, app, r.intent, r.activityInfo, title, r.parent。

r.embeddedID, r.lastNonConfigurationInstances, config。

r.referrer, r.voiceInteractor, window, r.configCallback。

r.assistToken

...

1.1 Activity.attach中进行了PhoneWindow初始化,并设置CallBack对象。

finalvoidattachContext context, ActivityThread aThread。

Instrumentation instr, IBinder token, intident。

Application application, Intent intent, ActivityInfo info。

CharSequence title, Activity parent, Stringid。

NonConfigurationInstances lastNonConfigurationInstances。

Configuration config, Stringreferrer, IVoiceInteractor voiceInteractor。

Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken

attachBaseContext(context)

mFragments.attachHost( null/*parent*/)

//PhoneWindow初始化

mWindow = newPhoneWindow( this, window, activityConfigCallback)

mWindow.setWindowControllerCallback(mWindowControllerCallback)

mWindow.setCallback( this)

mWindow.setOnWindowDismissedCallback( this)

mWindow.getLayoutInflater.setPrivateFactory( this)

2.ActivityThread.handleResumeActivity 通过PhoneWindow获取DecorView,调用activity.makeVisible

public voidhandleResumeActivityIBinder token, boolean finalStateRequest, boolean isForward。

Stringreason

...

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。

...

if(a.mVisibleFromClient)

if(a.mWindowAdded)

a.mWindowAdded = true。

wm.addView(decor, l)

} else{

a.onWindowAttributesChanged(l)

} elseif(willBeVisible) {

iflocalLOGV Slog.vTAG, “Launch ”+ r + “ mStartedActivity set”。

r.hideForNow = true。

...

if(r.activity.mVisibleFromClient)

r.activity.makeVisible。

...

2.1 PhoneWindow创建DecorView的过程

@Override

if(mDecor == null mForceDecorInstall)

//组装DecorView

installDecor。

returnmDecor。

privatevoidinstallDecor

mForceDecorInstall = false。

if(mDecor == null)

//获取DecorView

mDecor = generateDecor(- 1)

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS)

mDecor.setIsRootNamespace( true)

if(mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0)

mDecor.postOnAnimation(mInvalidatePanelMenuRunnable)

//返回值就是DecorView

protectedDecorView generateDecor( intfeatureId)

// System process doesnt have application context and in that case we need to directly use

// the context we have. Otherwise we want the application context, so we dont cling to the

// activity.

Context context。

if(mUseDecorContext)

Context applicationContext = getContext.getApplicationContext。

if(applicationContext == null)

context = getContext。

} else{

context = newDecorContext(applicationContext, this)

if(mTheme != - 1)

context.setTheme(mTheme)

} else{

context = getContext。

returnnewDecorView(context, featureId, this, getAttributes)

2.2. activity.makeVisible过程

voidmakeVisible

if(mWindowAdded)

ViewManager wm = getWindowManager。

//此时调用的其实事WindowManager.addView

wm.addView(mDecor, getWindow.getAttributes)

mWindowAdded = true。

mDecor.setVisibility(View.VISIBLE)

3. WindowManager是接口,找到其实现类并调用WindowManagerImpl.addView

@ Override

publicvoidaddView( @NonNull View view, @NonNull ViewGroup.LayoutParams params)

applyDefaultToken( params)

//此时调用的是WindowManagerGlobal.addView

mGlobal.addViewview, params, mContext.getDisplayNoVerify, mParentWindow。

mContext.getUserId

4. WindowManagerGlobal.addView 初始化ViewRootImpl,调用root.setView

publicvoidaddView View view, ViewGroup.LayoutParams params。

Display display, Window parentWindow, intuserId

...

synchronized (mLock)

...

//初始化ViewRootImpl

root = newViewRootImpl(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, userId)

} catch(RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if(index >= 0)

removeViewLocked(index, true)

throwe。

5. ViewRootImpl.setView(view,...,...) 创建InputChannel, InputQueue, WindowInputEventReceiver

publicvoidsetView(View view, WindowManager.LayoutParams attrs, View panelParentView, intuserId)

synchronized( this)

if(mView == null)

mView = view。

...

// Schedule the first layout -before- adding to the window

// manager, to make sure we do the relayout before receiving

// any other s from the system.

requestLayout。

InputChannel inputChannel = null。

ifmWindowAttributes.inputFeatures

& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)

inputChannel = newInputChannel。

if(inputChannel != null)

if(mInputQueueCallback != null)

//创建对象

mInputQueue = newInputQueue。

mInputQueueCallback.onInputQueueCreated(mInputQueue)

mInputEventReceiver = newWindowInputEventReceiverinputChannel。

Looper.myLooper

6. 其实Android事件的源头来自于用户输入行为,由硬件进行捕获,一般会保存在 dev/input 节点下,后续组装成KeyEvent/MotionEvent对象,经Native进入Java的InputEventReceiver.dispatchInputEvent中。

我们先分析InputEventReceiver

privatevoiddispatchInputEvent( intseq, InputEvent )

mSeqMap.put( .getSequenceNumber, seq)

onInputEvent( )

publicvoidonInputEvent( InputEvent )

finishInputEvent( , false)

由于InputEventReceiver是abstract类,因此需要找到对应的实现类,此时第5步中 WindowInputEventReceiver 即是实现类,因此找到重写的onInputEvent方法进行下一步分析。

7. ViewRootImpl.WindowInputEventReceiver extends InputEventReceiver,连带调用了enqueueInputEvent->doProcessInputEvents-> deliverInputEvent(q)方法中获取到mFirstPostImeInputStage对象其实为ViewPostImeInputStage。

@ Override

publicvoidonInputEvent( InputEvent )

Trace.traceBeginTrace.TRACE_TAG_VIEW, “processInputEventForCompatibility”。

ListprocessedEvents。

try

processedEvents =

mInputCompatProcessor.processInputEventForCompatibility( )

} finally{

Trace.traceEnd(Trace.TRACE_TAG_VIEW)

if(processedEvents != null)

if(processedEvents.isEmpty)

// InputEvent consumed by mInputCompatProcessor

finishInputEvent( , true)

} else{

for( inti = 0; i < processedEvents.size; i++)

enqueueInputEvent

processedEvents. get(i) this。

QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true

} else{

enqueueInputEvent( , this, 0, true)

voidenqueueInputEvent( InputEvent , InputEventReceiver receiver, intflags, boolean processImmediately)

QueuedInputEvent q = obtainQueuedInputEvent( , receiver, flags)

// Always enqueue the input in order, regardless of its time stamp.

// We do this because the application or the IME may inject key s

// in response to touch s 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 s are monotonic.

QueuedInputEvent last = mPendingInputEventTail。

if(last == null)

mPendingInputEventHead = q。

mPendingInputEventTail = q。

} else{

last.mNext = q。

mPendingInputEventTail = q。

mPendingInputEventCount += 1。

Trace.traceCounterTrace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName。

mPendingInputEventCount

if(processImmediately)

doProcessInputEvents。

} else{

scheduleProcessInputEvents。

voiddoProcessInputEvents

// Deliver all pending input s in the queue.

while(mPendingInputEventHead != null)

QueuedInputEvent q = mPendingInputEventHead。

mPendingInputEventHead = q.mNext。

if(mPendingInputEventHead == null)

mPendingInputEventTail = null。

q.mNext = null。

mPendingInputEventCount -= 1。

Trace.traceCounterTrace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName。

mPendingInputEventCount

longTime = q.mEvent.getEventTimeNano。

longoldestEventTime = Time。

if(q.mEvent instanceof MotionEvent)

MotionEvent me = (MotionEvent)q.mEvent。

if(me.getHistorySize > 0)

oldestEventTime = me.getHistoricalEventTimeNano( 0)

mChoreographer.mFrameInfo.updateInputEventTime(Time, oldestEventTime)

deliverInputEvent(q)

// We are done processing all input s that we can process right now

// so we can clear the pending flag immediately.

if(mProcessInputEventsScheduled)

mProcessInputEventsScheduled = false。

mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS)

privatevoiddeliverInputEvent( QueuedInputEvent q)

...

try

...

InputStage stage。

if(q.shouldSendToSynthesizer)

stage = mSyntheticInputStage。

} else{

//此时返回两个InputStage类型的对象,这个InputStage为abstract因此需要找对应的实现类。

stage = q.shouldSkipIme ? mFirstPostImeInputStage : mFirstInputStage。

if(q.mEvent instanceof KeyEvent)

Trace.traceBeginTrace.TRACE_TAG_VIEW, “preDispatchToUnhandledKeyManager”。

try

mUnhandledKeyManager.preDispatchKeyEvent q.mEvent

} finally{

Trace.traceEnd(Trace.TRACE_TAG_VIEW)

if(stage != null)

handleWindowFocusChanged。

stage.deliver(q)

} else{

finishInputEvent(q)

} finally{

Trace.traceEnd(Trace.TRACE_TAG_VIEW)

此时需要注意这个InputStage 的实现类相对比较多,不过可以重点看下setView方法的末尾,着重看mFirstPostImeInputStage赋值逻辑。

publicvoidsetView(View view, WindowManager.LayoutParams attrs, View panelParentView, intuserId)

....

// Set up the input pipeline.

CharSequence counterSuffix = attrs.getTitle。

mSyntheticInputStage = newSyntheticInputStage。

//最终new了ViewPostImeInputStage

InputStage viewPostImeStage = newViewPostImeInputStage(mSyntheticInputStage)

//继续看viewPostImeStage赋值

InputStage nativePostImeStage = newNativePostImeInputStageviewPostImeStage。

“aq:native-post-ime:”+ counterSuffix

//初始化并传入nativePostImeStage,需要查看nativePostImeStage赋值逻辑,继续往上看。

InputStage earlyPostImeStage = newEarlyPostImeInputStage(nativePostImeStage)

InputStage imeStage = newImeInputStageearlyPostImeStage。

“aq:ime:”+ counterSuffix

InputStage viewPreImeStage = newViewPreImeInputStage(imeStage)

InputStage nativePreImeStage = newNativePreImeInputStageviewPreImeStage。

“aq:native-pre-ime:”+ counterSuffix

mFirstInputStage = nativePreImeStage。

//这是赋值位置,可以看以上逻辑是怎么赋值earlyPostImeStage对象

mFirstPostImeInputStage = earlyPostImeStage。

mPendingInputEventQueueLengthCounterName = “aq:pending:”+ counterSuffix。

if(mView instanceofRootViewSurfaceTaker)

PendingInsetsController pendingInsetsController =

RootViewSurfaceTaker) mView).providePendingInsetsController。

if(pendingInsetsController != null)

pendingInsetsController.replayAndAttach(mInsetsController)

通过以上逻辑可以清晰的看到mFirstPostImeInputStage 即是ViewPostImeInputStage,我们需要跟进ViewPostImeInputStage看下对应的onProcess方法。

finalclassViewPostImeInputStageextendsInputStage

publicViewPostImeInputStage(InputStage next)

super(next)

@Override

protectedintonProcess(QueuedInputEvent q)

if(q.mEvent instanceofKeyEvent)

returnprocessKeyEvent(q)

} else{

finalintsource = q.mEvent.getSource。

ifsource & InputDevice.SOURCE_CLASS_POINTER != 0

returnprocessPointerEvent(q)

} elseifsource & InputDevice.SOURCE_CLASS_TRACKBALL != 0 {

returnprocessTrackballEvent(q)

} else{

returnprocessGenericMotionEvent(q)

...

以上逻辑中先判断了输入事件是键盘,还是触摸,触摸逻辑中又判断了输入源是屏幕还是trackball(我猜测这判断的是鼠标等设备输入)不过不重要,我们主要看屏幕输入的逻辑,也就是processPointerEvent(q)方法

privateintprocessPointerEvent( QueuedInputEvent q)

final MotionEvent = (MotionEvent)q.mEvent。

mAttachInfo.mUnbufferedDispatchRequested = false。

mAttachInfo.mHandlingPointerEvent = true。

//主要看这个地方,mView即是DecorView

boolean handled = mView.dispatchPointerEvent( )

maybeUpdatePointerIcon( )

maybeUpdateTooltip( )

mAttachInfo.mHandlingPointerEvent = false。

if(mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch)

mUnbufferedInputDispatch = true。

if(mConsumeBatchedInputScheduled)

scheduleConsumeBatchedInputImmediately。

returnhandled ? FINISH_HANDLED : FORWARD。

到此我需要简单做个总结,来捋一下这个流程,方便大家理解。

其实以上的 mView即是DecorView。

该对象是在最开始通过PhoneWindow创建。

由WindowManager.addView(View v...)传递到WindowManagerGlobal.addView(View v..)

通过ViewRootImpl.setView(View v...)传到了ViewRootImpl。

因此ViewRootImpl的变量mView即是DecorView,所以我们需要跟进DecorView.dispatchPointerEvent

由于DecorView extends View,因此先看View.dispatchPointerEvent

publicfinal boolean dispatchPointerEvent( MotionEvent )

if( .isTouchEvent)

//调用dispatchTouchEvent,而DecorView重写了dispatchTouchEvent

returndispatchTouchEvent( )

} else{

returndispatchGenericMotionEvent( )

跟进DecorView.dispatchTouchEvent

publicbooleandispatchTouchEvent(MotionEvent ev)

finalWindow.Callback cb = mWindow.getCallback。

cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev)

这里需要注意下这个Window.Callback cb,是通过mWindow.getCallback获取的。

mWindow是哪来的?

这个mWindow是一个PhoneWindow对象。

是在ActivityThread.handleResumeActivity时,通过Activity.window.getDecorView调用PhoneWindow.installDecor 时 mDecor.setWindow(this)赋值的。

因此需要查看PhoneWindow.getCallback是哪里赋值的。

在ActivityThread.prefromLuncherActivity中,调用的Activity.attach,此方法中对PhoneWindow的Window.Callback进行了赋值,传入的是自己。

mWindow.setCallback( this)

因此,Window.Callback cb即是Activity,找到Activity.dispatchTouchEvent

publicbooleandispatchTouchEvent(MotionEvent ev)

if(ev.getAction == MotionEvent.ACTION_DOWN)

onUserInteraction。

ifgetWindow.superDispatchTouchEventev

returntrue。

returnonTouchEvent(ev)

此时事件怎么到达的Activity已经完全描述清楚,后续的流程我们就都知道了,不过我可以简单描述下。

以上方法中调用了getWindow.superDispatchTouchEvent(ev)

即是调用的PhoneWindow. superDispatchTouchEvent(ev)

PhoneWindow调用mDecor.superDispatchTouchEvent。

即是调用DecorView.superDispatchTouchEvent;

DecorView调用super.dispatchTouchEvent;

因为DecorView extends FrameLayout,FrameLayout extends ViewGroup,因此调用ViewGroup.dispatchTouchEvent

通过连带调用ViewGroup.dispatchTransformedTouchEvent 调用super.dispatchTouchEvent

因为ViewGroup extends View, 因此会调用View.dispatchTouchEvent事件又传递到了View,整个流程也就完整。

本文相关词条概念解析:

赋值

将某一数值赋给某个变量的过程,称为赋值。在计算机程序设计语言中,用一定的赋值语句去实现变量的赋值。赋值自W.克鲁尔于20世纪30年代初提出以后,赋值理论广泛应用于代数数论、类域论以及代数几何等方面;到了60年代,它又与泛函分析有着日益增长的关联。除Г本身外的所有孤立子群,按包含关系所成全序集的序型定义为Г的阶。赋值理论也可以从拓扑代数的角度来研究,是基于下述事实。对于有绝对值φ的域F,所有形如{α∈F|φ(α)

网友评论

相关文章