Android事件输入和传递系统

本文详细解析了Android系统中事件输入和传递的过程,从按键事件、触摸事件、鼠标事件到轨迹球事件,再到InputEvent、InputManagerService、InputDispatcher的角色。事件采集由Linux驱动支持,通过EventHub监听设备文件。InputDispatcher负责事件分发,使用InputChannel通过Socket在系统服务和应用进程间通信。事件处理涉及多个阶段,包括预处理、焦点窗口查找、ViewGroup和View的事件分发策略。最后,介绍了不同类型的事件如何在View树中分发以及被处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android事件输入和传递系统

事件的产生和传递

  • 按键事件
  • 触摸事件
  • 鼠标事件
  • 轨迹球事件

InputEvent 输入事件的基类,派生两个类型的子类KeyEvent和TouchEvent。

整个事件处理流程如下:
事件采集(由Linux驱动支持) -> 前期处理(提取有用信息) -> WindowManagerService分发 -> 应用程序处理

这里主要搞清楚两个问题:
1、输入事件是怎么监听到的?
2、监听到事件是怎么分发到应用程序中的?

InputManagerService也是在system进程启动的。

//frameworks.base.services.java.com.android.server.SystemServer.java

private void startOtherServices() {
    InputManagerService inputManager = null;
    inputManager = new InputManagerService(context);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
}
//java.com.android.server.input.InputManagerService.java

public void start() {
    Slog.i(TAG, "Starting input manager");
    //mPtr指针是NativeInputManager实例,在其构造函数中初始化了InputManager。
    //mPtr是在nativeInit函数中初始化的
    nativeStart(mPtr);
    ...
}

//构造函数
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);
   //初始化NativeInputManager,同时初始化了InputManager
   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());
}
//frameworks.base.services.core.jni.com_android_server_input_InputManagerService.cpp

//NativeInputManager构造函数,初始化InputManager
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }
    mInteractive = true;
    
    //EventHub用于监听输入事件
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    //启动InputManager,调用其start函数
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}
事件采集

Native层的InputManager在创建后,会启动两个线程InputRenderThread和InputDispatcherThread。前者负责从驱动节点读取Event,后者负责分发这些事件。

//frameworks.base.services.inputflinger.InputManager.cpp

//构造函数
InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //InputDispatcher负责事件分发
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    //InputReader负责事件采集,并发采集到事件经过处理后交给mDispatcher分发出去
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    //构建两个线程
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
    //启动两个线程,Android中Thread的实现就是pthread_create函数,
    //线程创建后最终会调用threadLoop()函数
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}
//frameworks.base.services.inputflinger.InputReader.cpp

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

void InputReader::loopOnce() {

    //可以看到其核心是通过EventHub监听事件输入的,那么EventHub是怎么做到的呢?
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
    if (count) {
        //处理事件
        processEventsLocked(mEventBuffer, count);
    }
    //将事件分发给监听者,也就是InputDispatcher
    mQueuedListener->flush();
}

1.遍历/dev/input目录,为输入事件文件添加监听。

//事件输入目录,该目录下的每个文件都对应一个输入设备,文件名称样式为event0、event1...等等
static const char *DEVICE_PATH = "/dev/input";

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        openDeviceLocked(devname);
    }
    closedir(dir);
    return 0;
}

//遍历出所有文件,并添加监听
status_t EventHub::openDeviceLocked(const char *devicePath) {
    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    ...
    //省略通过ioctl获取输入设备的名称、版本、标识、物理位置等信息。
    ...
    
    //根据设备文件属性构造Device实例
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    
    //然后判断是什么类型的输入设备(多点触控板、单点触控板、蓝牙书写笔、游戏摇杆等)
    //如果是键盘,还要加载键盘布局
    
    // 使用epoll监听文件输入事件
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = deviceId;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
        delete device;
        return -1;
    }
    
    //添加到Device队列头部
    addDeviceLocked(device);
}

那么,是什么时候触发的输入设备扫描呢?就是前面提到的getEvents函数中。

//读取输入事件,其中第二个参数RawEvent是一个输出参数,用来存储原始的事件数据
//返回值size_t代表读取到的事件个数
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    //
    struct input_event readBuffer[bufferSize];
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    
    for (;;) {
        
        //扫描输入设备
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        
        //监听设备文件发出输入事件
        //mPendingEventItems是一个长度为16的epoll_event数组
        //pollResult代表发生的事件个数
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        
        //赋值给mPendingEventCount,代表有等待处理的事件
        mPendingEventCount = size_t(pollResult);
        
        //循环处理epoll事件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            
            //如果是输入事件,则从设备文件中读取事件数据
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                        
                size_t count = size_t(readSize) / sizeof(struct input_event);
                for (size_t i = 0; i < count; i++) {
                    //将input_event转换成RawEvent
                   struct input_event& iev = readBuffer[i];
                   
                   event->deviceId = deviceId;
                   event->type = iev.type;
                   event->code = iev.code;
                   event->value = iev.value;
                   event += 1;
               }
            }
        }
    }
    
    //RawEvent修改前后的差值就是事件个数
    return event - buffer;
}
事件分发

InputReader获取到输入事件后,就会交给InputDispatcher去分发给合适的应用程序。

//构造函数
InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //InputDispatcher负责事件分发
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    //InputReader负责事件采集,并发采集到事件经过处理后交给mDispatcher分发出去
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputDispatcher在创建之初就与InputReader建立了联系。当InputReader通过EventHub拿到事件后,返回到loopOnce()函数后,继续执行mQueuedListener->flush(),mQueuedListener是InputListenerInterface接口类型子接口,内部添加了一个flush()函数,InputDispatcher类就继承了InputListenerInterface接口。

在分给应用程序之前,会对原始输入事件进

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值