Android系統(tǒng)源碼剖析-事件分發(fā)

前言

上一篇文章中调违,對(duì)于事件的監(jiān)控和獲取做了分析穿撮,在拿到事件之后签赃,后續(xù)是如何處理分發(fā)的呢忽妒?本篇文章主要針對(duì)在通過getEvent獲取到事件之后,后續(xù)的相關(guān)分發(fā)處理流程姜挺。

事件處理分發(fā)

InputReaderThread函數(shù)不斷地調(diào)用looperOnce函數(shù),不斷的從中讀取事件彼硫,那么下一個(gè)問題來了炊豪,讀取到事件要放置到哪里,又在哪里被消耗掉了呢拧篮?也就是事件接下來的流向問題词渤。讓我們回到looperOnce之前。

事件分發(fā)

processEventsLocked

在調(diào)用了getEvent之后串绩,獲得了事件之后缺虐,接著調(diào)用了相應(yīng)的處理函數(shù)processEventsLocked

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) {
            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);
        } else {
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::DEVICE_REMOVED:
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                handleConfigurationChangedLocked(rawEvent->when);
                break;
            default:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

首先對(duì)于事件類型進(jìn)行了判斷,如果事件不是合成事件礁凡,則會(huì)對(duì)其DeviceID進(jìn)行判斷高氮,通過對(duì)其判斷來確定batchSize等慧妄,如果是合成事件,則會(huì)具體判斷剪芍,判斷是設(shè)備的添加塞淹,設(shè)備的移除,完成設(shè)備掃描等等罪裹,然后對(duì)事件分別進(jìn)行處理饱普,這里我們只關(guān)心對(duì)于設(shè)備自身產(chǎn)生的事件。也就是觸摸屏相關(guān)的事件状共。也就是processEventsForDeviceLocked函數(shù)中所進(jìn)行的操作套耕。

事件派發(fā)到InputDevice
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        return;
    }

    device->process(rawEvents, count);
}

根據(jù)事件獲得相應(yīng)的設(shè)備類型,然后將事件交給相應(yīng)的設(shè)備處理峡继。判斷是否忽略該事件冯袍,如果不是忽略該事件,則會(huì)調(diào)用相應(yīng)設(shè)備的process方法進(jìn)行處理鬓椭。

事件派發(fā)到InputMapper

InputDevice的process方法

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
     ....
    for (size_t i = 0; i < numMappers; i++) {
           InputMapper* mapper = mMappers[i];
           mapper->process(rawEvent);
    }
    ....
}

這里的事件又交給了InputMapper來處理

InputMapper

InputMapper對(duì)應(yīng)了很多的子類颠猴,這里根據(jù)事件的類型進(jìn)行相應(yīng)的派發(fā),處理小染。事件到了這里之后翘瓮,這里來看一下對(duì)于觸摸屏事件的處理的相關(guān)類和處理函數(shù)。

void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

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

通過這里的函數(shù)處理裤翩,我們繼續(xù)追蹤函數(shù)的數(shù)據(jù)流向资盅。對(duì)于相關(guān)事件會(huì)調(diào)用TouchInputMapper的sync方法來進(jìn)行處理。

void TouchInputMapper::sync(nsecs_t when) {
    .....
    processRawTouches(false /*timeout*/);
}

void TouchInputMapper::processRawTouches(bool timeout) {
        ....
        cookAndDispatch(mCurrentRawState.when);
        ....
}

在相關(guān)的函數(shù)調(diào)用之后踊赠,最終調(diào)用了dispatchTouches

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
      ....
    dispatchMotion();
      ....
}

對(duì)于dispatchTouches中呵扛,會(huì)根據(jù)記錄的上一次的觸摸位置,對(duì)事件的類型進(jìn)行判斷筐带,然后做相應(yīng)的分發(fā)今穿,事件類型有抬起,下落伦籍,移動(dòng)等蓝晒,然后對(duì)相應(yīng)的事件進(jìn)行分發(fā)。無論是對(duì)于何種類型的事件派發(fā)帖鸦,最終被調(diào)用到的都是dispatchMotion()方法芝薇。

對(duì)于相關(guān)事件的分發(fā)最終調(diào)用到了dispatchMotion(),對(duì)事件數(shù)據(jù)進(jìn)行組裝之后作儿,調(diào)用了

void TouchInputMapper::dispatchMotion() {
   ....
   NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}

這里對(duì)于事件進(jìn)行了封裝洛二,構(gòu)造出一個(gè)NotifyMotionArgs,在整個(gè)傳遞流程做的比較多的就是通過對(duì)于事件進(jìn)行一系列的判斷,然后進(jìn)行一系列的封裝晾嘶。接下來的執(zhí)行是調(diào)用QueuedInputListener的notifyMotion妓雾,將構(gòu)造的事件添加到其中的一個(gè)Args隊(duì)列之中。

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

notifyMotion函數(shù)實(shí)現(xiàn)

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

之后又調(diào)用了QueuedInputListenerflush方法变擒。遍歷事件的隊(duì)列君珠,然后對(duì)其逐一調(diào)用notify函數(shù)。

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

NotifyArgs的notify函數(shù)實(shí)現(xiàn)

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

對(duì)于這個(gè)listener的創(chuàng)建來自于InputReader構(gòu)建的時(shí)候娇斑。

mQueuedListener = new QueuedInputListener(listener);
 mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

而這里的Listener則是InputDispatcher策添,InputDispatcher 的notifyMotion實(shí)現(xiàn)源碼。

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    .....
   MotionEvent event;
   event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);
    ....
  MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
   needWake = enqueueInboundEventLocked(newEntry);
    ....
   if (needWake) {
      mLooper->wake();
   }
}

在該函數(shù)中毫缆,所做的事情是對(duì)于所傳遞的參數(shù)唯竹,構(gòu)造MotionEntry,然后將其加入到enqueueInboundEventLocked之中苦丁。然后喚醒其中的looper浸颓。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);
    ...
    //進(jìn)行一些事件和窗口相關(guān)的判斷處理
}

Dispatcher開啟的線程中,每次循環(huán)的操作如何旺拉?

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

Dispatcher下dispatchOnce的實(shí)現(xiàn)

void InputDispatcher::dispatchOnce() {
    ...
   dispatchOnceInnerLocked(&nextWakeupTime);
    ...
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
   ....
   mPendingEvent = mInboundQueue.dequeueAtHead();
   ....

   switch (mPendingEvent->type) {
        case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
    ....
   }
}

從mInboudQueue中产上,獲取到事件,然后對(duì)事件類型進(jìn)行判斷蛾狗,判斷之后調(diào)用了dispatchMotionLocked函數(shù)晋涣,來繼續(xù)進(jìn)行事件的傳遞。

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
      ....
      Vector<InputTarget> inputTargets;
if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
      //尋找目標(biāo)窗口
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    ....
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
  • dispatchEventLocked
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
    EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    ....
    pokeUserActivityLocked(eventEntry);
    .....
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } 
    }
}

獲得目標(biāo)輸入沉桌,根據(jù)InputChannel獲取相應(yīng)的連接谢鹊,然后調(diào)用prepareDispatchCycleLocked(),進(jìn)行事件的派發(fā)留凭。
enqueueDispatchEntriesLocked佃扼,在該方法中又調(diào)用了startDispatchCycleLocked方法。其實(shí)現(xiàn)為

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
   EventEntry* eventEntry = dispatchEntry->eventEntry;
    ....
   switch (eventEntry->type) {
      ....
    case EventEntry::TYPE_MOTION: {
      status = connection->inputPublisher.publishMotionEvent( ....);    
      break;
    }
    ....
   }
    ...
}

至此調(diào)用了connection 的inputPublisher的publishMotionEvent方法將事件分發(fā)消耗蔼夜。

InputPublisher定義在InputTransport.cpp中

status_t InputPublisher::publishMotionEvent(...) {
  ....
  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.actionButton = actionButton;
  msg.body.motion.flags = flags;
  msg.body.motion.edgeFlags = edgeFlags;
  msg.body.motion.metaState = metaState;
  msg.body.motion.buttonState = buttonState;
  msg.body.motion.xOffset = xOffset;
  msg.body.motion.yOffset = yOffset;
  msg.body.motion.xPrecision = xPrecision;
  msg.body.motion.yPrecision = yPrecision;
  msg.body.motion.downTime = downTime;
  msg.body.motion.eventTime = eventTime;
  msg.body.motion.pointerCount = pointerCount;
  for (uint32_t i = 0; i < pointerCount; i++) {
      msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
      msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
  }
    return mChannel->sendMessage(&msg);
}

該方法所執(zhí)行的操作是利用傳入的觸摸信息兼耀,構(gòu)建點(diǎn)擊消息,然后通過InputChannel將消息發(fā)送出去求冷。這里引出了InputChannel翠订,在此,我們通InputPublisher的創(chuàng)建反推出InputChannel是何時(shí)被引入的遵倦,何時(shí)被創(chuàng)建的。從而進(jìn)一步分析其作用官撼。在分析之前先讓我們來對(duì)上述的分析過程做一個(gè)總結(jié)梧躺。

觸摸事件讀取分發(fā)過程

ReaderThread開啟后會(huì)從EventHub中輪詢獲取時(shí)間,獲取到事件之后,將進(jìn)行一系列的處理掠哥,對(duì)事件進(jìn)行加工包裝巩踏,然后傳遞給相應(yīng)的InputDevice,InputMapper续搀,然后將包裝的事件添加到一個(gè)事件隊(duì)列中塞琼,InputDispatcher的線程則會(huì)在輪詢讀取該線程中的事件,然后將其再次進(jìn)行做判斷處理后禁舷,傳遞彪杉,最終到達(dá)InputChannel,通過InputChannel牵咙,來將數(shù)據(jù)發(fā)送出去派近。

事件產(chǎn)生讀取分發(fā)

到此,對(duì)于輸入事件洁桌,我們已經(jīng)分析到了InputChannel渴丸,對(duì)于其上的具體分析轉(zhuǎn)化,將是接下來分析的核心另凌。

InputChannel

從上面分析可以看到事件傳遞部分最后是通過InputChannel所發(fā)送出去的谱轨,那么InputChannel是在何時(shí)被創(chuàng)建的呢?何時(shí)被InputManager所使用的呢吠谢?同時(shí)土童,對(duì)于InputReaderThread和InputDispatcherThread是運(yùn)行在SystemServer進(jìn)程中的,而我們的應(yīng)用進(jìn)程是和其不在同一個(gè)進(jìn)程中的囊卜。這之間一定也是有進(jìn)程間的通信機(jī)制在里面娜扇。具體是如何實(shí)現(xiàn)的呢?這里我們從InputChannel的創(chuàng)建著手來看栅组。

InputChannel的創(chuàng)建是在 ViewRootImplsetView方法中雀瓢。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ....
  if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
       mInputChannel = new InputChannel();
   }
  ....
  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
  ....
}

這里對(duì)于ViewRootImpl和WindowSession相關(guān)暫且不介紹,對(duì)于這方面的知識(shí)玉掸,需要很大的篇幅來介紹刃麸,這里先只是講到是在這里創(chuàng)建的,對(duì)于其相關(guān)的內(nèi)容將在后續(xù)的文章中介紹司浪。這里首先是創(chuàng)建了一個(gè)InputChannel泊业,然后將其調(diào)用了WindowSessionaddToDisplay方法將其作為參數(shù)傳遞。

public InputChannel() {
}

在InputChannel中的方法都為調(diào)用了相應(yīng)的native方法啊易。這里調(diào)用的addToDisplay將會(huì)把InputChannel添加到WindowManagerService中吁伺。會(huì)調(diào)用WMS的addWindow方法。

 public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
      ....

      final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
      if  (openInputChannels) {
          win.openInputChannel(outInputChannel);
      }
      ....
}

對(duì)于InputChannel的相關(guān)處理調(diào)用了WindowState的openInputChannel方法租谈。

void openInputChannel(InputChannel outInputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
     }
     String name = makeInputChannelName();
     InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
     mInputChannel = inputChannels[0];
     mClientChannel = inputChannels[1];
     mInputWindowHandle.inputChannel = inputChannels[0];
     if (outInputChannel != null) {
       mClientChannel.transferTo(outInputChannel);
       mClientChannel.dispose();
       mClientChannel = null;
      } else {
         mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
      }
       mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

首先調(diào)用了InputChannel的openInputChannelPair方法篮奄,該方法調(diào)用了InputChannel的native方法nativeOpenInputChannelPair,創(chuàng)建了兩個(gè)InputChannel捆愁,對(duì)其中一個(gè)通過InputManager進(jìn)行了InputChannel的注冊(cè)。對(duì)于InputChannel的相關(guān)Native的實(shí)現(xiàn)是在InputTransport中窟却,nativeOpenInputChannelPair的源碼如下昼丑。

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

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

    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
        int error = errno;
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (size_t(nWrite) != msgLength) {
        return DEAD_OBJECT;
    }
    return OK;
}

接收消息,通過讀socket的方式來讀取消息夸赫。

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);

    if (nRead < 0) {
        int error = errno;
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (nRead == 0) { // check for EOF
        return DEAD_OBJECT;
    }

    if (!msg->isValid(nRead)) {
        return BAD_VALUE;
    }
    return OK;
}

接收端的消息由誰來觸發(fā)呢菩帝?是如何觸發(fā)開始接受消息,消息如何在傳到InputChannel之后茬腿,進(jìn)行的進(jìn)一步的數(shù)據(jù)傳遞呢呼奢?這是接下來所要去分析的,這里先對(duì)上面InputChannel進(jìn)行一個(gè)總結(jié)滓彰。

InputChannel數(shù)據(jù)傳輸

之前的setView中控妻,我們創(chuàng)建了InputChannel之后,開啟了對(duì)于InputChannel中輸入事件的監(jiān)聽揭绑。

if (mInputChannel != null) {
   if (mInputQueueCallback != null) {
       mInputQueue = new InputQueue();
       mInputQueueCallback.onInputQueueCreated(mInputQueue);
  }
   mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
}

WindowInputEventReceiver的構(gòu)造函數(shù)如下弓候,其繼承自InputEventReceiver。

final class WindowInputEventReceiver extends InputEventReceiver {
     public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
         super(inputChannel, looper);
     }
      ....
}

InputEventReceiver的構(gòu)造函數(shù)源碼如下

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
     ....
     mInputChannel = inputChannel;
     mMessageQueue = looper.getQueue();
     mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
  }

這里調(diào)用了native方法來做初始化他匪,相關(guān)的native方法的實(shí)現(xiàn)在android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
   ....
  sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
  sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
  sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
  .....
}

根據(jù)傳入的InputChannelMessageQueue菇存,創(chuàng)建一個(gè)NativeInputEventReceiver,然后調(diào)用其initialize方法邦蜜。

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

initialize()方法中依鸥,只調(diào)用了一個(gè)函數(shù)setFdEvents

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

從InputConsumer中獲取到channel的fd悼沈,然后調(diào)用Looper的addFd方法贱迟。

int ALooper_addFd(ALooper* looper, int fd, int ident, int events,
        ALooper_callbackFunc callback, void* data) {
    return ALooper_to_Looper(looper)->addFd(fd, ident, events, callback, data);
}

Looper的addFd的實(shí)現(xiàn)如下

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
    Request request;
    request.fd = fd;
    request.ident = ident;
    request.events = events;
    request.seq = mNextRequestSeq++;
    request.callback = callback;
     request.data = data;
     if (mNextRequestSeq == -1) mNextRequestSeq = 0;
     struct epoll_event eventItem;
     request.initEventItem(&eventItem);
     ssize_t requestIndex = mRequests.indexOfKey(fd);
      if (requestIndex < 0) {
          int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
          if (epollResult < 0) {
                return -1;
            }
         mRequests.add(fd, request);
       } 
}

該方法所執(zhí)行的操作就是對(duì)傳遞的fd添加epoll監(jiān)控,Looper會(huì)循環(huán)調(diào)用pollOnce方法絮供,而pollOnce方法的核心實(shí)現(xiàn)就是pollInner衣吠。其代碼大致實(shí)現(xiàn)內(nèi)容為等待消息的到來,當(dāng)有消息到來后壤靶,根據(jù)消息類型做一些判斷處理缚俏,然后調(diào)用其相關(guān)的callback。我們當(dāng)前是對(duì)于開啟的socket的一個(gè)監(jiān)聽贮乳,當(dāng)有數(shù)據(jù)到來忧换,我們便會(huì)執(zhí)行相應(yīng)的回調(diào)。這里對(duì)于InputChannel的回調(diào)是在調(diào)用了NativeInputEventReceiver的handleEvent方法向拆。

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    .....
   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;
    }
    ....
    return 1;
}

對(duì)于Event的處理亚茬,這里調(diào)用consumeEvents來對(duì)事件進(jìn)行處理。

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...
    for(;;) {
      ...
     InputEvent* inputEvent;
     status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        ...
    }
   ...
}

InputConsumer是在InputTransport中做的聲明浓恳。

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
    while (!*outEvent) {
         ....
         status_t result = mChannel->receiveMessage(&mMsg);
          ....
    }
}

調(diào)用consume方法會(huì)持續(xù)的調(diào)用InputChannel的receiveMessage方法來從socket中讀取數(shù)據(jù)刹缝。到這里葡兑,我們已經(jīng)將寫入socket的事件讀出來了。

public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
   if (inputChannel == null) {
      throw new IllegalArgumentException("inputChannel must not be null.");
   }
   nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}

nativeRegisterInputManger

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        String8 message;
        message.appendFormat("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return;
    }

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}

NativeInputManager的registerInputChannel還會(huì)調(diào)用到InputDispatcher的registerInputChannel赞草,會(huì)通過InputChannel創(chuàng)建相應(yīng)的Connection,同時(shí)將InputChannel加入到相應(yīng)的監(jiān)控之中吆鹤。在上面對(duì)代碼的分析之中厨疙,獲取InputChannel,就是通過這個(gè)Connection來獲取的疑务。

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    { // acquire lock
        AutoMutex _l(mLock);

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            return BAD_VALUE;
        }

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

ViewRootImpl

事件在從socket讀出之后沾凄,經(jīng)過傳遞,最終會(huì)調(diào)用到ViewRootImpl的enqueueInputEvent方法知允。

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
     adjustInputEventForCompatibility(event);
     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();
      }
 }

enqueueInputEvent方法從InputEventReceiver中獲取到InputEvent撒蟀,然后將其加入到當(dāng)前的事件隊(duì)列之中,最后調(diào)用doProcessInputEvents來進(jìn)行處理温鸽。

void doProcessInputEvents() {
    while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

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

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

遍歷所有的消息保屯,如果事件類型為觸摸屏事件,對(duì)其進(jìn)行相應(yīng)的時(shí)間修改涤垫,最后對(duì)于每一個(gè)處理完成的事件調(diào)用deliverInputEvent,

private void deliverInputEvent(QueuedInputEvent q) {
     
     q.mEvent.getSequenceNumber());
     if (mInputEventConsistencyVerifier != null) {
         mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
      }

     InputStage stage;
     if (q.shouldSendToSynthesizer()) {
          stage = mSyntheticInputStage;
      } else {
          stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
      }

     if (stage != null) {
          stage.deliver(q);
      } else {
          finishInputEvent(q);
      }
 }

在事件分發(fā)環(huán)節(jié)姑尺,首先進(jìn)行事件的一個(gè)判斷,通過shouldSkipIme來判斷是否傳遞給輸入法蝠猬,然后決定使用何種InputStage進(jìn)行消息的繼續(xù)傳遞切蟋,這里實(shí)現(xiàn)了多種InputStage,對(duì)于每一個(gè)類型的InputStage都實(shí)現(xiàn)了一個(gè)方法process方法來針對(duì)不同類型的事件做處理榆芦,如果是觸摸屏類的消息柄粹,最終會(huì)將事件的處理轉(zhuǎn)交到View的身上。

InputStage中的事件如何傳遞處理匆绣,傳遞處理之后驻右,如何進(jìn)行

對(duì)于InputStage涉及的篇幅較多,這里也不再展開犬绒,當(dāng)消息到達(dá)ViewRootImpl中后旺入,接下來就是在View間的派發(fā)。

View的事件派發(fā)

對(duì)于View層的事件派發(fā)凯力,我們最常見的就是dispatchTouchEvent茵瘾,onTouch,onInterceptTouchEvent,onClickonTouchEvent等咐鹤。對(duì)于View樹上事件的派發(fā)拗秘,就是在對(duì)樹的遍歷傳遞中,主要起作用的就是這幾個(gè)函數(shù)祈惶。這里我們先從View的相關(guān)事件函數(shù)開始分析雕旨,由于ViewGroup具有子View的原因扮匠,其相關(guān)的事件派發(fā)邏輯和View有所區(qū)別,這里我們先進(jìn)行View的事件分析凡涩。

public boolean dispatchTouchEvent(MotionEvent event) {
    ....
    if (onFilterTouchEventForSecurity(event)) {
          if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
    ....
    return result棒搜;
}

對(duì)于在View上的事件派發(fā),核心操作是兩點(diǎn)活箕,一個(gè)是調(diào)用監(jiān)聽器的onTouch方法力麸,然后判斷事件是否被消耗,如果沒有被消耗育韩,則會(huì)調(diào)用onTouchEvent方法克蚂。在onTouchEvent中根據(jù)消息類型進(jìn)行一些處理。
根據(jù)事件類型來更新內(nèi)部的一些狀態(tài)筋讨。這里比較復(fù)雜的還是在ViewGroup中的事件分發(fā)邏輯埃叭,這里在分發(fā)的過程中,需要判斷是否對(duì)事件進(jìn)行攔截悉罕,如果不攔截赤屋,是否自身可處理,如果需要考慮到其中的子View蛮粮。這里對(duì)其中的關(guān)鍵代碼進(jìn)行逐步分析益缎。

final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
    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 {
        intercepted = true;
  }

判斷是否進(jìn)行攔截,調(diào)用自身的onInterceptTouchEvent然想,開發(fā)者可以重載這個(gè)方法進(jìn)行自己的一些操作莺奔。返回true表示攔截事件。如果要對(duì)事件進(jìn)行攔截变泄,則不再進(jìn)行子View的遍歷令哟。否則將會(huì)進(jìn)行子View的遍歷,事件傳遞妨蛹,在子View的事件傳遞結(jié)束之后屏富,如果子View將事件消耗了則會(huì)將其加入到mFirstTouchTarget,如果遍歷完成沒有任何被添加

if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } 

接下來進(jìn)行的是對(duì)于事件在子View中的派發(fā)蛙卤,這里我們也只是針對(duì)其中的核心代碼進(jìn)行分析狠半。

 final View[] children = mChildren;
 for (int i = childrenCount - 1; i >= 0; i--) {
       final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
       final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

        ....
      dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign);
    ....
}

在對(duì)子View進(jìn)行遍歷的處理的時(shí)候,如果子View的事件被消耗颤难,那么我們就會(huì)將其TouchTarget賦值給mFirstTouchTarget神年,當(dāng)檢測(cè)到mFirstTouchTarget為空時(shí)會(huì)再調(diào)用Viewgroup自身的dispatchTransformedTouchEvent方法,這個(gè)時(shí)候就會(huì)調(diào)用其onTouchEvent行嗤,然后繼續(xù)View中的事件傳遞流程已日。

if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
    handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
 }

對(duì)于dispatchTransformedTouchEvent函數(shù)

 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
  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;
        }
    ....  
}

這里事件的派發(fā)也是在調(diào)用了每一個(gè)子View的dispatchTouchEvent方法,根據(jù)返回結(jié)果來判斷是否被消耗栅屏,一旦事件被消耗則會(huì)停止傳遞飘千。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末堂鲜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子护奈,更是在濱河造成了極大的恐慌缔莲,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霉旗,死亡現(xiàn)場(chǎng)離奇詭異酌予,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奖慌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來松靡,“玉大人简僧,你說我怎么就攤上這事〉衿郏” “怎么了岛马?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)屠列。 經(jīng)常有香客問我啦逆,道長(zhǎng),這世上最難降的妖魔是什么笛洛? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任夏志,我火速辦了婚禮,結(jié)果婚禮上苛让,老公的妹妹穿的比我還像新娘沟蔑。我一直安慰自己,他們只是感情好狱杰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布瘦材。 她就那樣靜靜地躺著,像睡著了一般仿畸。 火紅的嫁衣襯著肌膚如雪食棕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天错沽,我揣著相機(jī)與錄音簿晓,去河邊找鬼。 笑死甥捺,一個(gè)胖子當(dāng)著我的面吹牛抢蚀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镰禾,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皿曲,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼唱逢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屋休,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤坞古,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后劫樟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痪枫,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年叠艳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奶陈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡附较,死狀恐怖吃粒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拒课,我是刑警寧澤徐勃,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站早像,受9級(jí)特大地震影響僻肖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卢鹦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一臀脏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冀自,春花似錦谁榜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荐糜,卻和暖如春巷怜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暴氏。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工延塑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人答渔。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓关带,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宋雏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 序言 最近在看Android觸摸屏事件相關(guān)的源碼芜飘,為了對(duì)整個(gè)事件體系的了解,所以對(duì)事件相關(guān)磨总,從事件的產(chǎn)生嗦明,寫入設(shè)備...
    Jensen95閱讀 719評(píng)論 0 3
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,101評(píng)論 25 707
  • 要求:不僅僅能實(shí)現(xiàn)相應(yīng)的功能,還需要保證代碼的魯棒性蚪燕,并且能夠分析代碼的空間復(fù)雜度和時(shí)間復(fù)雜度娶牌。 二維數(shù)組中的查找...
    二十四橋客_閱讀 975評(píng)論 0 1
  • 我的味蕾被提醒 下班回家的方向飛行 期待在車輪外圈歡騰 似乎在計(jì)算彼此心房的里程 纏繞在睫毛的白色蒸發(fā)了又結(jié)晶 視...
    李譯閱讀 879評(píng)論 2 7
  • 華姐,是我們一個(gè)案子的當(dāng)事人馆纳。 第一次見她诗良,是在事務(wù)所,當(dāng)時(shí)說好我去幫她調(diào)查取證來著鲁驶。 太遠(yuǎn)累榜,其實(shí)我不想去。 一 ...
    琴人彌茶閱讀 522評(píng)論 0 2