Android Input系统简介


Android Input系统事件获取与分发简单总结

1.Input系统的角色和重要性

Input是Android系统中的一个重要模块,它是负责处理用户输入操作的核心组件。该系统从各种输入设备获取原始输入事件,并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。当文档中详细解释了Input系统的角色和重要性,以及它的主要组成部分,包括InputReader、InputDispatcher等。同时,还简单介绍了输入事件的来源,包括硬件设备和软件模拟,以及不同类型的输入事件,如按键事件和触摸事件。

2.Input简介

Input是Android系统中的一个重要模块,它是负责处理用户输入操作的核心组件。该系统从各种输入设备获取原始输入事件,并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。当文档中详细解释了Input系统的角色和重要性,以及它的主要组成部分,包括InputReader、InputDispatcher等。同时,还简单介绍了输入事件的来源,包括硬件设备和软件模拟,以及不同类型的输入事件,如按键事件和触摸事件。

具体来说,InputManagerService通过以下几个方面实现了对用户输入事件的管理和处理:

  1. Input系统是Android系统中负责处理用户输入操作的核心组件,它负责从各种输入设备(如屏幕、键盘、鼠标等)获取原始的输入事件(如按键、触摸、滑动等),并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。
  2. Input系统对于提供流畅、灵敏和一致的用户交互体验至关重要,它需要在不同的设备、场景和应用中保证输入事件的正确性、及时性和安全性。
  3. 输入事件处理:InputManagerService会对接收到的输入事件进行预处理和解析,以便提取出有用的信息,并根据需要进行转换或修正。例如,对于触摸事件,它会计算出触摸点的坐标和压力值。
  4. 输入焦点管理:InputManagerService跟踪当前具有输入焦点的应用程序或窗口,并确保只有具有焦点的应用程序才能接收和处理输入事件。它还负责处理焦点的切换和转移。

3.Input系统的主要组成部分

类名功能位置
InputReader从EventHub读取事件,进行预处理和分类,生成Event对象,通过notify方法通知InputDispatcherNative
InputDispatcher接收InputReader数据,进行策略判断与分发,将事件分发给对应的窗口或应用Native
InputEventReceiver接收natvie层inputdispatcher数据,进一步分发java
View通过dispatchPointerEvent将事件分发给应用java
ViewGroup获取view状态并根据情况判断是否继续分发java
Activity点击事件处理java
  1. EventHub将所有的输入事件通过一个接口getEvents()把从多个输入设备节点中读取的事件交给InputReader,它是输入系统最底层。偏驱动侧。本文档不做过多介绍。
  2. InputReader是Android系统中的一个重要组件,用于接收用户输入事件并将其传递给应用程序。它负责从输入设备(如触摸屏、键盘、鼠标等)读取输入事件,并将其转换为Android系统能够理解的格式。
    InputReader通过调用底层输入设备驱动程序来读取输入事件,然后将其传递给InputDispatcher。InputDispatcher负责将输入事件分发给相应的应用程序或系统组件。
    InputReader还负责处理输入设备的各种参数设置,如采样率、灵敏度、校准等。它还能够识别多点触控、手势等高级输入事件,并将其传递给系统进行处理。
  3. InputDispatcher是Android系统中的一个关键组件,负责管理和分发用户输入事件。它接收来自InputReader的输入事件,并将其分发给相应的应用程序或系统组件进行处理。
    InputDispatcher会根据输入事件的类型和目标应用程序的焦点状态,将事件发送到正确的目标。例如,如果用户点击了屏幕上的一个按钮,InputDispatcher会将点击事件发送给当前具有焦点的应用程序,以触发相应的操作。
    InputDispatcher还负责处理多个应用程序同时请求焦点的情况,以及处理输入事件的优先级和顺序。它确保用户输入事件按照正确的顺序被处理,以确保系统的稳定性和响应性。
    总的来说,InputDispatcher在Android系统中扮演着重要的角色,协调和管理用户输入事件的传递,以确保系统能够准确、高效地响应用户操作。
  4. Java层各组件,InputEventReceiver是一个用于接收来自InputDispatcher(输入调度器)的输入事件的类。它的主要作用是接收原始输入事件,然后将其发送到指定的HandlerThread进行处理。

4.InputReader详解

4.1.InputReader时序图

请添加图片描述

4.2.InputReader流程分析

InputReader线程启动后,调用loopOnce方法

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
    //省略
	//通过getevent获取数据
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            processEventsLocked(mEventBuffer, count);//处理数据
        }

   		...
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    ...
	mQueuedListener->flush();//数据交给InputDispatcher线程

getEvent中主要就是扫描设备,获取设备节点,从节点中读取数据buffer,这块偏向驱动侧,此文档不过多介绍。

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        //验证数据有效性,通常情况不会出问题,有异常找驱动看数据问题
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {//输入事件的type肯定远小于0x10000000,满足条件
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//1






//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);//取出InputDevice  
    std::shared_ptr<InputDevice>& device = deviceIt->second;
    device->process(rawEvents, count);//1
}




//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
   
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (mDropUntilNextSync) {
            	//省略
            } else {
				//省略
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            //省略
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
}

找出InputDevice对应的mapper(之前在处理输入设备的时候,设置过mappers集合),调用这些mapper的process方法。对于单指摸屏,mapper为TouchInputMapper.

TouchInputMapper中所有上报的数据中,仅有id能代表手指,所有从驱动上报的数据均保存在PointerCoords和PointerProperties数组中,

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent) {
    //省略

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}



//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::sync(nsecs_t when) {
    const RawState* last =
            mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();

    // Push a new state.
    mRawStatesPending.emplace_back();//往mRawStatesPending的尾部插入一个空的RawState
	
    RawState* next = &mRawStatesPending.back();//将next指向mRawStatesPending的最后一个
    next->clear();
    next->when = when;

   //省略

    // Sync touch
    syncTouch(when, next);//写数据 RawPointerData

    // Assign pointer ids.
    if (!mHavePointerIds) {
        assignPointerIds(last, next);
    }
	
    processRawTouches(false /*timeout*/);//处理数据
}


//frameworks\native\services\inputflinger\reader\mapper\MultiTouchInputMapper.cpp
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
    size_t outCount = 0;
    BitSet32 newPointerIdBits;
    mHavePointerIds = true;

    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
        const MultiTouchMotionAccumulator::Slot* inSlot =
                mMultiTouchMotionAccumulator.getSlot(inIndex);//遍历取出Slot
        if (!inSlot->isInUse()) {
            continue;
        }
        //省略
        /*根据slot的数据,开始填充RawState*/
        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
        outPointer.x = inSlot->getX();
        outPointer.y = inSlot->getY();
        outPointer.pressure = inSlot->getPressure();
        outPointer.touchMajor = inSlot->getTouchMajor();
        outPointer.touchMinor = inSlot->getTouchMinor();
        outPointer.toolMajor = inSlot->getToolMajor();
        outPointer.toolMinor = inSlot->getToolMinor();
        outPointer.orientation = inSlot->getOrientation();
        outPointer.distance = inSlot->getDistance();
        outPointer.tiltX = 0;
        outPointer.tiltY = 0;

        outPointer.toolType = inSlot->getToolType();
        //省略
        /*开始处理id和index*/
        // Assign pointer id using tracking id if available.
        if (mHavePointerIds) {
            int32_t trackingId = inSlot->getTrackingId();
            int32_t id = -1;
            if (trackingId >= 0) {
                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
                    uint32_t n = idBits.clearFirstMarkedBit();
                    if (mPointerTrackingIdMap[n] == trackingId) {
                        id = n;
                    }
                }

                if (id < 0 && !mPointerIdBits.isFull()) {
                    id = mPointerIdBits.markFirstUnmarkedBit();
                    mPointerTrackingIdMap[id] = trackingId;
                }
            }
            if (id < 0) {
                mHavePointerIds = false;
                outState->rawPointerData.clearIdBits();
                newPointerIdBits.clear();
            } else {
                outPointer.id = id;
                outState->rawPointerData.idToIndex[id] = outCount;
                outState->rawPointerData.markIdBit(id, isHovering);
                newPointerIdBits.markBit(id);
            }
        }
        outCount += 1;
    }

    outState->rawPointerData.pointerCount = outCount;
    mPointerIdBits = newPointerIdBits;

    mMultiTouchMotionAccumulator.finishSync();//清空Slot
}

syncTouch 方法中主要做的就是数据填充,将从驱动获取的源数据加入到集合中。

继续回到sync方法,syncTouch处理完成后,调用processRawTouches继续处理数据

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::processRawTouches(bool timeout) {
    //省略
    const size_t N = mRawStatesPending.size();//前面已经填充过mRawStatesPending了,这里取出来
    size_t count;
    for (count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];//遍历取出RawState
		//省略
        mCurrentRawState.copyFrom(next);//将数据拷贝到mCurrentRawState中
        if (mCurrentRawState.when < mLastRawState.when) {
            mCurrentRawState.when = mLastRawState.when;
        }
        cookAndDispatch(mCurrentRawState.when);//数据处理及分发
    }
    if (count != 0) {
        mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);//处理完之后要擦除
    }
   //省略
    


//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
	mCurrentCookedState.clear();
	
	cookPointerData();//数据处理,一套算法将处理x,y轴计算成显示屏对应坐标
	if (mDeviceMode == DEVICE_MODE_POINTER) {
		//省略
	}else{
		if (!mCurrentMotionAborted) {
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);//分发
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }
	}
	//省略
	// Copy current touch to last touch in preparation for the next cycle.
    mLastRawState.copyFrom(mCurrentRawState);//将这次的数据拷贝到mLastRawState
    mLastCookedState.copyFrom(mCurrentCookedState);//拷贝数据
}

cookPointerData方法比较长,主要是对数据进行加工,加工后的数据放入mCurrentCookedState中。为什么要进行加工呢?因为当前的数据,比如X,Y坐标还是针对触摸屏的,我们要加工成显示屏上对应的坐标。处理完后,将数据放入mCurrentCookedState中。

dispatchTouches中主要调用dispatchMotion方法,将mCurrentCookedState等数据传递过去。用于数据分发。


void TouchInputMapper::dispatchMotion123(nsecs_t when, uint32_t policyFlags, uint32_t source,
                                      int32_t action, int32_t actionButton, int32_t flags,
                                      int32_t metaState, int32_t buttonState, int32_t edgeFlags,
                                      const PointerProperties* properties,
                                      const PointerCoords* coords, const uint32_t* idToIndex,
                                      BitSet32 idBits, int32_t changedId, float xPrecision,
                                      float yPrecision, nsecs_t downTime) {



       // ALOGE("houyangInputTouch---start!!!---dispatchMotion123---action:%d---changedId:%d", action,changedId);
       /*
            char propDoubelScreenTouched[100];
            int mDoubleTouchFlag = 1;
            property_get("persist.sys.clickscreen", propDoubelScreenTouched, 0);
            mDoubleTouchFlag = atoi(propDoubelScreenTouched);
        */

		//获取分屏属性
        char propVirturalDisplayStatus[100];
        int mVirtualDisplayFlag = 0;
        property_get("persist.sys.virtualdisplay", propVirturalDisplayStatus, 0);
        mVirtualDisplayFlag = atoi(propVirturalDisplayStatus);
		
		
        PointerCoords pointerCoords[MAX_POINTERS];
        PointerProperties pointerProperties[MAX_POINTERS];
        uint32_t pointerCount = 0;


        PointerCoords display2PointerCoords[MAX_POINTERS];
        PointerProperties display2PointerProperties[MAX_POINTERS];
        uint32_t display2PointerCount = 0;
        int32_t display2Action = -1;
        uint32_t touchId = -1;
        //changeId: 区分当前事件类型,upid,downid,moveid。 默认为-1,从cook中传递下来
        //index 代表集中中位置
        //display2PointerProperties 原始数据,需要通过notify方法传递至InputDispatcher
       	//display2PointerCoords 处理后数据
       	//action,事件类型
        while (!idBits.isEmpty()) {
            uint32_t id = idBits.clearFirstMarkedBit();
            uint32_t index = idToIndex[id];
            ALOGE("houyangInputTouch---dispatchMotion123---index:%d---id:%d---changedId:%d", index, id, changedId);
            //ALOGE("houyangInputTouch---dispatchMotion123---mVirtualDisplayFlag:%d---postionXXX:%f", mVirtualDisplayFlag, coords[index].getAxisValue(AMOTION_EVENT_AXIS_X));
            if (mVirtualDisplayFlag == 1 && coords[index].getAxisValue(AMOTION_EVENT_AXIS_X) > VIRTUALDISPLAY_TOUCH_COORDINATE_U6X) {
                //ALOGE("houyangInputTouch---enter display2 touch judge");
                display2PointerProperties[display2PointerCount].copyFrom(properties[index]);
                display2PointerCoords[display2PointerCount].copyFrom(coords[index]);
                display2PointerCoords[display2PointerCount].setAxisValue(AMOTION_EVENT_AXIS_X, coords[index].getX() - VIRTUALDISPLAY_TOUCH_COORDINATE_U6X);

                display2Action = action;
                if (changedId >= 0 && id == uint32_t(changedId)) {
                	touchId = 2;
                    display2Action |= display2PointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
                }

                display2PointerCount += 1;
            } else {
                //ALOGE("houyangInputTouch---enter display0 touch judge");
                pointerProperties[pointerCount].copyFrom(properties[index]);
                pointerCoords[pointerCount].copyFrom(coords[index]);

                if (changedId >= 0 && id == uint32_t(changedId)) {
                	touchId = 0;
                    action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
                }
                pointerCount += 1;

            }
        }


        ALOG_ASSERT(pointerCount != 0);
        //ALOGE("houyangInputTouch---dispatchMotion123---before_touchId:%d", touchId);
        //ALOGE("houyangInputTouch---dispatchMotion123---before_dispay0_action000:%d,pointerCount:%d", action, pointerCount);
        //ALOGE("houyangInputTouch---dispatchMotion123---before_dispay2_display2Action222:%d,display2PointerCount:%d", 		display2Action, display2PointerCount);
        
        if (mVirtualDisplayFlag == 1) {
         if (changedId >= 0 && display2PointerCount == 1 && touchId == 2) { //分屏模式下处理副屏单指
                    if (display2Action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
                        display2Action = AMOTION_EVENT_ACTION_DOWN;
                    } else if (display2Action == AMOTION_EVENT_ACTION_POINTER_UP) {
                        display2Action = AMOTION_EVENT_ACTION_UP;
                    } else {
                        // Can't happen.
                        ALOG_ASSERT(false);
                    }
                } else if (changedId >= 0 && pointerCount == 1 && touchId == 0) {//分屏模式下处理主屏单指
                if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
                                action = AMOTION_EVENT_ACTION_DOWN;
                            } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
                                action = AMOTION_EVENT_ACTION_UP;
                            } else {
                                // Can't happen.
                                ALOG_ASSERT(false);
                            }
                }
        } else {
        if (changedId >= 0 && pointerCount == 1) { //非分屏模式下正常处理
                    // Replace initial down and final up action.
                    // We can compare the action without masking off the changed pointer index
                    // because we know the index is 0.
                    if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
                        action = AMOTION_EVENT_ACTION_DOWN;
                    } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
                        action = AMOTION_EVENT_ACTION_UP;
                    } else {
                        // Can't happen.
                        ALOG_ASSERT(false);
                    }
                }
        }


        //ALOGE("houyangInputTouch---dispatchMotion123---after_dispay0_action000:%d,pointerCount:%d", action, pointerCount);
        //ALOGE("houyangInputTouch---dispatchMotion123---after_dispay2_display2Action222:%d,display2PointerCount:%d", display2Action, display2PointerCount);

        float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
        float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
        if (mDeviceMode == DEVICE_MODE_POINTER) {
            mPointerController -> getPosition( & xCursorPosition, &yCursorPosition);
        }
        const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
        const int32_t deviceId = getDeviceId();
        std::vector < TouchVideoFrame > frames = getDeviceContext().getVideoFrames();
        std::for_each (frames.begin(), frames.end(),
                  [this](TouchVideoFrame & frame) {frame.rotate(this->mSurfaceOrientation); });

        //ALOGE("houyangInputTouch---display0PointerCount:%d---display2PointerCount:%d", pointerCount, display2PointerCount);

        //ALOGE("houyangInputTouch---finish_action---display0Action:%d---display2Action:%d----pointerCount:%d---display2PointerCount:%d",
//        action, display2Action, pointerCount, display2PointerCount);

/*
        //ka dun!!!!  remove it after java  screenTouchNum received ,now java cant receive screenTouchNum
        if (pointerCount > 0 && display2PointerCount > 0 && mDoubleTouchFlag != 2) {
        property_set("persist.sys.clickscreen", "2");
        } else if ((pointerCount == 0 || display2PointerCount == 0) && mDoubleTouchFlag != 1){
        property_set("persist.sys.clickscreen", "1");
        }

*/
		//处理屏幕数量
         int32_t screenTouchNum = 1;
         if (pointerCount > 0 && display2PointerCount > 0) {
                screenTouchNum =2 ;
                }

        //ALOGE("houyangInputTouch---finish_action---moreScreenTouched:%d---screenTouchNum:%d",moreScreenTouched,screenTouchNum);
        if (pointerCount > 0) {
        NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
                action, actionButton, flags, metaState, buttonState,
                MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                downTime, std::move(frames), screenTouchNum);
                //ALOGE("hhj---RotaryEncoderInputMapper---setValue---main1");

		//主屏数据传递至InputDispatcher
        getListener()->notifyMotion(&args);
        }
        if (display2PointerCount > 0) {
            NotifyMotionArgs display2Args (getContext()->
            getNextId(), when, deviceId, source, DISPLAYID_VIRTUALDISPLAY, policyFlags,
                    display2Action, actionButton, flags, metaState, buttonState,
                    MotionClassification::NONE, edgeFlags, display2PointerCount, display2PointerProperties,
                    display2PointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                    downTime, std::move (frames), screenTouchNum);
                    //ALOGE("hhj---RotaryEncoderInputMapper---setValue---main2");
                    //副屏数据传递至InputDispatcher
            getListener()->notifyMotion( & display2Args);
        }

    }

  1. 通过idBits 获取id. 通过id获取index,通过index获取集合中数据,后续通过notifymotion方法传递至InputDispatcher.
  2. 分屏模式下,主屏逻辑与副屏逻辑单独处理,创建两套完全独立的数据集合,并分别传递至InputDispatcher

4.3.InputReader简要概括总结

InputReader模块主要负责从节点文件中读取输入数据,并在TouchInputMapper中对数据进行处理后通过notifymotion方法数据交给InputDispatcher线程。

InputReader线程主要完成以下工作:

1.处理已有的输入设备
2.处理新增或者移除的输入设备
3.对输入设备产生的输入数据进行处理

5.InputDispatcher详解

5.1.InputDispatcher时序图

请添加图片描述

5.2.InputDispatcher简要流程图

在这里插入图片描述

5.3.InputDispatcher流程分析

dispatchOnce流程
在这里插入图片描述

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();
        //通过dispatchOnceInnerLocked进行输入事件分发,传出参数nextWakeupTime决定下次派发循环的时间点
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        //执行命令队列中的命令
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;//设置nextWakeupTime为立即开始执行下次线程循环
        }
    } // release lock
    // 计算需要休眠的时间timeoutMillis,并通过pollOnce进入epoll_wait
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

  1. 判断commandQueue中是否存在数据
  2. 通过dispatchOnceInnerLocked进行输入事件分发,传出参数nextWakeupTime决定下次派发循环的时间点

dispatchOnceInnerLocked流程


void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
    //当分发被冻结,则不再处理超时和分发事件的工作,
    //setInputDispatchMode可以使InputDispatcher在禁用、冻结、正常状态切换
    if (mDispatchFrozen) {
		if (DEBUG_FOCUS) {
            ALOGD("Dispatch frozen.  Waiting some more.");
        }
        return;
    }
    //优化app切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件
    //如果isAppSwitchDue为true,说明没有及时响应HOME键等操作
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }
    //如果还没有待分发的事件,去mInboundQueue中取出一个事件
    if (! mPendingEvent) {
         //如果mInboundQueue为空,并且没有待分发的事件,就return
        if (mInboundQueue.isEmpty()) {
            ...
        } else {
            //如果mInboundQueue不为空,取队列头部的EventEntry赋值给mPendingEvent
            mPendingEvent = mInboundQueue.dequeueAtHead();
        }
        //重置ANR信息.
        resetANRTimeoutsLocked();
    }
	//检查事件是否需要丢弃,dropReason描述了丢弃原因
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
    	//在事件派发前调用过interceptInputEvent并设置POLICY_FLAG_PASS_TO_USER,则丢弃
        dropReason = DROP_REASON_POLICY;
    } else if (!mDispatchEnabled) {
    	//如果InputDispatcher被禁用,通过setInputDispatchMode设置,则此事件也会被丢弃
        dropReason = DROP_REASON_DISABLED;
    }  ...
    
    switch (mPendingEvent->type) {
   ...
    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        ...
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
	case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        //事件因为home键没有能被及时响应丢弃
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        //事件因为过期丢弃
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEvent(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        //事件因为阻碍了其他窗口获得事件丢弃
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        //执行dispatchMotionLocked进行Motion事件的派发,如果派发完成,无论成功派发还是事件被丢弃,都会返回true
        //失败则返回false,在下次循环时再次尝试此事件派发
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
    ...
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;
        //设置mPendingEvent对象为null,使在下次循环时可以处理派发队列中的下一条事件
        releasePendingEventLocked();
        //使得InputDispatcher能够快速处理下一个分发事件
        //因为当派发队列为空时,派发线程可能需要在下次循环中生成重复按键事件,因此不能直接进入休眠
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

//设置mDispatchEnabled 和mDispatchFrozen 的方法。
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
    if (DEBUG_FOCUS) {
        ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
    }
    bool changed;
    { // acquire lock
        std::scoped_lock _l(mLock);

        if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
            if (mDispatchFrozen && !frozen) {
                resetNoFocusedWindowTimeoutLocked();
            }

            if (mDispatchEnabled && !enabled) {
                resetAndDropEverythingLocked("dispatcher is being disabled");
            }

            mDispatchEnabled = enabled;
            mDispatchFrozen = frozen;
            changed = true;
        } else {
            changed = false;
        }

        if (DEBUG_FOCUS) {
            logDispatchStateLocked();
        }
    } // release lock

    if (changed) {
        // Wake up poll loop since it may need to make new input dispatching choices.
        mLooper->wake();
    }
}
  1. 如果派发队列为空,则不会继续派发操作
  2. InputDispatcher的冻结处理
    如果当前InputDispatcher被冻结,则不进行派发操作,InputDispatcher有三种状态,分别是正常状态、冻结状态(frozen)和禁用状态(disable),可以通过InputDispatcher的setInputDispatchMode函数来设置
  3. 窗口切换操作处理
    mAppSwitchDueTime ,代表了App最近发生窗口切换操作时(比如按下Home键、挂断电话),该操作事件最迟的分发时间,当事件分发的时间点距离该事件加入mInboundQueue的时间超过500ms,则认为app切换过期,即isAppSwitchDue=true。
    如果mAppSwitchDueTime小于nextWakeupTime(下一次InputDispatcherThread醒来的时间),就将mAppSwitchDueTime赋值给nextWakeupTime,这样当InputDispatcher处理完分发事件后,会第一时间处理窗口切换操作
  4. 取出事件
    如果没有待分发的事件,就从mInboundQueue中取出一个事件,如果mInboundQueue为空,并且没有待分发的事件,就return,如果mInboundQueue不为空,取队列头部的EventEntry赋值给mPendingEvent,mPendingEvent的类型为EventEntry对象指针
  5. 事件丢弃
    dropReason代表了事件丢弃的原因,它的默认值为DROP_REASON_NOT_DROPPED,代表事件不被丢弃。
    根据mPendingEvent的type做区分处理,这里主要截取了对Motion类型的处理。经过过滤,会调用dispatchMotionLocked函数为这个事件寻找合适的窗口,即继续分发操作。
  6. 后续处理
    执行dispatchMotionLocked进行Motion事件的派发,如果派发完成,无论成功派发还是事件被丢弃,都会返回true,否则返回false,在下次循环时再次尝试此事件派发。如果dispatchMotionLocked事件分发成功,则会调用releasePendingEventLocked函数,其内部会将mPendingEvent的值设置为Null,并将mPendingEvent指向的对象内存释放掉。将nextWakeupTime的值设置为LONG_LONG_MIN,这是为了让InputDispatcher能够快速处理下一个分发事件

dispatchMotionLocked流程

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    //标记事件已经正式进入派发流程
    if (! entry->dispatchInProgress) {
        entry->dispatchInProgress = true;
    }
    //如果事件是需要丢弃的,则返回true,不会去为该事件寻找合适的窗口
    if (*dropReason != DROP_REASON_NOT_DROPPED) {
        setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
        return true;
    }
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
    //目标窗口信息列表会存储在inputTargets中
    std::vector<InputTarget> inputTargets;
    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {
        //处理点击形式的事件,比如触摸屏幕
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        //处理非触摸形式的事件
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    //输入事件被挂起,说明找到了窗口并且窗口无响应
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }
    setInjectionResult(entry, injectionResult);
     //返回值不为SUCCEEDED,表示无法找到合适窗口,例如窗口未获取焦点,或点击位置没落在任何一个窗口内,此事件将被直接丢弃
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
       ...
        return true;
    }
    //分发目标添加到inputTargets列表中
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
	...
    //将事件派发给inputTargets列表中的目标
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
  1. 对于被丢弃的事件直接返回true。
  2. 给事件寻找合适的目标窗口,目标窗口分为普通窗口和监听窗口(monitoring),普通窗口通过按点查找与按焦点查找两种方式获得,而监听窗口则无条件监听所有输入事件。普通窗口的查找结果决定了此次线程循环是否可以完成事件派发
  3. 如果成功找到可以接收事件的目标窗口,则调用dispatchEventLocked()函数进行派发
  4. 查找到的派发目标存储在InputTarget结构体中

findTouchedWindowTargetsLocked流程

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,bool* outConflictingPointerActions) {
           // For security reasons, we defer updating the touch state until we are sure that
    // event injection will be allowed.
    int32_t displayId = entry.displayId;
    int32_t action = entry.action;
    //点击类型,down/up/point_down/point_up/move
    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
    //ALOGE("findTouchWindowTrgetsLocked---displayId:%d   action:%d    maskedAction:%d",displayId,action,maskedAction);
    // Update the touch state as needed based on the properties of the touch event.
    int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
    InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
    sp<InputWindowHandle> newHoverWindowHandle;

    // Copy current touch state into tempTouchState.
    // This state will be used to update mTouchStatesByDisplay at the end of this function.
    // If no state for the specified display exists, then our initial state will be empty.
    const TouchState* oldState = nullptr;
    //所有的属性都会保存到此变量中,后续会通过其中的属性做逻辑判断
    TouchState tempTouchState;
    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
            mTouchStatesByDisplay.find(displayId);

    if (oldStateIt != mTouchStatesByDisplay.end()) {
        oldState = &(oldStateIt->second);
        tempTouchState.copyFrom(*oldState);

        //TouchState& state = oldStateIt->second;
        //int a = static_cast<int>(state.windows.size());
        // ALOGE("state.windows.size:%d",a);
        // ALOGE("state.windows.displayId:%d---down:%d",state.displayId,state.down);
    }
        ...
   if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
		// 从MotionEntry中获取事件坐标点
        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        int32_t x = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_X));
        int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_Y));
        bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        //此处为获取当前触摸window!
        sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
                displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
        ...
        //把选中的窗口保存到TempTouchState中,以便后续处理
        mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
        ...
   }
	...
	//遍历当前temptouchstate中的window.
        bool haveForegroundWindow = false;
        for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
			//是否是前台
            if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
          //  ALOGE("houyangInputDispatcher---InputDispatcher----targetFlags:%d",touchedWindow.targetFlags);
                haveForegroundWindow = true;
                //权限判断
                if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
                    injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
                    injectionPermission = INJECTION_PERMISSION_DENIED;
               //     ALOGE("GOFail---1");
                    goto Failed;
                }
            }else {
         //       ALOGE("houyangInputDispatcher---InputDispatcher----targetFlags not null");
            }
        }
        //int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
         //           int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));

       // ALOGE("houyangInputDispatcher---haveForegroundWindow:%d---hasGestureMonitor:%d",haveForegroundWindow,hasGestureMonitor);
        //        ALOGE("houyangInputDispatcher---x:%d---y:%d",x,y);
		//若没有前台window
        if (!haveForegroundWindow) {
            ALOGI("Dropping event because there is no touched foreground window in display "
                  "%" PRId32 " or gesture monitor to receive it123321.",
                  displayId);
            injectionResult = INPUT_EVENT_INJECTION_FAILED;
           // ALOGE("GOFail---2");
            goto Failed;
        }
    ...
    //如果执行到这里,说明窗口的查找过程一切顺利,设置injectionResult为SUCCEEDED,并将injectionResult放入参数inputTargets中
    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
 
 	//更新inputTargets
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, inputTargets);
    }

    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
        addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                                  touchedMonitor.yOffset, inputTargets);
    }
    ...
    if (!wrongDevice) {
			//phase v 多屏添加,获取touchinputmapper中设置的属性值
            //ALOGE("InputDispatcher---wrongDevice---moreScreenTouched:%d",entry.moreScreenTouched);
            int32_t mMoreScreenTouched = entry.moreScreenTouched;
            char propClickStatus[100];
            int mClickFlag = 0;
            property_get("persist.sys.clickscreen", propClickStatus, 0);
            mClickFlag = atoi(propClickStatus);
        //ALOGE("tempTouchState---mClickFlag:%d",mClickFlag);
        if (switchedDevice) {
            if (DEBUG_FOCUS) {
                ALOGD("Conflicting pointer actions: Switched to a different device.");
            }
            *outConflictingPointerActions = true;
        }
//ALOGE("tempTouchState---!isHoverAction:%d---maskedAction:%d",!isHoverAction,maskedAction);
			//类型
		 if (maskedAction == AMOTION_EVENT_ACTION_UP ||
                   maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
            // All pointers up or canceled.
            //初始化
            tempTouchState.reset();
        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
            // First pointer went down.
            if (oldState && oldState->down) {
                if (DEBUG_FOCUS) {
                    ALOGD("Conflicting pointer actions: Down received while already down.");
                }
                *outConflictingPointerActions = true;
            }
        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && mMoreScreenTouched != 2) {
            // One pointer went up.
            if (isSplit) {
                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;

//add if judge
//if(pointerId!=0 && pointerIndex!=0){
            for (size_t i = 0; i < tempTouchState.windows.size();) {
                                TouchedWindow& touchedWindow = tempTouchState.windows[i];
            //int position = static_cast<int>(i);
            //ALOGE("tempTouchState---Touched---position:%d",position);
            //ALOGE("tempTouchState---Touched window was removed: %s in pointerId %d  pointerIndex %d    displayId %" PRId32,
             //                                                         touchedWindow.windowHandle->getName().c_str(), pointerId
             //                                                         ,pointerIndex,displayId);
                                if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
                                //ALOGE("tempTouchState---for---1111---clearBit-pointerId:%d   tempTouchState.windows.size:%d",pointerId,static_cast<int>(tempTouchState.windows.size()));
                                    touchedWindow.pointerIds.clearBit(pointerId);
                                    if (touchedWindow.pointerIds.isEmpty()) {
                                    ALOGE("tempTouchState---for---eraseeraseerase!!!");
                                    //清空此window.将不会再往该window上分发
                                        tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
                                        continue;
                                    } else {
                                    //ALOGE("tempTouchState---else---222");
                                    }
                                }else {
                                //ALOGE("tempTouchState---else---111");
                                }
                                i += 1;
                            }
//}
            }
        }
    }
    return injectionResult;
}

该函数主要目的用于获取所有窗口信息,为查找输入窗口提供依据,因此每当窗口形态发生变化是,WMS会通过IMS将所有窗口提交到InputDispatcher,完成InputWindowHandle的更新。(更新窗口为WMS内容,此块不过分涉及)

  1. 根据窗口点击坐标和事件发生坐标选择合适的目标窗口
  2. 获取前台window,若无前台window则返回异常injectionResult
  3. 根据点击类型部分选择性清空tempTouchState

dispatchEventLocked流程


void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
                                         // ALOGE("dispatchEventToCurrentInputTargets----start");
    ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif
    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
    for (const InputTarget& inputTarget : inputTargets) {
    //遍历当前inputTargets
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
        //分发
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        } else {
            if (DEBUG_FOCUS) {
                ALOGD("Dropping event delivery to target with channel '%s' because it "
                      "is no longer registered with the input dispatcher.",
                      inputTarget.inputChannel->getName().c_str());
            }
        }
    }
}

  1. 遍历InputTargets
  2. 根据inputTarget内部的inputChannel来获取Connection的索引,再根据这个索引作为Key值来获取mConnectionsByFd容器中的Connection。Connection可以理解为InputDispatcher和目标窗口的连接,其内部包含了连接的状态、InputChannel。 InputDispatcher与java层通信就是通过InputChannel。
  3. 调用prepareDispatchCycleLocked函数根据当前的inputTarget,开始事件发送循环。最终会通过inputTarget中的inputChannel来和窗口进行进程间通信,最终将Motion事件发送给目标窗口。
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                 EventEntry* eventEntry,
                                                 const InputTarget& inputTarget,
                                                 int32_t dispatchMode) {
...

    // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
    // different EventEntry than what was passed in.
    EventEntry* newEntry = dispatchEntry->eventEntry;
    // Apply target flags and update the connection's input state.
    switch (newEntry->type) {
        case EventEntry::Type::MOTION: {
            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
            // Assign a default value to dispatchEntry that will never be generated by InputReader,
            // and assign a InputDispatcher value if it doesn't change in the if-else chain below.
            constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
                    static_cast<int32_t>(IdGenerator::Source::OTHER);
            dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
            if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
            } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
            } else {
                dispatchEntry->resolvedAction = motionEntry.action;
                dispatchEntry->resolvedEventId = motionEntry.id;
            }
            if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
                !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
                                                   motionEntry.displayId)) {
#if DEBUG_DISPATCH_CYCLE
                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
                      "event",
                      connection->getInputChannelName().c_str());
#endif
                dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
            }

            dispatchEntry->resolvedFlags = motionEntry.flags;
            if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
                dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
            }
            if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
                dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
            }
          ...   
    // Enqueue the dispatch entry.
    connection->outboundQueue.push_back(dispatchEntry.release());
    traceOutboundQueueLength(connection);
}

prepareDispatchCycleLocked没有太多内容,调用enqueueDispatchEntryLocked继续事件分发,将InputDispatcher中mInboundQueue中的事件取出后, 找到目标window后,封装dispatchEntry加入到connection的outbound队列。


void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {

    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
     // 从outboundQueue 队列中取出 DispatchEntry
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        dispatchEntry->deliveryTime = currentTime;
        const nsecs_t timeout =
                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
        dispatchEntry->timeoutTime = currentTime + timeout;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
           
            case EventEntry::Type::MOTION: {
                MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);

                PointerCoords scaledCoords[MAX_POINTERS];
                const PointerCoords* usingCoords = motionEntry->pointerCoords;

                // Set the X and Y offset and X and Y scale depending on the input source.
                float xOffset = 0.0f, yOffset = 0.0f;
                float xScale = 1.0f, yScale = 1.0f;
                if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
                    !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                    float globalScaleFactor = dispatchEntry->globalScaleFactor;
                    xScale = dispatchEntry->windowXScale;
                    yScale = dispatchEntry->windowYScale;
                    xOffset = dispatchEntry->xOffset * xScale;
                    yOffset = dispatchEntry->yOffset * yScale;
                std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
                //ALOGE("houyangInputDispatcher---inputPublisher---moreScreenTouched:%d",motionEntry->moreScreenTouched);
                // Publish the motion event.
                //发布事件
                status = connection->inputPublisher
                                 .publishMotionEvent(dispatchEntry->seq,
                                                     dispatchEntry->resolvedEventId,
                                                     motionEntry->deviceId, motionEntry->source,
                                                     motionEntry->displayId, std::move(hmac),
                                                     dispatchEntry->resolvedAction,
                                                     motionEntry->actionButton,
                                                     dispatchEntry->resolvedFlags,
                                                     motionEntry->edgeFlags, motionEntry->metaState,
                                                     motionEntry->buttonState,
                                                     motionEntry->classification, xScale, yScale,
                                                     xOffset, yOffset, motionEntry->xPrecision,
                                                     motionEntry->yPrecision,
                                                     motionEntry->xCursorPosition,
                                                     motionEntry->yCursorPosition,
                                                     motionEntry->downTime, motionEntry->eventTime,
                                                     motionEntry->pointerCount,
                                                     motionEntry->pointerProperties, usingCoords,
                                                     motionEntry->moreScreenTouched);
                reportTouchEventForStatistics(*motionEntry);
                break;
            }      
        }
		 // 将事件从outboundQueue中移除
        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                                    connection->outboundQueue.end(),
                                                    dispatchEntry));
        traceOutboundQueueLength(connection);
        // 在waitQueue 尾部重新插入
        connection->waitQueue.push_back(dispatchEntry);
    }
}


  1. 遍历在enqueueDispatchEntryLocked方法中设置的outboundQueue。
  2. 清除outboundQueue中已经处理的数据。
  3. 将数据加入到waitQueue.

该方法主要通过connection 发布最终的事件。
至此,InputDispatcher完成事件的发布,并且将发布的事件保存在connection的waitQueue中。

status_t InputPublisher::publishMotionEvent(
             uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId ..) {
    ....
    InputMessage msg;
    // 设置event的参数
    msg.header.type = InputMessage::Type::MOTION;
    msg.body.motion.action = action;
    ....
    // 调用InputChannel的sendMessage方法
    return mChannel->sendMessage(&msg);
}



status_t InputChannel::sendMessage(const InputMessage* msg) {
    ....
    ssize_t nWrite;
    do {
        nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    ....
    return OK;
}


>frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
       if ((mWindowAttributes.inputFeatures
               & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
          mInputChannel = new InputChannel();
       }
       try {
        mOrigWindowType = mWindowAttributes.type;
        mAttachInfo.mRecomputeGlobalAttributes = true;
        collectViewAttributes();
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // 将mInputChannel 注册到WMS中
       } catch (RemoteException e) {
         ...
       }
       if (mInputChannel != null) {
          if (mInputQueueCallback != null) {
              mInputQueue = new InputQueue();
              mInputQueueCallback.onInputQueueCreated(mInputQueue);
          }
          // 创建app端监听,即WindowInputEventReceiver 作为事件的接收端
          mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                                             Looper.myLooper());
        }
 }

5.4.InputDispatcher简要概括总结

总结:

InputReader把input event放入mInboundQueue(NotifiyMotionArgs转换为MotionEntry,添加到队尾)。
InputDispatcherThread被唤醒后,通过InputDispatcher主要任务就是找到对应的window,并建立进程间通信,把input event传递过去。其实简单来说,InputDispatcher就是负责消费mInboundQueue中的事件,并将数据转换处理后通过publishMotionEvent方法将事件发送到java层

整个InputDispatcher中涉及到3中queue.

  1. InputDispatcher的mInboundQueue:存储的是从InputReader 送来的输入事件。
  2. Connection的outboundQueue:该队列是存储即将要发送给应用的输入事件。
  3. Connection的waitQueue:队列存储的是已经发给应用的事件,但是应用还未处理完成的。
  1. 通过dispatchOnceInnerLocked(),取出mInboundQueue 里面的 EventEntry事件
  2. 通过enqueueDispatchEntryLocked(),生成事件DispatchEntry并加入connection的outbound队列。
  3. 通过startDispatchCycleLocked(),从outboundQueue中取出事件DispatchEntry, 重新放入connection的waitQueue队列。
  4. 在startDispatchCycleLocked()里面,通过inputPublisher.publishMotionEvent() 方法将按键事件分发给java层。 publishMotionEvent的实现是在InputTransport.cpp 中

此WMS中会在setView会绑定注册InputEventReceiver ,用于接收InputDispatcher sendmsg发送的数据。具体bind过程涉及WMS,文档不过多介绍。·bind过程中会调用inputchannel去setFd,而sendMessage 中通过getFdfa方法来获取是发送到那一个window上。至此,所有native层流程结束。

6.Java层部分流程

6.1 Java层时序图

请添加图片描述

6.2 Java层input流程图

在这里插入图片描述

当我们触摸屏幕后,会有各种事件产生,我们可以做相应的处理,那么我们事件是怎么传递到我们的对应的控件的呢?

1 . 触摸屏幕后,我们的事件会传递到当前的 Activity 的 dispatchTouchEvent 方法中
2. Activity的dispatchTouchEvent把事件分发到 ViewGrop 的 dispatchTouchEvent 方法中
3. ViewGrop 的 dispatchTouchEvent方法开始分发给子view
4. 如果ViewGrop处理没有消费事件,事件将在最后传递回Activity的onTouchEvent中

在这里插入图片描述

Activity、ViewGroup、View的关系:

//todo 图

6.3 Activity、ViewGroup、View 流程分析

一个点击事件产生后,传递顺序是:Activity(Window) -> ViewGroup -> View

Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent()—->View的dispatchTouchEvent
—->View的onTouchEvent()—->ViewGrop的onTouchEvent()—->Activity的onTouchEvent()


//Activity
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        //是一个空方法
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }

final void attach(...) {
    ...
    mWindow = new PhoneWindow(this);
   . ...
}

onUserInteraction是一个空方法,可以重写这个方法,如果有特殊的业务需求,可以在事件分发给window之前做一些逻辑处理。
getWindow()方法获取的是一个PhoneWindow对象。 在attach中赋值

  1. 当触摸屏幕以后,事件会传递的 Activity 的 dispatchTouEvent方法中
  2. 判断当前的动作是不是 down,如果是down,那么我们开始调用 onUserInteraction()
  3. 调用 Window的superDispatchTouchEvent(),如果 Window 消费了事件,那么就直接返回true,代表事件已经消费,如果 Window 没有消费事件,那么返会false,如果ViewGrop处理没有消费事件,事件将传递到Activity的onTouchEvent中
  4. 上面一步如果放回了 false,那么就会调用 Activity的 onTouchEvent方法,如果onTouchEvent返会true表示事件被消费,返会false表示事件没被消费,那么将丢弃事件
> frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

> frameworks/base/core/java/com/android/internal/policy/DecorView.java
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

superDispatchTouchEvent方法只是调用DecorView的superDispatchTouchEvent,superDispatchTouchEvent 方法直接调用ViewGroup 的 dispatchTouchEvent
我们发现,最终这个方法调用的是 ViewGrop 的dispatchTouchEvent方法,接着就是用 ViewGrop的dispatchTouEvent方法分发事件了。
Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent()
至此为止,我们看了 Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent() 的过程。
如果ViewGrop没有消费事件,那么我们的事件回传到Activity的onTouchEvent中

>ViewGroup.java 第一段
public boolean dispatchTouchEvent(MotionEvent ev) {

			

			//这个值,用于作为返回,目的是为了标志事件有没有被拦截
        	boolean handled = false;
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                //清除firsttarget,丢弃之前全部状态,相当于一个新的开始
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // Check for interception.
            //开始检查是否需要 拦截
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                    //获取当前的事件是否 不允许拦截 (true不允许)
                    
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                	//事件被拦截了,那么把事件分发到拦截的方法中去
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }
...
        return handled;
    }

ViewGroup的dispatchTouchEvent中做了以下几个操作

  1. 首先会判断事件是否是为 ACTION_DOWN 事件,如果是,则进行初始化。这里为什么要进行初始化呢?这是因为一个完整的事件序列是以 DOWN 开始,以 UP 结束的。所以,如果是 DOWN 事件,说明这是一个新的事件序列,故而需要初始化之前的状态。在注释 1 处,如果条件满足,则执行下面的代码,mFirstTouchTarget 的意义是,当前 ViewGroup 是否拦截了此事件,如果拦截了,mFirstTouchTarget == null,如果没有拦截则交给子 View 来处理,mFirstTouchTarget != null。假设当前的 ViewGroup 拦截了此事件,mFirstTouchTarget == null 为 false,如果这时触发 ACTION_DOWN 事件,则会执行 onInterceptTouchEvent(ev) 方法;如果触发的是 ACTION_MOVE、ACTION_UP 事件,则不再执行 onInterceptTouchEvent(ev) ,而是直接设置 intercepted = true ,此后的一个时间序列均由这个 ViewGroup 处理。
  2. FLAG_DISALLOW_INTERCEPT 标志位,它主要是禁止 ViewGroup 拦截除了 DOWN 之外的事件,一般通过子 View.requestDisallowInterceptTouchEvent(…) 来设置。

所以,总结一下就是,当 ViewGroup 要拦截事件的时候,那么后续的事件序列都交给它处理,而不用再调用 onInterceptTouchEvent(…) 方法了。所以 onInterceptTouchEvent(…) 方法并不是每次事件都会调用的。

>ViewGroup.java 第二段
>
final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x =
                                isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
                                //相对于父布局的x,y
                        final float y =
                                isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        遍历所有的子view,寻找到最合适的view,把事件分发下去
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);
                                     //判断当前的循环到的子view是否能够接受事件,和当前的点是否所在此子view上
                        //只要此子view不能接受事件或者点没有落在它身上,查找下一个如果不允许则跳过本地循环,继续下一个view的判断
                            if (!child.canReceivePointerEvents()
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                continue;
                            }
						
                            //将事件分发到子view上,如果子view拦截了事件事件,放回true,
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            // The accessibility focus didn't handle the event, so clear
                            // the flag and do a normal dispatch to all children.
                            ev.setTargetAccessibilityFocus(false);
                        }
        return handled;
    }

上面代码做了以下几个操作

  1. for 循环,首先遍历 ViewGroup 的子元素,判断子元素是否能够接收到点击事件,如果子元素能够接收到点击事件,则交由子元素来处理。需要注意的是这个 for 循环是倒序便利的,即从最上层的子 View 开始往内层遍历。
  2. 判断触摸点的卫士是否在子 View 的范围或者子 View 是否在播放动画,如果均不符合则执行 continue 语句,表示这个子 View 不符合条件,开始便利下一个子 View。
  3. 调用 ViewGroup.dispatchTransformedTouchEvent(…)
// /frameworks/base/core/java/android/view/ViewGroup.java
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
      View child, int desiredPointerIdBits) {
      final boolean handled;

      final int oldAction = event.getAction();
      if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
          event.setAction(MotionEvent.ACTION_CANCEL);
          if (child == null) {
              handled = super.dispatchTouchEvent(event);
          } else {
              handled = child.dispatchTouchEvent(event);
          }
          event.setAction(oldAction);
          return handled;
      }

     ...
}

如果有子 View,则调用子 View.dispatchTouchEvent(…) 方法,如果没有子 View,则调用 super.dispatchTouchEvent(…) 方法。

// frameworks/base/core/java/android/view/View.java
public boolean dispatchTouchEvent(MotionEvent event) {
    ...

    boolean result = false;

    ...

    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        //noinspection SimplifiableIfStatement
        //touchlistener回调监听
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) { // 1
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }

   ...

    return result;
}

这个方法很好理解,若有ontouch监听,则优先执行监听,若没有监听则继续执行view.ontouchevent

1.判断是否存在事件拦截,如果存在事件拦截,则分发到拦截的事件中,继而中断事件。
2.找到ViewGroup中所有的子View,把事件分发到view中。
3.遍历view后会存在几种判断,判断当前的循环到的子view是否能够接受事件,和当前的点是否所在此子view上,只要此子view不能接受事件或者点没有落在它身上,查找下一个
4. 若没有找到合适的view,则会继续分发调用父类View的dispatchtouchevent.

// /frameworks/base/core/java/android/view/View.java
public boolean onTouchEvent(MotionEvent event) {
  .	...
    final int action = event.getAction();

//这边判断view是否设置了clickable属性。
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; // 1
	
    ...

    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                ...
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    // take focus if we don't have it already and we should in
                    // touch mode.
                    boolean focusTaken = false;
                    ...
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // This is a tap, so remove the longpress check
                        removeLongPressCallback();

                        // Only perform take click actions if we were in the pressed state
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClickInternal();
                            }
                        }
                    }

                    ...
                }
                mIgnoreNextUpEvent = false;
                break;

 								...
        }

        return true;
    }

    return false;
}

只要 View 的 CLICKABLE 和 LONG_CLICKABLE 有一个为 true,那么 View.onTouch(…) 就会返回 true 消耗这个事件。CLICKABLE 和 LONG_CLICKABLE 代表 View 可以被点击和长按点击,可以通过 View.setClickable(…) 和 View.setLongClickable(…) 方法来设置,也可以通过 View.setOnClickListener(…) 和 View.setOnLongClickListener(…) 方法来设置,它们会自动将 View 设置为 CLICKABLDE 和 LONG_CLICKABLE。接着在 ACTION_UP 事件中会调用 View.performClickInternal() 方法
performClickInternal 方法就是执行应用的onclick事件。

6.4. 简要概括总结

总结:

  1. 触摸屏幕后,我们的事件会传递到当前的 Activity 的 dispatchTouchEvent 方法中
  2. Activity的dispatchTouchEvent把事件分发到 ViewGrop 的 dispatchTouchEvent 方法中
  3. ViewGrop 的 dispatchTouchEvent方法开始分发给子view
  4. 如果ViewGrop处理没有消费事件,事件将在最后传递回Activity的onTouchEvent中
  5. 如果事件不被中断,整个事件流向就是一个最初的那个环型图。 如果没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity -> ViewGroup -> View从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View -> ViewGroup -> Activity从下往上调用onTouchEvent方法;
  6. dispatchTouchEvent和onTouchEvent一旦return true,事件就停止传递了,没有谁能再收到这个事件;return false的时候,事件都回传给父控件的onTouchEvent和dispatchTouch处理;

6.5. java层从natice层接收数据部分代码逻辑

下方为java层接收native层数据的部分代码

> frameworks/base/core/java/android/view/InputEventReceiver.java
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        Slog.e("hyy","InputEventReceiver---dispatchInputEvent---event:"+event);
        onInputEvent(event);
    }

> frameworks/base/core/java/android/view/ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
   @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }
    ...
}

> frameworks/base/core/java/android/view/ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
            //将event放入QueuedInputEvent
		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;
        //成员变量 mPendingInputEventHead 赋值为QueuedInputEvent 在doProcessInputEvents方法中使用
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        if (processImmediately) {
            doProcessInputEvents(); // 分发输入事件
        } else {
            scheduleProcessInputEvents();
        }
}

    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);
        }

        // 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);
        }
    }


>View.java
    public final boolean dispatchPointerEvent(MotionEvent event) {
    //如果是触摸事件
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }



说明:

  1. Activity的dispatchTouchEvent只有return super.dispatchTouchEvent(ev)才往下走,返回true或者false事件就被消费了(终止传递);

7. 常见问题及处理方案

1.点击驱动无上报
通过获取设备节点数据无反馈
adb shell getevent -l 数据节点

2.驱动正常上报,界面点击无响应。log中反馈InputDispatcher drop

3.java无事件上报

adb shell dumpsys input

1.Event Hub 状态
2.Input Reader 状态
3.Input Dispatcher 状态

可以通过Event Hub查看当前设备点击屏幕所使用的设备节点。
在这里插入图片描述
![请添加图片描述](https://img-blog.csdnimg.cn/direct/1154a6f945814398a173a5648e2b2b94.jpeg

InputReader中可以查看点击的x,y轴坐标

在这里插入图片描述

InputDispatcher中可以查看window信息,历史事件信息,当前队列信息等

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值