Android事件输入和传递系统

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接口。

在分给应用程序之前,会对原始输入事件进行处理,每种事件类型都对应一个InputMapper,InputMapper实现不同类型输入事件的转换。以键盘输入事件为例:

//frameworks.base.services.inputflinger.InputReader.cpp

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    //区分不同的事件类型
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;

        if (isKeyboardOrGamepadKey(scanCode)) {
            //如果是按键,要根据扫描码转换成按键码
            //rawEvent->value代表按下或抬起
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    ...
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
    
    //由EventHub将扫描码scan转换成按键码,因为EventHub知道加载的什么样的键盘布局
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }
    
    ...
    
    //将按键事件封装成NotifyKeyArgs,并添加到QueuedInputListener的队列中
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

//frameworks.base.services.inputflinger.InputListener.cpp

Vector<NotifyArgs*> mArgsQueue;
//添加到队列中
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

//添加到队列后,当再调用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();
}

//实际上就是调用InputListenerInterface的notifyKey函数,这里也就是InputDispatcher类
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}
//frameworks.base.services.inputflinger.InputDispatcher.cpp

//InputDispatcher本身是一个线程,里面有一个循环队列Looper,添加事件后就开始处理,否则就阻塞。
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    bool needWake;
    //又将NotifyKeyArgs变成KeyEntry类型,并添加队列末尾中。
    KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

    needWake = enqueueInboundEventLocked(newEntry);
    //如果当前循环处于阻塞状态,则需要重新唤醒。
    if (needWake) {
        mLooper->wake();
    }
}

//线程循环,调用dispatchOnce函数,之后又会根据事件类型走不同的分发路径,按键事件对应dispatchKeyLocked函数
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    
    //1. 处理重复按键。重复按键行为可能是硬件产生的,也可能是软件模拟的。
    //如果重复按键2次,则标识为长按事件
    
    //2. 上次的拦截策略告诉我们要稍后重试
    
    //3. 让拦截策略有机会拦截按键事件。这个拦截策略是由Java层的PhoneWindowManager interceptKeyBeforeDispatching方法实现的,返回0则继续分发,返回-1则停止分发。
    //让系统可以处理一些特殊的按键事件。例如Home按键、音量键、截屏键等
    
    //4.继续分发,找到当前获取焦点的窗口,然后分发给他。
    Vector<InputTarget> inputTargets;//inputTargets是个输出参数
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

其实InputDispatcher中是持有当前焦点窗口引用的mFocusedWindowHandle,findFocusedWindowTargetsLocked函数只是判断了前端的窗口是否符合事件分发条件,例如是否正在启动、是否处于暂停状态、是否有权限限制。

那么InputDispatcher是怎么知道当前焦点窗口的呢?
答案是通过InputMonitor与WindowManagerService建立起联系,从而获得窗口信息,或者将按键事件反馈给WindowManagerService。

其实WindowManagerService中就包含了InputManagerService对象,为什么要再通过InputMonitor转发一次呢?

因为在把当前焦点变更的窗口告知InputManagerService的时候,有一些逻辑操作,为了避免将这些逻辑耦合到WindowManagerService中,所以由InputMonitor处理。

//WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    final InputManagerService mInputManager;
    final InputMonitor mInputMonitor = new InputMonitor(this);
    
    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        ... 
        
        //添加窗口时,通知InputMonitor窗口变更
        if (focusChanged) {
           mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
       }
       mInputMonitor.updateInputWindowsLw(false /*force*/);
    }
}
//InputMonitor.java

//将缓存的窗口信息提供给事件输入分发器
public void updateInputWindowsLw(boolean force) {
    // Send windows to native code.
    //又交给InputManagerService去更新,最终会传递给InputDispatcher
    mService.mInputManager.setInputWindows(mInputWindowHandles);
}

好了,到现在InputDispatcher知道该把事件分发给哪个Window了,因为InputManagerService和应用窗口不在同一个进程,那么是怎么把按键消息传递过去的呢?

答案是基于虚拟文件系统的Socket。

//InputTransport.cpp

//通过Unix Domain Socket实现两个进程的通信
status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    //socketpair函数创建Socket的两个端,sockets就是两个文件描述符
    //通过socketpair调用得到两个文件句柄,假设这两个文件句柄是fd1和fd2,这两个文件都对应有两个
    //缓冲区(send_buf、rcv_buf),当某个进程或线程通过fd1写到他的send_buf的时候,内核里面的
    //socket就会把send_buf里面的数据写到fd2的rcv_buf里面,另外一个线程或进程就可以读取fd2得
    //到那些数据了,相反同理。
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }
    
    //设置Socket的读写缓冲区
    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));

    //分别为Socket的两端添加后缀标识,(server)和(client)
    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;
}

InputChannel的创建是在应用进程请求WindowManagerService添加窗口时触发的。Socket的两个文件创建好后,会将其中一个返回给应用进程,应用进程通过监听这个文件描述符就可以接收到InputDispatcher发送过来的消息了。
创建好Socket之后,又将其封装成InputChannel。之后又将InputChannel注册到InputDispatcher中。

//WindowState.java

void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        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);
        }
        
        //注册到InputDispatcher中
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
//InputDispatcher.cpp

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

        //已经注册过
        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }

        //构造成一个Connection类型
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        //以文件描述符为键,添加到KeyedVector集合中
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
        
        //添加输入事件回调函数
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // 有新的客户端连接也要唤醒线程循环
    mLooper->wake();
    return OK;
}

好,注册完之后再回到dispatchEventLocked中。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
 
    //inputTargets是找到的拥有焦点的窗口,从mConnectionsByFd集合中找到对应的Connection就可以发送事件了。
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

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

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
        
    //循环发送事件
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
        
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

       // 分发按键事件
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
        ...
    }
}
//InputTransport.cpp

//将按键信息拼装成InputMessage交由Socket发送
tatus_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {
    if (!seq) {
        ALOGE("Attempted to publish a key event with sequence number 0.");
        return BAD_VALUE;
    }

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    //调用InputChannel的sendMessage函数发送InputMessage
    return mChannel->sendMessage(&msg);
}

status_t InputChannel::sendMessage(const InputMessage* msg) {
    onst size_t msgLength = msg->size();
    InputMessage cleanMsg;
    //复制一份消息
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        //调用Socket的read函数发送消息
        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    
    return OK;
}

InputDispatcher通过Socket发送按键事件,应用进程是如何接收的呢?
InputChannel的创建时在setView方法中触发的,创建成功后会同时创建一个事件队列和一个窗口输入事件接口器,前者用来存储InputDispatcher发送过来的事件,后者用来实现Socket通信。

//ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
    
    //声明一个未初始化的InputChannel
    if ((mWindowAttributes.inputFeatures
        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
          mInputChannel = new InputChannel();
    }
    
    //通过WindowManagerService初始化InputChannel,mInputChannel是一个输出参数
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), 
                            mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, 
                            mInputChannel);
    
    //根据InputChannel生成事件接收器
    if (mInputChannel != null) {
         if (mInputQueueCallback != null) {
             mInputQueue = new InputQueue();
             mInputQueueCallback.onInputQueueCreated(mInputQueue);
         }
         //事件接收器
         mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                 Looper.myLooper());
     }       
}

应用进程是怎么获取到输入事件的呢?
前面说到InputDispatcher通过socketpair创建了两个文件描述符,并把其中一个返回给了应用进程。应用进程则使用epoll函数等待这个文件描述符发生写入事件,从而实现进程间通信。

当应用进程处理完一个输入事件后,还要告诉InputDispatcher处理完成了,因为InputDispatcher还需要计算输入事件的处理时间,判断是否产生ANR。

WindowInputEventReceiver继承自InputEventReceiver,它的构造函数如下:

//InputEventReceiver.java

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
   if (inputChannel == null) {
       throw new IllegalArgumentException("inputChannel must not be null");
   }
   if (looper == null) {
       throw new IllegalArgumentException("looper must not be null");
   }

   mInputChannel = inputChannel;
   mMessageQueue = looper.getQueue();
   //调用nativeInit方法,初始化Native层的NativeInputEventReceiver类
   mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
           inputChannel, mMessageQueue);

   mCloseGuard.open("dispose");
}

//android_view_InputEventReceiver.cpp

//初始化InputEventReceiver
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }   

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    //新建NativeInputEventReceiver实例,并调用initialize函数
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

//初始话函数
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

//将SocketPair生成一个文件描述符添加到Looper的监听中
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}
//Looper.cpp

//Looper内部是使用epoll系统调用实现文件描述符监听的


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;
   
   struct epoll_event eventItem;
   request.initEventItem(&eventItem);
   
   epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
   
   return 1;
}

事件处理

前面说到在ViewRootImpl中的setView(View, LayoutParams, View)方法中创建了消息接收器WindowInputEventReceiver。那么当消息通过Socket管道传到应用进程中后,会回调java层的dispatchInputEvent(int seq, InputEvent event)方法。该方法位于WindowInputEventReceiver的父类InputEventReceiver中。

// 由Native层回调到Java层的该方法
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
   mSeqMap.put(event.getSequenceNumber(), seq);
   onInputEvent(event);
}

mSeqMap是一个SparseIntArray类的数组,用于存储事件分发器序号与事件序号的映射,当事件处理完成后,回调给Service进程时,要把事件分发器序号req传递回去。
然后调用了onInputEvent(Event)方法,该方法默认实现是通知Service进程该事件处理完成,子类(WindowInputEventReceiver)可以重写该方法实现自己的处理逻辑。

// android/view/ViewRootImpl.java

final class WindowInputEventReceiver extends InputEventReceiver {
   @Override
   public void onInputEvent(InputEvent event) {
       enqueueInputEvent(event, this, 0, true);
   }
}

从方法名上可以看出,直接把输入事件添加到了一个队列中。实际上也是这样的,不过是将InputEvent封装成了QueuedInputEvent对象,enqueueInputEvent方法的最后一个参数代表是否立即处理输入事件,这里为true,表示要立即处理。那什么时候不用立即处理呢?下面具体罗列这两种处理方式的场景:

  • 立即处理: 屏幕触摸事件、键盘输入事件、WebView未处理的或被原生App拦截的按键事件
  • 稍后处理: 游戏杆、轨迹球、

KeyEvent和MotionEvent的区别?
KeyEvent代表按键事件,包含了软键盘、导航栏、硬件按钮点击事件
MotionEvent代表运动事件,包含屏幕点击、滑动、抬起、取消等事件。

开始分发事件

// android/view/ViewRootImpl.java

    private void deliverInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                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 (q.mEvent instanceof KeyEvent) {
            mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
        }

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

责任链模式

NativePreImeInputStage -> ViewPreImeInputStage -> ImeInputStage -> EarlyPostImeInputStage -> NativePostImeInputStage -> ViewPostImeInputStatge ->
SyntheticInputStage

上述的输入事件的处理策略,每个策略只负责自己能处理的事件onProcess,对于处理不了事件,forward交给下一个策略去处理。对于自己能处理的事件,处理完成后会将事件标记为已处理,然后逐一透传到后面的策略中,最后一个策略会执行事件处理完成的方法finishInputEvent(QueuedInputEvent)。

下面逐一说明这些策略的用途:

NativePreImeInputStage
键盘输入事件预处理策略。将按键事件交给Native Activity去处理,Native Activity是一种由C/C++来开发的方式,适用于游戏开发。

ViewPreImeInputStage
视图的输入法事件预处理策略。该策略会回调View类的onKeyPreIme方法,让View有机会在输入法之前处理按键事件。一个典型场景是,应用可以处理物理返回键的点击事件,来更新UI,从而避免让输入法接收到该事件后隐藏自己。

ImeInputStage
将按键事件交给输入法去处理。

EarlyPostImeInputStage
在输入法处理事件前执行的一些操作,例如屏幕有焦点的视图会高亮显示,显示输入光标等。

NativePostImeInputStage
输入法处理事件完成后,将事件传递给Native Activity。

ViewPostImeInputStatge
输入法处理按键事件后,将事件传递给View。

final class ViewPostImeInputStage extends InputStage {
   
   //这里区分不同的事件类型:
   //KeyEvent 按键事件
   //MotionEvent 运动事件
   //MotionEvent又分为屏幕触摸、轨迹球触摸、通用运动事件(其中悬停事件单独处理)
   @Override
   protected int onProcess(QueuedInputEvent q) {
       if (q.mEvent instanceof KeyEvent) {
           return processKeyEvent(q);
       } else {
           final int source = q.mEvent.getSource();
           if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
               return processPointerEvent(q);
           } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
               return processTrackballEvent(q);
           } else {
               return processGenericMotionEvent(q);
           }
       }
   }
}

SyntheticInputStage
将前面未处理的事件,重新整合成新事件处理。例如轨迹球、摇杆、虚拟导航按键。

View触摸事件分发

ViewRootImpl中保存了视图树的根视图DecorView,所以会将输入事件交给DecorView去分发。分发顺序如下:

  1. Window.Callback
    由当前窗口处理。
    当Activity或Dialog初始化时,会为当前窗口PhoneWindow添加事件处理回调。在DecorView分发事件会优先分发的Window.Callback中。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   final Window.Callback cb = mWindow.getCallback();
   return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
           ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
  1. Dialog/Activity dispatchXXXEvent(InputEvent)
    Dialog或Activity会实现Window.Callback,所以输入事件会传递到Dialog/Activity的事件处理方法中。
public boolean dispatchTouchEvent(MotionEvent ev) {
   if (ev.getAction() == MotionEvent.ACTION_DOWN) {
       onUserInteraction();
   }
   if (getWindow().superDispatchTouchEvent(ev)) {
       return true;
   }
   return onTouchEvent(ev);
}

为当前Activity单独响应ACTION_DOWN按下事件,回调方法为Activity的onUserInteraction()。

  1. 分发给ViewGroup的dispatchTouchEvent(InputEvent)方法
    输入事件传递给Activity后,又会通过所关联的窗口Widow,将事件分发给DecorView的父类ViewGroup。到这里也就进入了我们常说的事件分发流程中。
//PhoneWidnow.java

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
   return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java

public boolean superDispatchTouchEvent(MotionEvent event) {
   return super.dispatchTouchEvent(event);
}
//ViewGroup.java

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
}
  1. Activity的onTouchEvent(InputEvent)
    如果在上一步中,输入事件没有被任何一个视图处理,那么将会分发到Activity的onTouchEvent(InputEvent)方法中。
public boolean onTouchEvent(MotionEvent event) {
   if (mWindow.shouldCloseOnTouch(this, event)) {
       finish();
       return true;
   }

   return false;
}

一般情况下,输入事件是不会传递到Activity的。使用场景是,当Activity持有的Window比较小,输入事件的位置落在了Window外面,这时就会把输入事件传递Activity了。
那么什么时候Window会比较小呢?
显然是在使用Dialog的时候。
我们知道Dialog也是一个单独的窗口,并且在创建Dialog时可以自定义它的Window尺寸。我们常用的CloseOnTouchOutside功能就是在Activity的onTouchEvent方法中实现的。

触摸事件在视图树中的分发逻辑

输入事件的一致性验证

if (mInputEventConsistencyVerifier != null) {
    mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}

根据安全策略过滤触摸事件

public boolean onFilterTouchEventForSecurity(MotionEvent event) {
   //noinspection RedundantIfStatement
   if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
           && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
       // Window is obscured, drop this touch.
       return false;
   }
   return true;
}

如果当前窗口正处于模糊阶段,如执行转场动画时,不响应触摸事件,将当前事件丢弃。

为ACTION_DOWN事件做初始化操作

// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
    // Throw away all previous state when starting a new touch gesture.
    // The framework may have dropped the up or cancel event for the previous gesture
    // due to an app switch, ANR, or some other state change.
    cancelAndClearTouchTargets(ev);
    resetTouchState();
}

当检测到ACTION_DOWN事件时,说明需要重新计算手势,此时就要把之前存储的状态清除掉。也就是向TouchTarget链表节点逐一发送Cancel事件,然后清空TouchTarget链表。

判断是否需要拦截当前事件

// Check for interception.
  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 {
      // There are no touch targets and this action is not an initial down
      // so this view group continues to intercept touches.
      intercepted = true;
  }

ViewGroup可以通过onInterceptTouchEvent方法拦截当前触摸事件,不分发给子视图。
我们也可以在创建布局时为ViewGroup指定FLAG_DISALLOW_INTERCEPT标识,让当前ViewGroup不拦截任何触摸事件。
设置方式如下:

ViewGroup.requestDisallowInterceptTouchEvent(true);

如果没有设置FLAG_DISALLOW_INTERCEPT,则执行默认的onInterceptTouchEvent(TouchEvent)方法。该方法会拦截对于滚动条的操作。

public boolean onInterceptTouchEvent(MotionEvent ev) {
   if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
           && ev.getAction() == MotionEvent.ACTION_DOWN
           && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
           && isOnScrollbarThumb(ev.getX(), ev.getY())) {
       return true;
   }
   return false;
}

如果onInterceptTouchEvent(Event)方法返回true,触摸事件就不会分发给子视图,而是调用ViewGroup父类View的onTouchEvent(Event)方法来消耗当前触摸事件。

ViewGroup不拦截事件,分发给子视图

ViewGroup在寻找响应触摸事件的子视图时,需要每次都遍历所有子视图么?
答案当然不是,只有在ACTION_DOWN或ACTION_POINT_DOWN的时候才会遍历一次,找到对应的子视图后,会封装成TouchTarget节点,保存在mFirstTouchTarget链表中。后续的ACTION_MOIVE、ACTION_UP事件的分发,只需要遍历mFirstTouchTarget链接即可。

if (!canceled && !intercepted) {
    //在不是取消事件,并且不拦截事件,并且是ACTION_DOWN的情况下,遍历所有子视图
    if (actionMasked == MotionEvent.ACTION_DOWN
        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
        final int childrenCount = mChildrenCount;
        if (childrenCount != 0) {
            //获取触摸事件的坐标
            final float x = ev.getX(actionIndex);
            final float y = ev.getY(actionIndex);
            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);
                
                //判断子视图是否可以接收触摸事件,并且触摸点落在子视图的范围内
                if (!canViewReceivePointerEvents(child)
                    || !isTransformedTouchPointInView(x, y, child, null)) {
                     ev.setTargetAccessibilityFocus(false);
                     continue;
                }
                
                //如果满足上述条件,就从TouchTarget链表中找到对应的TouchTarget
                //一般情况下第一个ACTION_DOWN事件的发生,TouchTarget链表为空,这里肯定找不到
                //但是在多点触控情况,第二根手指也按在了相同的子视图上,那么会发生ACTION_POINT_DOWN事件,此时就可以找到对应的TouchTarget了
                newTouchTarget = getTouchTarget(child);
                
                //如果newTouchTarget不为空,说明第二根手指触摸到了屏幕,
                //这时就需要把新的触摸点id添加到newTouchTarget中,然后结束遍历
                if (newTouchTarget != null) {
                      // Child is already receiving touch within its bounds.
                      // Give it the new pointer in addition to the ones it is handling.
                      newTouchTarget.pointerIdBits |= idBitsToAssign;
                      break;
                }
                
                //如果newTouchTarget为空,那么就是第一次的ACTION_DOWN事件
                //这时就要把ACTION_DOWN事件分发给子视图,
                //如果子视图消费了该事件,就在TouchTarget链接中添加节点
                if (dispatchTransformedTouchEvent(ev, false, child, 
                                                    idBitsToAssign)) {
                      // Child wants to receive touch within its bounds.
                      mLastTouchDownTime = ev.getDownTime();
                      if (preorderedList != null) {
                          // childIndex points into presorted list, find original index
                          for (int j = 0; j < childrenCount; j++) {
                              if (children[childIndex] == mChildren[j]) {
                                  mLastTouchDownIndex = j;
                                  break;
                              }
                          }
                      } else {
                          mLastTouchDownIndex = childIndex;
                      }
                      mLastTouchDownX = ev.getX();
                      mLastTouchDownY = ev.getY();
                      newTouchTarget = addTouchTarget(child, idBitsToAssign);
                      alreadyDispatchedToNewTouchTarget = true;
                      break;
                  }
            }
            
            //如果当前ViewGroup不包含子视图,或者没有任何一个子视图消费这个触摸事件
            //那么就尝试将事件分发给TouchTarget链接中的最后一个节点,也就是最早加入链表的子视图
            //在多点触控情况下,有可能这个事件是它想要的。
            if (newTouchTarget == null && mFirstTouchTarget != null) {
                // Did not find a child to receive the event.
                // Assign the pointer to the least recently added target.
                newTouchTarget = mFirstTouchTarget;
                while (newTouchTarget.next != null) {
                    newTouchTarget = newTouchTarget.next;
                }
                newTouchTarget.pointerIdBits |= idBitsToAssign;
            }            
        }
    }
}

ViewGroup拦截事件自己处理

当ViewGroup想要拦截事件后,就会执行下面逻辑。

// 1. 在事件被拦截后,TouchTarget链表为空,所以直接将事件交给了ViewGroup的onTouchEvent方法去处理
  if (mFirstTouchTarget == null) {
      // No touch targets so treat this as an ordinary view.
      handled = dispatchTransformedTouchEvent(ev, canceled, null,
              TouchTarget.ALL_POINTER_IDS);
  } else {
      //2. 如果事件没有被拦截,并且不是ACTION_DOWN事件条件下,
      //就会遍历TouchTarget链表,将事件分发给TouchTarget对应的子视图 
      TouchTarget predecessor = null;
      TouchTarget target = mFirstTouchTarget;
      while (target != null) {
          final TouchTarget next = target.next;
          //在发生ACTION_DOWN事件,寻找合适子视图时,已经分发过ACTION_DOWN事件了,
          //所以此处不必在分发一下
          if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
              handled = true;
          } else {
              final boolean cancelChild = resetCancelNextUpFlag(target.child)
                      || intercepted;
              if (dispatchTransformedTouchEvent(ev, cancelChild,
                      target.child, target.pointerIdBits)) {
                  handled = true;
              }
              //如果取消了某个子视图,则将该子视图从TouchTarget链表中移除。
              //那么什么时候cancelChild为true呢?
              //1、intercepted为true,也就是ViewGroup拦截了除ACTION_DOWN事件外的其他事件
              //2、resetCancelNextUpFlag(target.child)为true,也就是这个子视图从视图树中被移除了,不需要再响应触摸事件了
              if (cancelChild) {
                  if (predecessor == null) {
                      mFirstTouchTarget = next;
                  } else {
                      predecessor.next = next;
                  }
                  target.recycle();
                  target = next;
                  continue;
              }
          }
          predecessor = target;
          target = next;
      }
  }

将事件分发给子视图或自己

ViewGroup将触摸事件分发给自己还是子视图,都是通过dispatchTransformedTouchEvent实现的。
对于将要分发的事件,要针对多点触控的场景做一个事拆分处理。
怎么判断是否是多点触控呢?

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
   final boolean handled;//当前事件是否被消费的标识
    
   ...
    
   //如果child为null,说明ViewGroup需要将事件分发自己,也就是调用父类View的
   // dispatchTouchEvent方法
   //如果child不为null,就要把事件分发给子视图了
   //无论分发给父类,还是子视图,都是执行View类dispatchTouchEvent方法的默认实现
   if (child == null) {
       handled = super.dispatchTouchEvent(transformedEvent);
   } else {
       final float offsetX = mScrollX - child.mLeft;
       final float offsetY = mScrollY - child.mTop;
       transformedEvent.offsetLocation(offsetX, offsetY);
       if (! child.hasIdentityMatrix()) {
           transformedEvent.transform(child.getInverseMatrix());
       }

       handled = child.dispatchTouchEvent(transformedEvent);
   }
}

根据上面的方法名称可以看出,ViewGroup并不是将原始的MotionEvent分发出来,而是做了一些转换操作。
那么为什么要做转换?

  1. 在ViewGroup内有两个子View A和B,一个手指点击在View A上,此时第二个手指点击View B,当前ViewGroup会收到ACTION_POINTER_DOWN类型的事件,但是对于View B来说这是一个全新的事件序列,所以需要把ACTION_POINTER_DOWN转换为ACTION_DOWN,再分发给View B。
  2. 和1相同的场景,View B收到的是ACTION_DOWN,而View A也会收到一个ACTION_MOVE类型的事件。也就是说尽管两个手指没有点击在同一个View上,手指在View B上的操作均会给View A分发ACTION_MOVE事件
  3. 第三种转换是对MotionEvent的坐标转换,如果ViewGroup中包含的内容发生了滚动,要修正MotionEvent的坐标后再分发给子视图。

View类的dispatchTouchEvent(Motion)方法实现
前面说到在ViewGroup的事件分发流程中,最终都会调用到View的dispatchTouchEvent(TouchEvent)方法,无论是分发给子视图,还是自己处理。下面我们分析一下该方法的默认实现。

//View.java

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    
      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;
}
  1. 先判断是否是拖动滚动条,如果是,则代表由当前View消费该事件。
  2. 判断是够为View设置了OnTouchListener回调接口,如果设置了,并且该接口的方法onTouch返回了true,则代表由该回调消费了该事件。
  3. 如果前面两个步骤中消费了触摸事假,则不再将事件分发到View的onTouchEvent(TouchEvent)方法中,否则调用onTouchEvent(TouchEvent)方法

View类的onTouchEvent(TouchEvent)默认实现
View类的onTouchEvent(MotionEvent)默认实现了以下几个功能:

  1. 按照系统声音设置,播放点击音效
  2. 响应为View设置的OnClickListener事件
  3. 响应辅助功能的点击事件
  4. 修改当前视图的按下状态
  5. 在条件允许的情况下,获取焦点
public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();
    
    //对于被禁用的可点击视图,仍会消费触摸事件,只是不会产生点击事件的响应
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
       if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
           setPressed(false);
       }
       return (((viewFlags & CLICKABLE) == CLICKABLE
               || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
               || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
   }
   
   //这里有个触摸事件的代理,也就是通过TouchDelegate将当前视图的触摸事件,转移到另外一个视图上
   //SearchView中有使用TouchDelegate的示例。
   if (mTouchDelegate != null) {
       if (mTouchDelegate.onTouchEvent(event)) {
           return true;
       }
   }
   
   //在当前视图处于可点击的状态,才消费触摸事件,否则返回false
   if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
            case MotionEvent.ACTION_UP:
            ...
            break;    
            case MotionEvent.ACTION_DOWN:
            ...
            break;
            
            case MotionEvent.ACTION_CANCEL:
            ...
            break;
            
            case MotionEvent.ACTION_MOVE:
            ...
            break;
        }
        
        return true;
    }
    
    return false;
}

总结

整体流程
点击屏幕
InputManagerService的Read线程捕获事件,预处理后发送给Dispatcher线程
Dispatcher找到目标窗口
通过Socket将事件发送到目标窗口
APP端被唤醒
找到目标窗口处理事件

事件在App端的分发策略
事件传递优先级:
Activity.dispatchTouchEvent(MotionEvent) ->
PhoneWindow.superDispatchTouchEvent(MotionEvent) ->
DecorView.superDispatchTouchEvent(MotionEvent) ->
ViewGroup.dispatchTouchEvent(MotionEvent) ->
ViewGroup.onInterceptTouchEvent(MotionEvent) ->
View.dispatchTouchEvent(MotionEvent) ->
View.OnTouchListener.onTouch(MotionEvent) ->
View.onTouchEvent(MotionEvent) ->
View.OnClickListenaer.onClick(View)->
Activity.onTouchEvent(MotionEvent)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值