网上有很多关于事件分发的讲解,我也看过很多,但是看完后总是会只记得大概,于是就试试自己去看看源码。
如有错误,请及时提出
首先我们来分析下触摸事件传递流程:
我们手点屏幕---->屏幕硬件通过某种方式接受到相应信息---->将相应信息传递到活动---->传到decorview ---->向下分发
前两种属于硬件直接忽略,那事件是怎么传到活动的呢?不清楚,但是我们知道活动是通过ViewRootImpl关联decorview
所以如果要传到视图应该会通过viewRootImpl传递让我们先看看活动源码:
public boolean dispatchTouchEvent(MotionEvent ev){
if(ev.getAction()== MotionEvent.ACTION_DOWN){
onUserInteraction();
}
if(getWindow()。superDispatchTouchEvent(ev)){
返回true;
}
返回onTouchEvent(ev);
}
这段代码说明事件的分发和窗口有关,我们再看看getWindow:public Window getWindow(){
返回mWindow;
}
查看mWindow初始化:最终的无效附加(Context context,ActivityThread aThread,
Instrumentation instr,IBinder token,int ident,
应用程序应用程序,意图意图,ActivityInfo信息,
CharSequence标题,活动父项,字符串标识,
NonConfigurationInstances lastNonConfigurationInstances,
配置配置,字符串引用者,IVoiceInteractor voiceInteractor,
窗口窗口,ActivityConfigCallback activityConfigCallback){
attachBaseContext(上下文);
mFragments.attachHost(null / * parent * /);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
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());
}
}
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;
mWindow.setColorMode(info.colorMode);
}
上面代码可以发现是PhoneWindow://这是包含窗口装饰的窗口的顶层视图。
私人DecorView mDecor;
在这个类里看到这个属性,根据他的注释可以得知这是顶层视图,到此已经到了视图,而我们并没有找到怎么传递的。由于先创建窗口后有图,我们去看看WMS( WindowManagerService): // ------------------------------------------------ -------------
//输入事件和焦点管理
// ------------------------------------------------ -------------
final InputMonitor mInputMonitor = new InputMonitor(this);
进入InputMonitor类发现多处输入事件与服务里的输入管理有关联:private void updateInputDispatchModeLw(){
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled,mInputDispatchFrozen);
}
回头看看这个mIputManager是InputManagerService类: 最终的InputManagerService mInputManager;
再到InputManagerService类:public void start(){
Slog.i(TAG,“启动输入管理器”);
nativeStart(mPtr);
........
}
可以看到这里调用了底层代码,于是我在网上找了下:static void nativeStart(JNIEnv * env,jclass / * clazz * /,jlong ptr){
NativeInputManager * im = reinterpret_cast <NativeInputManager *>(ptr);
status_t result = im-> getInputManager() - > start();
if(result){
jniThrowRuntimeException(env,“输入管理器无法启动。”);
}
}
可以看到这里调用了getInputManager的启动方法:
status_t InputManager :: start(){
status_t result = mDispatcherThread-> run(“InputDispatcher”,PRIORITY_URGENT_DISPLAY);
if(result){
ALOGE(“由于错误%d,无法启动InputDispatcher线程”,result);
返回结果;
}
result = mReaderThread-> run(“InputReader”,PRIORITY_URGENT_DISPLAY);
if(result){
ALOGE(“由于错误%d,无法启动InputReader线程”,result);
mDispatcherThread-> requestExit();
返回结果;
}
返回OK;
}
可以看到这里开启了两个线程:mDispatcherThread和mReaderThread,可以根据字面意思看出前者用来分发,后者用来接受(查找了一番也是如此)//原生回调。
private void notifyConfigurationChanged(long当Nanos){
mWindowManagerCallbacks.notifyConfigurationChanged();
}
由此可知,这里是通过监听然后回调,至于怎么监听的有兴趣的同学可以自行研究下,接着往下看:/ *通知输入设备配置已更改。* /
@覆盖
public void notifyConfigurationChanged(){
// TODO(多显示器):通知与此输入设备关联的正确显示。
mService.sendNewConfiguration(DEFAULT_DISPLAY);
同步(mInputDevicesReadyMonitor){
if(!mInputDevicesReady){
mInputDevicesReady = true;
mInputDevicesReadyMonitor.notifyAll();
}
}
}
发现InputMonitor这个类里面实现此方法,根据注释可知输入设备配置发生改变时调用次方法,可以看到这里事件接受器刷新了。到此我们知道了事件的接受与分发,但他到底怎么传递到视图的呢这个时候我们回头看下ViewRootImpl类?
/ **
*我们有一个孩子
* /
public void setView(View view,WindowManager.LayoutParams attrs,View panelParentView){
同步(this){
if(mInputChannel!= null){
if(mInputQueueCallback!= null){
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
}
可以看到这里WindowinputEventReceiver就是事件接收器,进去看看:final类WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel,Looper looper){
超级(inputChannel,looper);
}
@覆盖
public void onInputEvent(InputEvent event){
enqueueInputEvent(event,this,0,true);
}
@覆盖
public void onBatchedInputEventPending(){
if(mUnbufferedInputDispatch){
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@覆盖
public void dispose(){
unscheduleConsumeBatchedInput();
super.dispose();
}
}
这里看下onInputEvent里面的方法: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();
}
}
可以看到这里接受了事件,如果立即处理调用doProcessInputEvents,否则调用scheduleProcessInputEvents 看下doProcessInputEvents:
void doProcessInputEvents(){
//传递队列中所有待处理的输入事件。
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);
}
// 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阶段;
if(q.shouldSendToSynthesizer()){
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme()?mFirstPostImeInputStage:mFirstInputStage;
}
if(stage!= null){
stage.deliver(Q);
} else {
finishInputEvent(Q);
}
}
可以看到这里通过InputStage来判断到底执行那个方法,而阶段所要赋的值在的setView方法中可以找到://设置输入管道。
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;
所以回调用提供方法:/ **
*提供要处理的事件。
* /
public final void deliver(QueuedInputEvent q){
((q.mFlags&QueuedInputEvent.FLAG_FINISHED)!= 0){
向前(Q);
} else if(shouldDropInputEvent(q)){
完成(q,错误);
} else {
适用(q,onProcess(q));
}
}
所以可以看到最终调用InputStage类中申请方法,在ViewRootImpl类中有很多实现了InputStage内部类,我们找到了ViewPostImeInputStage这个类:/ **
*将后期输入事件传递给视图层次结构。
* /
最终课程ViewPostImeInputStage扩展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();
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(处理指针事件(翻译)),processTrackballEvent(处理轨迹球事件(翻译))和processGenericMotionEvent这三个方法,根据方法名可知processGenericMotionEvent这个方法是处理一般手势:private int processGenericMotionEvent(QueuedInputEvent q){
最终的MotionEvent事件=(MotionEvent)q.mEvent;
//将事件传递给视图。
if(mView.dispatchGenericMotionEvent(event)){
返回FINISH_HANDLED;
}
回到FORWARD;
}
这里可以看出MView的调用了dispatchGenericMotionEvent,继续往下看:/ **
*派遣一般运动事件。
* <p>
*带源类的通用运动事件{@Link InputDevice#SOURCE_CLASS_POINTER}
*被传递到指针下的视图。所有其他通用运动事件都是
* delivered to the focused view. Hover events are handled specially and are delivered
* to {@link #onHoverEvent(MotionEvent)}.
* </p>
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
}
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_HOVER_ENTER
|| action == MotionEvent.ACTION_HOVER_MOVE
|| action == MotionEvent.ACTION_HOVER_EXIT) {
if(dispatchHoverEvent(event)){
返回true;
}
} else if(dispatchGenericPointerEvent(event)){
返回true;
}
} else if(dispatchGenericFocusedEvent(event)){
返回true;
}
if(dispatchGenericMotionEventInternal(event)){
返回true;
}
if(mInputEventConsistencyVerifier!= null){
mInputEventConsistencyVerifier.onUnhandledEvent(event,0);
}
返回false;
}
并没有发现我们所熟知的dispatchTouch方法,我们再去processPointerEvent方法里看看:private int processPointerEvent(QueuedInputEvent q){
最终的MotionEvent事件=(MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(事件);
maybeUpdateTooltip(事件);
mAttachInfo.mHandlingPointerEvent = false;
if(mAttachInfo.mUnbufferedDispatchRequested &&!!mUnbufferedInputDispatch){
mUnbufferedInputDispatch = true;
if(mConsumeBatchedInputScheduled){
scheduleConsumeBatchedInputImmediately();
}
}
退货处理?FINISH_HANDLED:FORWARD;
}
可以看到也是MView的调用了:/ **
*分派指针事件。
* <p>
*将触摸相关的指针事件分派给{@link #onTouchEvent(MotionEvent)}和全部
*其他事件{@link #onGenericMotionEvent(MotionEvent)}。这种关注的分离
*强化了{@link #onTouchEvent(MotionEvent)}真正关于触摸的不变性
*并且不应该期望处理其他指向设备功能。
* </ p>
*
* @param event要发送的运动事件。
* @return如果事件由视图处理,则返回true,否则返回false。
* @隐藏
* /
public final boolean dispatchPointerEvent(MotionEvent event){
if(event.isTouchEvent()){
返回dispatchTouchEvent(event);
} else {
返回dispatchGenericMotionEvent(event);
}
}
可以看到这里调用了dispatchTouchEvent,根据注释可以确定就是这里传递到视图的。(吐槽下翻译)其实其他事件也是通过类似的方法传递的,比如软键盘事件,有兴趣的可以在ViewRootImpl里寻找相应的InputStage
好了到此我们知道了事件是如何传递到view的了,下一篇继续
如有问题,请一定要告知