| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
guoyi0591
7年前发布

Android系统源码剖析-事件分发

   <h2>前言</h2>    <p>对于事件的监控和获取做了分析,在拿到事件之后,后续是如何处理分发的呢?本篇文章主要针对在通过getEvent获取到事件之后,后续的相关分发处理流程。</p>    <p><img src="https://simg.open-open.com/show/ae1beac840d72b79ed96d080bda5f873.png"></p>    <p>InputReaderThread函数不断地调用looperOnce函数,不断的从中读取事件,那么下一个问题来了,读取到事件要放置到哪里,又在哪里被消耗掉了呢?也就是事件接下来的流向问题。让我们回到looperOnce之前。</p>    <h2>事件分发</h2>    <p>processEventsLocked</p>    <p>在调用了getEvent之后,又调用了函数 processEventsLocked</p>    <pre>  <code class="language-java">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;      }  }</code></pre>    <p>首先对于事件类型进行了判断,如果事件不是合成事件,则会对其DeviceID进行判断,通过对其判断来确定batchSize等,然后对其进行处理,对于其他的类型事件,则会具体判断,判断是设备的添加,设备的移除,完成设备扫描等等,然后对事件分别进行处理,这里我们只关心对于设备自身产生的事件。也就是触摸屏相关的事件。也就是 processEventsForDeviceLocked 函数中所进行的操作。</p>    <p>事件派发到Device</p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>根据事件获得相应的设备类型,然后将事件交给相应的设备处理。判断是否忽略该事件,如果不是忽略该事件,则会调用相应设备的process方法进行处理。</p>    <p>事件派发到InputMapper</p>    <p>InputDevice的process方法</p>    <pre>  <code class="language-java">void InputDevice::process(const RawEvent* rawEvents, size_t count) {       ....      for (size_t i = 0; i < numMappers; i++) {             InputMapper* mapper = mMappers[i];             mapper->process(rawEvent);      }      ....  }</code></pre>    <p>这里的事件又交给了InputMapper来处理</p>    <p><img src="https://simg.open-open.com/show/eb7fec2cf3cc15fc4beec5186c1f5ab2.png"></p>    <p>InputMapper对应了很多的子类,这里根据事件的类型进行相应的派发,处理。</p>    <p>事件到了这里之后,如何传递到应用层,这里mapper->process进行了那些处理。这里来看一下对于触摸屏事件的处理函数。</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>通过这里的函数处理,我们继续追踪函数的数据流向。对于相关的事件处理,这里最终执行的是将</p>    <pre>  <code class="language-java">void TouchInputMapper::sync(nsecs_t when) {      .....      processRawTouches(false /*timeout*/);  }</code></pre>    <pre>  <code class="language-java">void TouchInputMapper::processRawTouches(bool timeout) {      if (mDeviceMode == DEVICE_MODE_DISABLED) {          // Drop all input if the device is disabled.          mCurrentRawState.clear();          mRawStatesPending.clear();          return;      }        // Drain any pending touch states. The invariant here is that the mCurrentRawState is always      // valid and must go through the full cook and dispatch cycle. This ensures that anything      // touching the current state will only observe the events that have been dispatched to the      // rest of the pipeline.      const size_t N = mRawStatesPending.size();      size_t count;      for(count = 0; count < N; count++) {          const RawState& next = mRawStatesPending[count];            // A failure to assign the stylus id means that we're waiting on stylus data          // and so should defer the rest of the pipeline.          if (assignExternalStylusId(next, timeout)) {              break;          }            // All ready to go.          clearStylusDataPendingFlags();          mCurrentRawState.copyFrom(next);          if (mCurrentRawState.when < mLastRawState.when) {              mCurrentRawState.when = mLastRawState.when;          }          cookAndDispatch(mCurrentRawState.when);      }      if (count != 0) {          mRawStatesPending.removeItemsAt(0, count);      }        if (mExternalStylusDataPending) {          if (timeout) {              nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;              clearStylusDataPendingFlags();              mCurrentRawState.copyFrom(mLastRawState);              cookAndDispatch(when);          } else if (mExternalStylusFusionTimeout == LLONG_MAX) {              mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;              getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);          }      }  }</code></pre>    <p>在相关的函数调用之后,最终调用了 dispatchTouches</p>    <pre>  <code class="language-java">void TouchInputMapper::cookAndDispatch(nsecs_t when) {      // Always start with a clean state.      mCurrentCookedState.clear();        // Apply stylus buttons to current raw state.      applyExternalStylusButtonState(when);        // Handle policy on initial down or hover events.      bool initialDown = mLastRawState.rawPointerData.pointerCount == 0              && mCurrentRawState.rawPointerData.pointerCount != 0;        uint32_t policyFlags = 0;      bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;      if (initialDown || buttonsPressed) {          // If this is a touch screen, hide the pointer on an initial down.          if (mDeviceMode == DEVICE_MODE_DIRECT) {              getContext()->fadePointer();          }            if (mParameters.wake) {              policyFlags |= POLICY_FLAG_WAKE;          }      }        // Consume raw off-screen touches before cooking pointer data.      // If touches are consumed, subsequent code will not receive any pointer data.      if (consumeRawTouches(when, policyFlags)) {          mCurrentRawState.rawPointerData.clear();      }        // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure      // with cooked pointer data that has the same ids and indices as the raw data.      // The following code can use either the raw or cooked data, as needed.      cookPointerData();        // Apply stylus pressure to current cooked state.      applyExternalStylusTouchState(when);        // Synthesize key down from raw buttons if needed.      synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,              policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);        // Dispatch the touches either directly or by translation through a pointer on screen.      if (mDeviceMode == DEVICE_MODE_POINTER) {          for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);                  !idBits.isEmpty(); ) {              uint32_t id = idBits.clearFirstMarkedBit();              const RawPointerData::Pointer& pointer =                      mCurrentRawState.rawPointerData.pointerForId(id);              if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS                      || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {                  mCurrentCookedState.stylusIdBits.markBit(id);              } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER                      || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {                  mCurrentCookedState.fingerIdBits.markBit(id);              } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {                  mCurrentCookedState.mouseIdBits.markBit(id);              }          }          for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);                  !idBits.isEmpty(); ) {              uint32_t id = idBits.clearFirstMarkedBit();              const RawPointerData::Pointer& pointer =                      mCurrentRawState.rawPointerData.pointerForId(id);              if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS                      || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {                  mCurrentCookedState.stylusIdBits.markBit(id);              }          }            // Stylus takes precedence over all tools, then mouse, then finger.          PointerUsage pointerUsage = mPointerUsage;          if (!mCurrentCookedState.stylusIdBits.isEmpty()) {              mCurrentCookedState.mouseIdBits.clear();              mCurrentCookedState.fingerIdBits.clear();              pointerUsage = POINTER_USAGE_STYLUS;          } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {              mCurrentCookedState.fingerIdBits.clear();              pointerUsage = POINTER_USAGE_MOUSE;          } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||                  isPointerDown(mCurrentRawState.buttonState)) {              pointerUsage = POINTER_USAGE_GESTURES;          }            dispatchPointerUsage(when, policyFlags, pointerUsage);      } else {          if (mDeviceMode == DEVICE_MODE_DIRECT                  && mConfig.showTouches && mPointerController != NULL) {              mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);              mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);                mPointerController->setButtonState(mCurrentRawState.buttonState);              mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,                      mCurrentCookedState.cookedPointerData.idToIndex,                      mCurrentCookedState.cookedPointerData.touchingIdBits);          }            if (!mCurrentMotionAborted) {              dispatchButtonRelease(when, policyFlags);              dispatchHoverExit(when, policyFlags);              dispatchTouches(when, policyFlags);              dispatchHoverEnterAndMove(when, policyFlags);              dispatchButtonPress(when, policyFlags);          }            if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {              mCurrentMotionAborted = false;          }      }        // Synthesize key up from raw buttons if needed.      synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,              policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);        // Clear some transient state.      mCurrentRawState.rawVScroll = 0;      mCurrentRawState.rawHScroll = 0;        // Copy current touch to last touch in preparation for the next cycle.      mLastRawState.copyFrom(mCurrentRawState);      mLastCookedState.copyFrom(mCurrentCookedState);  }</code></pre>    <pre>  <code class="language-java">void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {        ....      dispatchMotion();        ....  }</code></pre>    <p>对于dispatchTouches中,会根据记录的上一次的触摸位置,对事件的类型进行判断,然后做相应的分发,事件类型有抬起,下落,移动等,然后对相应的事件进行分发。无论是对于何种类型的事件派发,最终被调用到的都是 dispatchMotion() 方法。</p>    <p>对于相关事件的分发最终调用到了dispatchMotion(),对事件数据进行组装之后,调用了</p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>getListener函数</p>    <pre>  <code class="language-java">InputListenerInterface* InputReader::ContextImpl::getListener() {      return mReader->mQueuedListener.get();  }</code></pre>    <p>notifyMotion函数实现</p>    <pre>  <code class="language-java">void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {      mArgsQueue.push(new NotifyMotionArgs(*args));  }</code></pre>    <p>这里可以看到,我们将触摸相关的事件进行包装之后,将其加入到一个ArgsQueue队列,到此,我们已经将数据加入到参数队列中,到此事件从设备文件获取到写入流程已经完成,这里让我们再回到loopOnce方法中,最后调用了 QueuedInputListener 的 flush 方法,</p>    <pre>  <code class="language-java">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();  }</code></pre>    <p>NotifyArgs的notify函数实现</p>    <pre>  <code class="language-java">void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {      listener->notifyMotion(this);  }</code></pre>    <p>对于这个listener的创建来自于InputReader构建的时候。</p>    <pre>  <code class="language-java">mQueuedListener = new QueuedInputListener(listener);</code></pre>    <pre>  <code class="language-java">mReader = new InputReader(eventHub, readerPolicy, mDispatcher);</code></pre>    <p>而这里的Listener则是 InputDispatcher ,InputDispatcher 的notifyMotion实现源码。</p>    <pre>  <code class="language-java">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();     }  }</code></pre>    <p>在该函数中,所做的事情是对于所传递的参数,构造MotionEntry,然后将其加入到enqueueInboundEventLocked之中。然后唤醒其中的looper。</p>    <pre>  <code class="language-java">bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {      bool needWake = mInboundQueue.isEmpty();      mInboundQueue.enqueueAtTail(entry);      ...      //进行一些事件和窗口相关的判断处理  }</code></pre>    <p>Dispatcher开启的线程中,每次循环的操作如何?</p>    <pre>  <code class="language-java">bool InputDispatcherThread::threadLoop() {      mDispatcher->dispatchOnce();      return true;  }</code></pre>    <p>Dispatcher下dispatchOnce的实现</p>    <pre>  <code class="language-java">void InputDispatcher::dispatchOnce() {      ...     dispatchOnceInnerLocked(&nextWakeupTime);      ...  }</code></pre>    <pre>  <code class="language-java">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;      }      ....     }  }</code></pre>    <p>从mInboudQueue中,获取到事件,然后对事件类型进行判断,判断之后调用了dispatchMotionLocked函数,来继续进行事件的传递。</p>    <pre>  <code class="language-java">bool InputDispatcher::dispatchMotionLocked(          nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {        ....        Vector<InputTarget> inputTargets;  if (isPointerEvent) {          // Pointer event.  (eg. touchscreen)        //寻找目标窗口          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;  }</code></pre>    <ul>     <li>dispatchEventLocked</li>    </ul>    <pre>  <code class="language-java">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);          }       }  }</code></pre>    <p>获得目标输入,根据InputChannel获取相应的连接,然后调用prepareDispatchCycleLocked(),进行事件的派发。</p>    <p>enqueueDispatchEntriesLocked,在该方法中又调用了startDispatchCycleLocked方法。其实现为</p>    <pre>  <code class="language-java">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;      }      ....     }      ...  }</code></pre>    <p>至此调用了connection 的inputPublisher的publishMotionEvent方法将事件分发消耗。</p>    <p>InputPublisher定义在InputTransport.cpp中</p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>该方法所执行的操作是利用传入的触摸信息,构建点击消息,然后通过InputChannel将消息发送出去。这里引出了InputChannel,在此,我们通InputPublisher的创建反推出InputChannel是何时被引入的,何时被创建的。从而进一步分析其作用。在分析之前先让我们来对上述的分析过程做一个总结。</p>    <p><img src="https://simg.open-open.com/show/75e22b4ee48b4ead08f1442abce9d627.png"></p>    <p>ReaderThread开启后会从EventHub中轮询获取时间,获取到事件之后,对手将进行一系列的处理,最终将经过一系列处理封装的事件信息通过InputChannel发送出去。</p>    <p><img src="https://simg.open-open.com/show/a03d601bc5a1dd2d2b3552aa72a8cadf.png"></p>    <p>到此,对于输入事件,我们已经分析到了InputChannel,对于其上的具体分析转化,将是接下来分析的核心。</p>    <p>InputChannel</p>    <p>从上面分析可以看到事件传递部分最后是通过InputChannel所发送出去的,那么InputChannel是在何时被创建的呢?何时被InputManager所使用的呢?同时,InputReaderThread和InputDispatcherThread是运行在SystemServer进程中的,而我们的应用进程是和其不在同一个进程中的。这之间一定也是有进程间的通信机制在里面。具体又是如何实现的呢?对于InputChannel这边是事件传递过程中一个比较核心的点。</p>    <p>InputChannel的创建是在 ViewRootImpl 中 setView 方法中。</p>    <pre>  <code class="language-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();     }    ....    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                              getHostVisibility(), mDisplay.getDisplayId(),                              mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                              mAttachInfo.mOutsets, mInputChannel);    ....  }</code></pre>    <p>这里对于ViewRootImpl和WindowSession相关暂且不介绍,对于这方面的知识,需要很大的篇幅来介绍,这里先只是讲到是在这里创建的,对于其相关的内容将在后续的文章中介绍。这里首先是创建了一个InputChannel,然后将其调用了 WindowSession 的 addToDisplay 方法将其作为参数传递。</p>    <pre>  <code class="language-java">public InputChannel() {  }</code></pre>    <p>在InputChannel中的方法都为调用了相应的native方法。这里调用的addToDisplay将会把InputChannel添加到WindowManagerService中。会调用WMS的 addWindow 方法。</p>    <pre>  <code class="language-java">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);        }        ....  }</code></pre>    <p>对于InputChannel的相关处理调用了WindowState的openInputChannel方法。</p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>首先调用了InputChannel的 openInputChannelPair 方法,该方法调用了InputChannel的native方法 nativeOpenInputChannelPair ,创建了两个 InputChannel ,对其中一个通过 InputManager 进行了InputChannel的注册。对于 InputChannel 的相关Native的实现是在InputTransport中, nativeOpenInputChannelPair 的源码如下。</p>    <pre>  <code class="language-java">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;  }</code></pre>    <p>这里的Socket创建用到了 Linux中的Socketpaire,在之前的版本中是通过管道来实现的,但是 创建的管道只能是单向的 -- mode 只能是 "r" 或 "w" 而不能是某种组合--用户只能选择要么往里写,要么从中读,而不能同时在一个管道中进行读写。实际应用中,经常会有同时进行读写的要求。</p>    <p>Linux实现了一个源自BSD的socketpair调用可以实现上述在同一个文件描述符中进行读写的功能(该调用目前也是POSIX规范的一部分 。该系统调用能创建一对已连接的(UNIX族)无名socket。在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写。</p>    <p>socketpair产生的文件描述符是一对socket,socket上的标准操作都可以使用,其中也包括shutdown。——利用shutdown,可以实现一个半关闭操作,通知对端本进程不再发送数据,同时仍可以利用该文件描述符接收来自对端的数据。</p>    <pre>  <code class="language-java">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);       .....      return OK;  }</code></pre>    <p>接收消息,通过读socket的方式来读取消息。</p>    <pre>  <code class="language-java">status_t InputChannel::receiveMessage(InputMessage* msg) {      ssize_t nRead;      do {          nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);      } while (nRead == -1 && errno == EINTR);      ......      return OK;  }</code></pre>    <p>通过之前的分析,我们已经追踪到了通过InputChannel发送消息的代码,接收端的消息处理是如何呢?是如何触发开始接收消息,消息如何在传到InputChannel之后,进行的进一步的数据传递呢?分析之前,这里先对上面InputChannel进行一个总结。</p>    <p><img src="https://simg.open-open.com/show/d0fc4e82fafc5ca6d15c0f78939ee860.png"></p>    <p>之前的 setView 中,我们创建了InputChannel之后,开启了对于InputChannel中输入事件的监听。</p>    <pre>  <code class="language-java">if (mInputChannel != null) {     if (mInputQueueCallback != null) {         mInputQueue = new InputQueue();         mInputQueueCallback.onInputQueueCreated(mInputQueue);    }     mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,                              Looper.myLooper());  }</code></pre>    <p>WindowInputEventReceiver的构造函数如下,其继承自InputEventReceiver。</p>    <pre>  <code class="language-java">final class WindowInputEventReceiver extends InputEventReceiver {       public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {           super(inputChannel, looper);       }        ....  }</code></pre>    <p>InputEventReceiver的构造函数源码如下</p>    <pre>  <code class="language-java">public InputEventReceiver(InputChannel inputChannel, Looper looper) {       ....       mInputChannel = inputChannel;       mMessageQueue = looper.getQueue();       mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),                  inputChannel, mMessageQueue);    }</code></pre>    <p>这里调用了native方法来做初始化,相关的native方法的实现在 android_view_InputEventReceiver.cpp</p>    <pre>  <code class="language-java">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();    .....  }</code></pre>    <p>根据传入的 InputChannel 和 MessageQueue ,创建一个NativeInputEventReceiver,然后调用其 initialize 方法。</p>    <pre>  <code class="language-java">status_t NativeInputEventReceiver::initialize() {      setFdEvents(ALOOPER_EVENT_INPUT);      return OK;  }</code></pre>    <p>在 initialize() 方法中,只调用了一个函数 setFdEvents ,</p>    <pre>  <code class="language-java">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);          }      }  }</code></pre>    <p>从InputConsumer中获取到channel的fd,然后调用Looper的 addFd 方法。</p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>Looper的addFd的实现如下</p>    <pre>  <code class="language-java">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);         }   }</code></pre>    <p>该方法所执行的操作就是对传递的fd添加epoll监控,Looper会循环调用 pollOnce 方法,而 pollOnce 方法的核心实现就是 pollInner 。其代码大致实现内容为等待消息的到来,当有消息到来后,根据消息类型做一些判断处理,然后调用其相关的callback。我们当前是对于开启的socket的一个监听,当有数据到来,我们便会执行相应的回调。这里对于InputChannel的回调是在调用了NativeInputEventReceiver的 handleEvent 方法。</p>    <pre>  <code class="language-java">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;  }</code></pre>    <p>对于Event的处理,这里调用consumeEvents来对事件进行处理。</p>    <pre>  <code class="language-java">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);          ...      }     ...  }</code></pre>    <p>InputConsumer是在InputTransport中做的声明。</p>    <pre>  <code class="language-java">status_t InputConsumer::consume(InputEventFactoryInterface* factory,          bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {      while (!*outEvent) {           ....           status_t result = mChannel->receiveMessage(&mMsg);            ....      }  }</code></pre>    <p>调用consume方法会持续的调用InputChannel的receiveMessage方法来从socket中读取数据。到这里,我们已经将写入socket的事件读出来了。InputChannel在创建之后,通过为其InputEventReceiver对其fd进行epoll监控,当有变动的时候,调用InputChannel来接收消息。InputChannel又是如何被设置到InputDispatcher之中的呢?在调用 openInputChannel 方法,创建Socket的完成之后,调用该方法。</p>    <pre>  <code class="language-java">public void registerInputChannel(InputChannel inputChannel,              InputWindowHandle inputWindowHandle) {     if (inputChannel == null) {        throw new IllegalArgumentException("inputChannel must not be null.");     }     nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);  }</code></pre>    <p>nativeRegisterInputManger</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>NativeInputManager的 registerInputChannel 还会调用到InputDispatcher的registerInputChannel,会通过InputChannel创建相应的Connection,同时将InputChannel加入到相应的监控之中。在上面对代码的分析之中,获取InputChannel,就是通过这个Connection来获取的。</p>    <pre>  <code class="language-java">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;  }</code></pre>    <p>经过InputManager的层层传递,最终会到达InputDispatcher之中,然后对其进行封装,并在其内部进行保存,同时也传递了相应的窗口的句柄,方便了后期在事件传递的时候,对于窗口的判断。</p>    <p>ViewRootImpl</p>    <p>事件在从socket读出之后,经过传递,最终会调用到ViewRootImpl的 enqueueInputEvent 方法。</p>    <pre>  <code class="language-java">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();        }   }</code></pre>    <p>enqueueInputEvent 方法从InputEventReceiver中获取到InputEvent,然后将其加入到当前的事件队列之中,最后调用 doProcessInputEvents 来进行处理。</p>    <pre>  <code class="language-java">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);          }            if (mProcessInputEventsScheduled) {              mProcessInputEventsScheduled = false;              mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);          }      }</code></pre>    <p>遍历所有的消息,如果事件类型为触摸屏事件,对其进行相应的时间修改,最后对于每一个处理完成的事件调用 deliverInputEvent ,</p>    <pre>  <code class="language-java">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);        }   }</code></pre>    <p>在事件分发环节,首先进行事件的一个判断,通过shouldSkipIme来判断是否传递给输入法,然后决定使用何种InputStage进行消息的继续传递,这里实现了多种InputStage,对于每一个类型的InputStage都实现了一个方法 process 方法来针对不同类型的事件做处理,如果是触摸屏类的消息,最终会将事件的处理转交到View的身上。</p>    <p>InputStage中的事件如何传递处理,传递处理之后,如何进行</p>    <p>对于InputStage涉及的篇幅较多,这里也不再展开,当消息到达ViewRootImpl中后,接下来就是在View间的派发。</p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000012860933</p>    <p> </p>    
 本文由用户 guoyi0591 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1516156500920.html
Socket 源码分析 安卓开发 Android开发 移动开发