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<InputTarget> 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<InputTarget>& inputTargets
InputWindowHandle類型的數組,確定觸屏windowHandle
Vector<sp<InputWindowHandle> > 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<InputWindowHandle> 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<InputWindowHandle>&
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<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& 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> inputChannel;
sp<InputWindowHandle> inputWindowHandle;
InputPublisher inputPublisher;
在Connection構造方法中,傳入的InputChannel同時交給InputPublisher內部,所以InputPublisher內也包含inputChannel。
Connection構造方法代碼片段:inputPublisher(inputChannel)
。
遍歷inputTargets,其中inputTargets中有一個InputTarget包觸屏通道
內部包含sp<InputChannel> inputChannel
尋找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> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
getConnectionIndexLocked獲取connectionIndex,connectionIndex是mConnectionsByFd中Fd的索引值,根據connectionIndex獲取Connection。
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
if (connectionIndex >= 0) {
sp<Connection> 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<int, sp<Connection> > mConnectionsByFd;
系統創建每一個窗體,均會為其注冊一個服務端InputChannel,創建鏈路Connection并建立InputChannel的Fd與Connection的關系存儲Map表。
依次走到3個方法派送
- prepareDispatchCycleLocked
- enqueueDispatchEntriesLocked
- startDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) {
...
//
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& 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<InputChannel> serverChannel;
sp<InputChannel> 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<InputChannel>& outServerChannel, sp<InputChannel>& 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<NativeInputManager*>(ptr);
//根據Java層入參InputWindowHandle對象和InputChannel對象,找到對應Native對象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
...
sp<InputWindowHandle> 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>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ //上鎖
AutoMutex _l(mLock);
.......
sp<Connection> 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<InputEventReceiver>(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> inputChannel = android_view_InputChannel_
getInputChannel(env,inputChannelObj);
//根據java層MessageQueue找到對應Native層消息隊列
sp<MessageQueue> messageQueue = android_os_MessageQueue_
getMessageQueue(env, messageQueueObj);
//創建Native層接收器,inputChannel交給mInputConsumer(inputChannel)
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();//初始化
//返回給Java層指針
receiver->incStrong(gInputEventReceiverClassInfo.clazz);
return reinterpret_cast<jlong>(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<MotionEvent*>(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
^^