InputDispatcher#dispatchMotionLocked分发处理事件解析
三个步骤
- 判断事件是否触摸事件
- 寻找触屏Window
- 事件派发
InputDispatcher#dispatchMotionLocked方法
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
....
//判定事件源为手指触屏
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
Vector inputTargets;
if (isPointerEvent) {// 触屏事件
//初始化Window目标到inputTargets数组,返回事件注入的结果
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
...//非触屏事件
}
//派发
dispatchEventLocked(currentTime, entry, inputTargets);
}
injectionResult 成功找到Window目标,标志是0。
INPUT_EVENT_INJECTION_SUCCEEDED = 0
其他
INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,//失败 无权限
INPUT_EVENT_INJECTION_FAILED = 2,//失败 无目标
INPUT_EVENT_INJECTION_TIMED_OUT = 3//超时
-
findTouchedWindowTargetsLocked解析
初始化inputTargets数组
Vector
& inputTargets
InputWindowHandle类型的数组,确定触屏windowHandle
Vector
> mWindowHandles;
遍历InputWindowHandle数组元素,寻找触屏InputWindowHandle。
findTouchedWindowTargetsLocked方法代码段:
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
//触摸坐标(x,y)
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));
size_t numWindows = mWindowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
sp windowHandle = mWindowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
...
int32_t flags = windowInfo->layoutParamsFlags;
if (windowInfo->visible) {//信息说明窗体可见
//flags信息说明窗体支持可触摸
if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
newTouchedWindowHandle = windowHandle;
break; //发现触屏Window,退出遍历
}
}
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
&& (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
mTempTouchState.addOrUpdateWindow(
windowHandle, outsideTargetFlags, BitSet32(0));
}
}
}
...
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
touchableRegionContainsPoint判断是否包含触摸坐标(x,y),如果包含说明触摸点在该窗体上。
根据找到的newTouchedWindowHandle更新或者增加mTempTouchState结构体中windows元素内容
windows是保存mTempTouchState(TouchState)结构体中TouchedWindow类型的数组,遍历windows,若TouchedWindow的windowHandle与newTouchedWindowHandle相等,更新TouchedWindow中的targetFlags与pointerIds值。
TouchState#addOrUpdateWindow方法
void InputDispatcher::TouchState::addOrUpdateWindow(const sp&
windowHandle,int32_t targetFlags, BitSet32 pointerIds){
...
//遍历TouchState中windows的每个TouchedWindow
//找到与入参InputWindowHandle相同的TouchedWindow
//更新其中的值
for (size_t i = 0; i < windows.size(); i++) {
TouchedWindow& touchedWindow = windows.editItemAt(i);
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
}
touchedWindow.pointerIds.value |= pointerIds.value;
return;
}
}
//如果没有找到,压入栈顶新元素,创建一个TouchedWindow
windows.push();
TouchedWindow& touchedWindow = windows.editTop();//操作栈顶
//新TouchedWindow赋值
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
touchedWindow.pointerIds = pointerIds;
}
确保TouchState的TouchedWindow列表中有一个是保存触屏Window信息。
最后初始化InputTargets数组,通过addWindowTargetLocked方法完成InputTargets数组初始化。
findTouchedWindowTargetsLocked方法代码段:
// Success! Output targets.返回注入成功结果
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
//遍历TouchedWindow数组
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
这时TouchState的TouchedWindow数组中应该有一个TouchedWindow的windowHandle对应触摸的Window。一般触摸的情况下TouchedWindow应该只有一个元素吧。
用每个TouchedWindow中的InputWindowHandle信息初始化inputTargets。
addWindowTargetLocked方法
void InputDispatcher::addWindowTargetLocked(const sp& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector& inputTargets) {
inputTargets.push();//将一项入栈,该项默认的构造方法构建
const InputWindowInfo* windowInfo = windowHandle->getInfo();
InputTarget& target = inputTargets.editTop(); //取出栈顶,授予对栈顶的访问
target.inputChannel = windowInfo->inputChannel;
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
target.scaleFactor = windowInfo->scaleFactor;
target.pointerIds = pointerIds;
}
最后一个被赋值的InputTarget位于栈顶。
InputTarget主要赋值内容:inputChannel,targetFlags,pointerIds。
TouchedWindow#InputWindowHandle中的windowInfo信息赋值InputTarget。
包括inputChannel,它属于WindowState中保存的的服务端InputChannel。
于是InputTargets数组有元素就包含了服务端inputChannel。
-
dispatchEventLocked方法解析
1.寻找Connection
Connection代表通向窗体的一条数据链路,获得Connection意味着拿到可写入socket套接字,便可以将事件发布给窗体。
Connection是InputDispatcher内部类,在InputChannel通道注册时创建。
Connection封装了三个重要对象
- InputChannel :传输通道,Socket套接字通信。
- InputWindowHandle:Window相关对象。
- InputPublisher :封装事件的Message,事件发布。
sp inputChannel;
sp inputWindowHandle;
InputPublisher inputPublisher;
在Connection构造方法中,传入的InputChannel同时交给InputPublisher内部,所以InputPublisher内也包含inputChannel。
Connection构造方法代码片段:inputPublisher(inputChannel)
。
遍历inputTargets,其中inputTargets中有一个InputTarget包触屏通道
内部包含spinputChannel
寻找Connection
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {//说明找到Connection的位置
sp connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
getConnectionIndexLocked获取connectionIndex,connectionIndex是mConnectionsByFd中Fd的索引值,根据connectionIndex获取Connection。
ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputChannel) {
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
if (connectionIndex >= 0) {
sp connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->inputChannel.get() == inputChannel.get()) {
return connectionIndex;
}
}
return -1;
}
根据InputChannel的Fd在mConnectionsByFd中查找Connection,Connection也保存了当时传入的服务端InputChannel,而InputTarget的InputChannel是InputWindowHandle交给的,保证这两个通道相同,才能通过正确的链路传给窗体。
mConnectionsByFd保存socket句柄与Connection的Map
KeyedVector > mConnectionsByFd;
系统创建每一个窗体,均会为其注册一个服务端InputChannel,创建链路Connection并建立InputChannel的Fd与Connection的关系存储Map表。
依次走到3个方法派送
- prepareDispatchCycleLocked
- enqueueDispatchEntriesLocked
- startDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) {
...
//
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
}
2.发布事件
在startDispatchCycleLocked中,开始真正的事件发布。
1.Connection的inputPublisher对象是发布者,实现在InputTransport.cpp文件中,可处理发布不同事件,有触摸publishMotionEvent、Key事件publishKeyEvent等。
2.将传入的将事件信息(例motionEntry->deviceId)进一步封装,产生InputMessage对象。
3.依赖服务端InputChannel派送。
startDispatchCycleLocked方法片段
...
status = connection->inputPublisher.publishMotionEvent(dispatchEntry
->seq,motionEntry->deviceId, motionEntry->source,..);
...
InputPublisher#publishMotionEvent方法,主要工作是封装InputMessage,利用InputPublisher的InputChannel发送。
status_t InputPublisher::publishMotionEvent(
uint32_t seq,
int32_t deviceId,
....
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
....
InputMessage msg;
msg.header.type = InputMessage::TYPE_MOTION;
msg.body.motion.seq = seq;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.action = action;
msg.body.motion.flags = flags;
...
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
return mChannel->sendMessage(&msg);
}
发送对象是InputMessage类型
3.发送
将InputMessage对象写入InputChannel的socket套接字
将数据写入到套接字mFd发送缓冲区。
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
//调用Linux send方法
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
}
最终向服务端InputChannel的套接字写入了事件信息InputMessage。
Java层IMS服务创建与注册InputChannel通道
WMS#addWindow方法调用IMS服务提供的InputChannel创建与注册方法。
addWindow代码段:
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
.....
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility,
displayContent);
...
if (outInputChannel != null && (attrs.inputFeatures
& LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
//创建Java层InputChannel数组
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//服务端InputChannel
win.setInputChannel(inputChannels[0]);
//客户端InputChannel
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel,
win.mInputWindowHandle);
}
.....
}
服务端InputChannel交给WindowState注册到InputDispatcher。在WindowState中同时交给内部InputWindowHandle。
WindowState#setInputChannel方法
void setInputChannel(InputChannel inputChannel) {
mInputChannel = inputChannel;
mInputWindowHandle.inputChannel = inputChannel;
}
客户端InputChannel交给了outInputChannel。outInputChannel是addWindow的入参,即通过mWindowSession.addToDisplay方法传入的mInputChannel,最终保存在ViewRootImpl中。
因此,InputChannel数组的一对InputChannel,一个注册给了InputDispatcher,另一个交给应用程序ViewRootImpl。
在WMS#addWindow方法中
IMS服务提供Java层InputChannel的创建与注册
创建:InputChannel静态方法
InputChannel.openInputChannelPair(inputChannelName)
注册:IMS方法
registerInputChannel(InputChannel ,InputWindowHandle)
-
InputChannel的创建
包括Java层与Native层的InputChannel,各一对,保存数组中。
JNI#nativeOpenInputChannelPair方法
public static InputChannel[] openInputChannelPair(String name) {
...
return nativeOpenInputChannelPair(name)
}
C++代码创建Java层InputChannel对象
- 构造一个Java层jobjectArray数组对象channelPair,数组元素的类型是gInputChannelClassInfo的clazz。
初始化值为"android/view/InputChannel",所以创建的Java对象为InputChannel。- 创建Java层的InputChannel对象jobject ,将创建的Native层InputChannel封装成NativeInputChannel,指针赋值给Java层mPtr变量。
- 最后将jobject对象为jobjectArray赋值, 数组channelPair,将其返回给Java层,即InputChannel[]。
- Java层的InputChannel对象引用Native层NativeInputChannel指针。
JNI#nativeOpenInputChannelPair代码段:
sp serverChannel;
sp clientChannel;
//Native层InputChannel的创建
status_t result = InputChannel::openInputChannelPair(name, serverChannel,
clientChannel);
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
//Java层InputChannel的创建
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
//两个InputChannel对象设置到数组
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;//返回Java数组
InputChannel[0]代表服务端通道serverChannelObj。InputChannel[1]代表客户端通道clientChannelObj。
InputChanne#openInputChannelPair方法
status_t InputChannel::openInputChannelPair(const String8& name,
sp& outServerChannel, sp& outClientChannel) {
int sockets[2];
//socketpair创建一对套接字描述符,保存在sockets数组中
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
...失败处理
}
//setsockopt设置套接字
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
//创建服务InputChannel(C++层)
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
//创建客户端InputChannel(C++层)
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
C++代码创建Native层InputChannel对象
- InputChannel代表一个通道,每个InputChannel内部均有一个socket句柄,用于进行socket通信。
- 创建完毕后,openInputChannelPair的入参serverChannel与clientChannel指针就指向了Native层InputChannel对象
-
InputChannel的注册
IMS服务registerInputChannel负责注册
本质是通过InputDispatcher进行注册,告诉InputDispatcher注册这个通道
JNI#nativeRegisterInputChannel方法
服务端InputChannel事件接收架构图如下:
服务端InputChannel注册到InputDispatcher
IMS#registerInputChannel方法
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
JNI#nativeRegisterInputChannel方法
nativeRegisterInputChannel代码段:
static void nativeRegisterInputChannel(JNIEnv* env, jclass jlong ptr, jobject inputChannelObj,
jobject inputWindowHandleObj, jboolean monitor) {
//根据ptr指针获取本地NativeInputManager
NativeInputManager* im = reinterpret_cast(ptr);
//根据Java层入参InputWindowHandle对象和InputChannel对象,找到对应Native对象
sp inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
...
sp inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
...
//调用NativeInputManager的注册通道方法
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
}
NativeInputManager#registerInputChannel注册方法,实质是触发InputDispatcher的注册方法
mInputManager->getDispatcher()->registerInputChannel(inputChannel,
inputWindowHandle, monitor)
InputDispatcher注册registerInputChannel输入通道,主要功能:创建Connection,保存Fd与Connection的Map,保存Fd与Connection的关系表,增加对Fd监听。
InputDispatcher#registerInputChannel代码
status_t InputDispatcher::registerInputChannel(const sp& inputChannel,
const sp& inputWindowHandle, bool monitor) {
{ //上锁
AutoMutex _l(mLock);
.......
sp connection = new Connection(inputChannel,
inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
....
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} //释放锁
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
InputDispatcher的registerInputChannel注册方法,Looper是InputDispatcher构造方法创建,线程在服务进程中,服务创建过程在Android输入管理InputManager之服务启动文章中参考。handleReceiveCallback是接收事件回调方法,最后唤醒Looper。
注册完成后,InputDispatcher增加一条派发通道。
ViewRootImpl创建窗体输入事件监听器WindowInputEventReceiver
监听的本质:
在客户端应用程序,借助Native层Looper将InputChannel通道的套接字Fd交给底层epoll进行事件流监视,将监视的事件流派发给ViewRootImpl中的树视图。
客户端ViewRootImpl监听触摸事件接收器结构图如下
以下代码段是在ViewRootImpl#setView方法中初始化监听器的过程。
//窗体注册到WMS,初始化通道mInputChannel
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
//ViewRootImpl中注册事件监听接收器WindowInputEventReceiver
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
WindowInputEventReceiver类继承InputEventReceiver抽象类
InputEventReceiver构造方法
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
//InputChannel与Looper为空异常判断
....
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference(this),
inputChannel, mMessageQueue);
}
JNI#nativeInit负责初始化底层,创建底层NativeInputEventReceiver接收器,mReceiverPtr保存接收器对象指针。
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
//根据java层InputChannel找到对应Native层InputChannel
sp inputChannel = android_view_InputChannel_
getInputChannel(env,inputChannelObj);
//根据java层MessageQueue找到对应Native层消息队列
sp messageQueue = android_os_MessageQueue_
getMessageQueue(env, messageQueueObj);
//创建Native层接收器,inputChannel交给mInputConsumer(inputChannel)
sp receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();//初始化
//返回给Java层指针
receiver->incStrong(gInputEventReceiverClassInfo.clazz);
return reinterpret_cast(receiver.get());
}
NativeInputEventReceiver构造方法初始化mInputConsumer,mMessageQueue。InputConsumer封装了InputChannel。
NativeInputEventReceiver#initialize接收器初始化,设置fd事件监听。
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
从InputConsumer的InputChannel中拿到Fd。
//InputChannel的Fd
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
请求Looper提供对fd的监听支持addFd(fd, 0, events, this, NULL)
参数fd代表监听句柄,参数events代表事件类型,值是ALOOPER_EVENT_INPUT即1。这两个参数会写入结构体struct epoll_event eventItem,作为epoll_ctl的入参。
初始化eventItem,events是addFd传入的值ALOOPER_EVENT_INPUT。
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;//EVENT_INPUT是1
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;//EPOLLOUT是2
memset(eventItem, 0, sizeof(epoll_event));
eventItem->events = epollEvents;
eventItem->data.fd = fd;
参数this代表回调对象LooperCallback,是处理Looper事件的回调类,NativeInputEventReceiver继承LooperCallback。
NativeInputEventReceiver实现了底层消息的回调handleEvent方法,当监听的句柄fd发生事件,触发NativeInputEventReceiver#handleEvent方法。
处理事件方法,负责构建Java层的事件实体对象,并回调Java方法。
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data)
NativeInputEventReceiver#handleEvent处理事件消息
事件处理流程图如下所示
-
事件处理流程
NativeInputEventReceiver#handleEvent方法代码段:
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
NativeInputEventReceiver#consumeEvents消费事件方法
这里分析只针对触摸事件,根据Native层MotionEvent,构造Java层的MotionEvent对象。
Java层对象是在android/view/MotionEvent
NativeInputEventReceiver#consumeEvents代码段:
case AINPUT_EVENT_TYPE_MOTION: {
MotionEvent* motionEvent = static_cast(inputEvent);
f ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
//inputEventObj是Java层MotionEvent对象
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
...
if (inputEventObj) {
...
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent,
seq, inputEventObj);
...
}
通过env的CallVoidMethod方法调Java层方法,gInputEventReceiverClassInfo.clazz对应的类是
android/view/InputEventReceiver。
CallVoidMethod触发Java层InputEventReceiver#dispatchInputEvent方法。入参seq和inputEventObj,inputEventObj是Java层MotionEvent对象。
InputEventReceiver#dispatchInputEvent方法
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
dispatchInputEvent中调用onInputEvent,WindowInputEventReceiver重写onInputEvent。
WindowInputEventReceiver重写的onInputEvent
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
因此WindowInputEventReceiver最终调用的方法是ViewRootImpl的enqueueInputEvent,从接收器进入ViewRootImpl,开始View事件责任链处理以及后续View事件传递。
ViewRootImpl#enqueueInputEvent方法
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
....
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
1:obtainQueuedInputEvent方法获取QueuedInputEvent,mQueuedInputEventPool是QueuedInputEvent对象池的首指针,从mQueuedInputEventPool链表中获取QueuedInputEvent,不存在就新建对象。将MotionEvent、InputEventReceiver和flag设置到QueuedInputEvent中。
2:mPendingInputEventTail是待处理的最后一个节点,如果不是空,将刚刚获取的节点放到最后,如果是空,说明没有待处理的节点,mPendingInputEventHead与mPendingInputEventTail指向当前QueuedInputEvent节点,只有一个节点需要处理。
3:doProcessInputEvents处理,循环遍历QueuedInputEvent链表,mPendingInputEventHead开头,直到mNext指向空。
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;//拿到节点
mPendingInputEventHead = q.mNext;//指向下一个
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null//每个处理节点的mNext置空,mPendingInputEventHead已经指向下一个。
mPendingInputEventCount -= 1;
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);//发送事件
}
....
}
while循环会处理完所有mPendingInputEventHead链表节点,最后mPendingInputEventHead与mPendingInputEventTail均变为null。
deliverInputEvent方法处理一个QueuedInputEvent节点,QueuedInputEvent节点封装了MotionEvent对象。
InputStage责任链处理QueuedInputEvent节点。
InputStage处理链如下图所示
-
InputStage分析
责任链设计
链表派送与处理的对象:QueuedInputEvent事件对象
deliver派送事件入口
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);//有了结束标志就一直向后传
} else if (shouldDropInputEvent(q)) {//是否放弃事件
finish(q, false);//false代表未处理状态下的结束,增加结束标志,然后向后传
} else {
apply(q, onProcess(q));
}
}
1:forward(QueuedInputEvent q)方法,触发onDeliverToNext派送给下一个对象,执行链表next节点的deliver派送方法,如果next为空,链式处理结束ViewRootImpl#finishInputEvent方法。
2:QueuedInputEvent有结束标志FLAG_FINISHED时,forward方法派送下一个对象。放弃事件时,finish(QueuedInputEvent q, boolean handled)结束方法,QueuedInputEvent加上FLAG_FINISHED标志,forward方法派送下一个对象。
3:以上都不满足时进入apply,首先onProcess处理,结果传给apply。:
InputStage子类重写onProcess处理方法。
onProcess结果:FORWARD、FINISH_HANDLED、FINISH_NOT_HANDLED
apply(QueuedInputEvent q, int result)方法,根据onProcess结果选择forward方法还是finish方法。
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {//自己未处理,向后传
forward(q);
} else if (result == FINISH_HANDLED) {//自己节点处理过,加上结束标志,向后传
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {//自己节点未处理成功,加上结束标志,向后传
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
EarlyPostImeInputStage节点处理键盘事件,根据事件类型及其输入源处理,触摸事件派送到方法processPointerEvent(QueuedInputEvent q)中,返回FORWARD,继续传给下一个节点。
ViewPostImeInputStage节点,onProcess方法,根据输入源类型判断,如果是Touch事件派送到processPointerEvent处理,发送MotionEvet到View层次结构,若成功返回FINISH_HANDLED,这样apply时QueuedInputEvent加上结束标志,后续节点便不再处理。
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
boolean handled = mView.dispatchPointerEvent(event);
.....
return handled ? FINISH_HANDLED : FORWARD;
}
View#dispatchPointerEvent方法就是Touch事件进入View树形结构的入口方法。
Happy
End
^^