Touch 事件原理分析 (一)

文章內(nèi)容主要從 https://segmentfault.com/a/1190000011826846 而來

1,InputManagerService

Android Framework 層的 service,大部分都是在 SystemServer 進程中創(chuàng)建,InputManagerService 即是如此,創(chuàng)建完 Service 后,便把它加入到 ServiceManager 中,并同時設置了 InputMonitor,如下

inputManager = new InputManagerService(context);
WindowManagerService wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();

看一下InputManagerservice 的構造函數(shù)

    public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

        String doubleTouchGestureEnablePath = context.getResources().getString(
                R.string.config_doubleTouchGestureEnableFile);
        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
            new File(doubleTouchGestureEnablePath);

        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }

nativeInit 的實現(xiàn)如下

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

首先是獲取 MessageQueue, 接著創(chuàng)建 NativeInputManager ,NativeInputManager 的實現(xiàn)如下

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
     ...

    mInteractive = true;
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager 中創(chuàng)建了 EventHub,并利用 EventHub 創(chuàng)建了InputManger, InputManager 實現(xiàn)如下

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

代碼中創(chuàng)建了 InputDispatcher,接著創(chuàng)建了 InputReader,接著是 initialize 方法

void InputManager::initialize() 
{
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

分別將 reader 和 dispatcher 封裝到相應的 Thread 中,此時對 SystemServer 來說,InputManager 基本創(chuàng)建完成,接著便是 start Service ,對應的 native 方法便是 nativeStart(mPtr)

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
}
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
}

2,小結

整體流程如下


整體流程圖

InputManger 通過 InputReaderThread 讀和處理未加工的輸入事件然后分發(fā)到 DispatcherThread 隊列中, InputDispatcherThread 將接收的隊列發(fā)送給相應的應用程序

3,epoll

這里首先了解一下 epoll,之前我們處理輸入流的時候,我們會對每一個流進行遍歷,然后檢測到有修改的數(shù)據(jù),將其取出來,這其中存在大量的資源消耗,尤其是在流比較多的時候,epoll 便在這里優(yōu)化,當無數(shù)據(jù)的時候會阻塞隊列,當有數(shù)據(jù)的時候,只將其中有變化的進行分發(fā)。
epoll更詳細的分析

4,EventHub

在 NativeInputManager 中,曾經(jīng)創(chuàng)建的EventHub,并作為參數(shù)傳遞給了 InputManger。EventHub是將不同來源的消息轉化為統(tǒng)一類型并交給上層處理。先查看其構造函數(shù)

EventHub::EventHub(void) :
      mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
      mOpeningDevices(0), mClosingDevices(0),
      mNeedToSendFinishedDeviceScan(false),
      mNeedToReopenDevices(false), mNeedToScanDevices(true),
      mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {

    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //創(chuàng)建一個epoll句柄
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    mINotifyFd = inotify_init();

    //監(jiān)視dev/input目錄的變化刪除和創(chuàng)建變化
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;

    //把inotify的句柄加入到epoll監(jiān)測
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    //創(chuàng)建匿名管道
    int wakeFds[2];
    result = pipe(wakeFds);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    //將管道的讀寫端設置為非阻塞
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

    eventItem.data.u32 = EPOLL_ID_WAKE;

    //將管道的讀端加入到epoll監(jiān)測
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kerel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

其中創(chuàng)建了 epoll 句柄、inotify 句柄、匿名管道(非阻塞),inotify負責監(jiān)控目錄和文件的變化,這里監(jiān)控的是/dev/input 目錄。
EventHub 負責監(jiān)控,相關事件的處理由 ReaderThread 處理。ReaderThread 是繼承Android 的 Thread

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

這個方法返回 true , 表示 threadLoop會被循環(huán)調(diào)用,也就是 loopOnce 會被循環(huán)調(diào)用,下面是 loopOnce

void InputReader::loopOnce() {
      .....
    //從EventHub中獲取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
        //如果讀到數(shù)據(jù),處理事件數(shù)據(jù)
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

   //將排隊的事件隊列發(fā)送給監(jiān)聽者,實際上這個監(jiān)聽者就是Input dispatcher
    mQueuedListener->flush();
}

我們要分析的是其中的兩個方法 getEvents 和 processEventsLocked

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
     for(;;) {
        ....
      while (mPendingEventIndex < mPendingEventCount) {
         const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
        .....
       ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
     if (eventItem.events & EPOLLIN) {
         int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
        if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
           // 設備被移除,關閉設備
           deviceChanged = true;
           closeDeviceLocked(device);
         } else if (readSize < 0) {
             //無法獲得事件
             if (errno != EAGAIN && errno != EINTR) {
                 ALOGW("could not get event (errno=%d)", errno);
             }
         } else if ((readSize % sizeof(struct input_event)) != 0) {
            //獲得事件的大小非事件類型整數(shù)倍
            ALOGE("could not get event (wrong size: %d)", readSize);
       } else {
           int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
          //計算讀入了多少事件
           size_t count = size_t(readSize) / sizeof(struct input_event);
           for (size_t i = 0; i < count; i++) {
               struct input_event& iev = readBuffer[i];
               if (iev.type == EV_MSC) {
                 if (iev.code == MSC_ANDROID_TIME_SEC) {
                     device->timestampOverrideSec = iev.value;
                     continue;
                  } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                     device->timestampOverrideUsec = iev.value;
                     continue;
                  }
               }
              //事件時間相關計算,時間的錯誤可能會導致ANR和一些bug。這里采取一系列的防范 
               .........
             event->deviceId = deviceId;
             event->type = iev.type;
             event->code = iev.code;
             event->value = iev.value;
             event += 1;
             capacity -= 1;
          }
        if (capacity == 0) {
          //每到我們計算完一個事件,capacity就會減1,如果為0。則表示  結果緩沖區(qū)已經(jīng)滿了,
      //需要重置開始讀取時間的索引值,來讀取下一個事件迭代                    
           mPendingEventIndex -= 1;
           break;
      }
 } 
    //表明讀到事件了,跳出循環(huán)
    if (event != buffer || awoken) {
            break;
     }
     mPendingEventIndex = 0;
     int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
       if (pollResult == 0) {
          mPendingEventCount = 0;
          break;
       }
      //判斷是否有事件發(fā)生
       if (pollResult < 0) {
          mPendingEventCount = 0;
        } else {
            //產(chǎn)生的事件的數(shù)目
          mPendingEventCount = size_t(pollResult);
        }
    }
    //產(chǎn)生的事件數(shù)目
    return event - buffer;
}
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;
    }
}

getEvents 方法會進行一些新增設備和移除設備的更新操作。至于點擊事件是通過指針參數(shù) RawEvent, 其作為起始地址記錄事件,在循環(huán)體中,處理獲取時間、檢測相關設備類型、讀取事件,如果檢測到事件,則跳出循環(huán)。更新 mPendingEventCount 和 mPendingEventIndex 來控制事件的讀取,epoll_wait 來得到事件的來源。
在 looperOnce 獲取到事件后,就會調(diào)用 processEventsLocked。其負責事件添加、設備移除等,事件相關還是 processEventsForDeviceLocked 方法,根據(jù)事件獲取相應的設備類型,并交給相應的設備處理,即 InputMapper 。


InputMapper及其子類

在這里,事件會被 mapper->process 處理

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);
    }
}
void TouchInputMapper::sync(nsecs_t when) {
    .....
    processRawTouches(false /*timeout*/);
}
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
      ....
    dispatchMotion();
      ....
}
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);
}
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

此時數(shù)據(jù)被加入到參數(shù)隊列中,此時回到 loopOnce 中,調(diào)用 QueuedInputListener 的 flush 方法

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();
}
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}
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();
   }
}

在 notifyMotion 中將參數(shù)包裝成 MotionEntry,加入到 enqueueInboundEventLocked 中,然后喚醒 looper。
Dispatcher 下的 dispatchOnce

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
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;
    }
    ....
   }
}
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);
        } 
    }
}

prepareDispatchCycleLocked 調(diào)用 enqueueDispatchEntriesLocked 調(diào)用 startDispatchCycleLocked

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;
    }
    ....
   }
    ...
}
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);
}
具體流程圖
整體流程圖

ReaderThread 開啟后會從EventHub輪訓獲取時間,獲取事件后,經(jīng)過一系列的封裝,通過 InputChannel 發(fā)送出去

Touch 事件原理分析 (下)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 序言 最近在看Android觸摸屏事件相關的源碼,為了對整個事件體系的了解,所以對事件相關,從事件的產(chǎn)生,寫入設備...
    Jensen95閱讀 807評論 0 3
  • 前言 之前項目中涉及到對于Android事件的轉發(fā)處理相關的需求,因此對于這部分的系統(tǒng)源碼進行了分析,從寫入設備文...
    Jensen95閱讀 781評論 0 1
  • 必備的理論基礎 1.操作系統(tǒng)作用: 隱藏丑陋復雜的硬件接口,提供良好的抽象接口。 管理調(diào)度進程,并將多個進程對硬件...
    drfung閱讀 3,763評論 0 5
  • 第3章 基本概念 3.1 語法 3.2 關鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,514評論 0 21
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,663評論 1 32

友情鏈接更多精彩內(nèi)容