android应用程序键盘事件机制

启动初始化

Android的键盘事件由InputManager监控。

先来看下InputManager是如何启动的。

Android在启动的时候,Zygote会启动SystemServer.java

目录:frameworks/base/services/java/com/android/server/SystemServer.java

class ServerThread {
   ……..
 
public voidinitAndLoop() {
                   …….
         // Create a handler thread just for thewindow manager to enjoy.
    HandlerThread wmHandlerThread = newHandlerThread("WindowManager");
    wmHandlerThread.start();
    Handler wmHandler = newHandler(wmHandlerThread.getLooper());
    …….
inputManager = new InputManagerService(context, wmHandler);
 
       Slog.i(TAG, "Window Manager");
       wm = WindowManagerService.main(context, power, display, inputManager,
                wmHandler, factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL,
                !firstBoot, onlyCore);
       ServiceManager.addService(Context.WINDOW_SERVICE, wm);
       ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 
       ActivityManagerService.self().setWindowManager(wm);
 
       inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
       inputManager.start();
                   ……….
         }
}

SystemServer在初始化的时候,会调用WindowManagerServer的main函数。同时,初始化InputManagerService并启动InputManagerService。在这里将wmHandler的句柄传给了InputManagerServer。

 

那么,在WindowManagerServer.main又做了什么?

frameworks/base/services/java/com/android/server/wm./WindowManagerServer.java
public static WindowManagerService main(final Context context,
           final PowerManagerService pm, final DisplayManagerService dm,
           final InputManagerService im, final Handler wmHandler,
           final boolean haveInputMethods, final boolean showBootMsgs,
           final boolean onlyCore) {
       final WindowManagerService[] holder = new WindowManagerService[1];
        wmHandler.runWithScissors(new Runnable() {
           @Override
           public void run() {
                holder[0] = newWindowManagerService(context, pm, dm, im,
                        haveInputMethods,showBootMsgs, onlyCore);
           }
       }, 0);
       return holder[0];
}

这里main,实例化了一个WindowManagerServer。这里传入的inputmanager是怎么用的呢?InputManager在4.4.4中,在WindowmanagerServer中InputManager提供状态以及UI的更新调用。有兴趣的读者,可以瞄一眼

 

InputManagerService.java

看一下InputManagerService.java的构造体

frameworks/base/core/java/android/hardware/input/InputManagerService.java

public classInputManagerServer {
    ……..
    public InputManagerService(Context context,Handler handler) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(handler.getLooper());
 
        ……..
        mPtr = nativeInit(this, mContext,mHandler.getLooper().getQueue());
    }
    ……..
}

InputManagerServer是由SystemServer直接启动的。

Handler是什么呢?

之前说这个从SystemServer传进来的。换言之,这里的Looper跟SystemServer传入的Looper是一样的,也就是WindowManager跟InputManager用了一个MessageQueue。那么,它在某种意义上算是消息同步的。

nativeInit是native层的方法,将结果又传递给java层的mPtr保存起来,在后续一定会有相关的转换调用。

 

nativeInit在com_android_server_input_InputManagerService.cpp中

frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

static jint 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 notinitialized.");
        return 0;
    }
 
    NativeInputManager* im = newNativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jint>(im);
}


这里又将java层传入的MessageQueue转换,再次初始化NativeInputManager。

NativeInputManager::NativeInputManager(jobjectcontextObj,
        jobject serviceObj, constsp<Looper>& looper) :
        mLooper(looper) {
    …….
    sp<EventHub> eventHub = newEventHub();
    mInputManager = new InputManager(eventHub,this, this);
}


将looper保存到native层的mLooper。实例话了EventHub,将其作为参数传递给InputManager实例化。

 

这里有必要看一下EventHub的结构体

 

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

 

    mEpollFd =epoll_create(EPOLL_SIZE_HINT);

   

    mINotifyFd =inotify_init();

    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;

    result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

   

    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;

    result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

}

看标红的代码段:EventHub使用的epoll机制,inotify机制,pipe机制。

这段结构体有如下解释

1.       对一些成员变量做了初始化

2.       创建了epoll管道,EPOLL_SIZE_HINT代表的是最大监控数量

3.       调用inotify机制对/dev/input文件目录进行监听,任何增删改动作

4.       用epoll机制EPOLL_CTL_ADD命令把inotify的句柄和以及后面一句中的mWakeReadPipeFd句柄,加入到epoll的句柄mEpollFd中。这样,只要/dev/input下面有任何动作,就会在mWakeWritePipeFd中写数据,唤醒线程,epoll_wait就会被返回。

 

 

InputManager.cpp

frameworks/base/services/input/InputManager.cpp

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

这里实例化了InputDispatcher和InputReader。

InputDispatcher:从这里看不出具体的,猜测是按键消息的分发给当前激活的activity。比如dispatchTouchEvent之类就是分发

InputReader:同样看不出来,但是,结合eventHub,readerPolicy,可能是读取键盘事件。

事实证明猜测是正确的。后续分析。。。。

voidInputManager::initialize() {
    mReaderThread = newInputReaderThread(mReader);
    mDispatcherThread = newInputDispatcherThread(mDispatcher);
}

这里,又将实例化的InputDispatcher和InputReader,再次传给InputDispatcherThread和InputReaderThread作为参数。这里的两个线程实例一个是用来读取键盘事件,而另一个是用来分发键盘事件的消息。

 

至此,键盘事件的初始化就完成了。

文件结构如下:

小结:

1.             SystemServer在initandloop中实例化了InputManagerServer,这是用来接收分派按键事件的service

2.             SystemServer将InputManagerServer传递给WindowManagerService控制输入法的显示,以及获取相关状态

3.             C++层也相应地创建一个InputManager的实例会监控处理键盘事件

4.             InputManager用InputReader和InputDispatcher分别处理键盘事件的读取以及消息分发

5.             InputReader和InputDispatcher各自有一个线程来做相关的工作

 

 

 

事件分发和获取

对整体的结构有个大概的样子之后,开始了解键盘事件的处理方法

重新回到SystemServer

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

Class SystemServer{
         Publicvoid initAndLoop(){
                   ……..
                   inputManager= new InputManagerService(context, wmHandler);
                   ………
       inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
       inputManager.start();
……
}
}

 

InputManagerServer被实例化后,又被调用了start方法。

 

来看下start方法

切换到InputManagerServer.java

frameworks/base/core/java/android/hardware/input/InputManagerService.java

 

   

public void start() {
       Slog.i(TAG, "Starting input manager");
       nativeStart(mPtr);
 
       // Add ourself to the Watchdog monitors.
       Watchdog.getInstance().addMonitor(this);
 
       registerPointerSpeedSettingObserver();
       registerShowTouchesSettingObserver();
 
       mContext.registerReceiver(new BroadcastReceiver() {
           @Override
           public void onReceive(Context context, Intent intent) {
               updatePointerSpeedFromSettings();
                updateShowTouchesFromSettings();
           }
       }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
 
       updatePointerSpeedFromSettings();
       updateShowTouchesFromSettings();
}

 

这里,将直接init出来的mPtr作为参数传递给native层的nativestart方法。

registerPointerSpeedSettingObserver:这是监听Settings.System.POINTER_SPEED,一旦改变就修改对应的触摸监听频率

registerShowTouchesSettingObserver:这是监听Settings.System.SHOW_TOUCHES,这个具体是什么用的。不详。有兴趣的读者可以了解下。

这里同时监听了Intent.ACTION_USER_SWITCHED,若用户切换,那么重新调用updatePointerSpeedFromSettings();        updateShowTouchesFromSettings();

updatePointerSpeedFromSettings();:更新系统属性值Settings.System.POINTER_SPEED,并设置

   updateShowTouchesFromSettings();:更新系统属性值Settings.System.SHOW_TOUCHES,并设置

重点看一下nativeStart,其他的都是浮云啊。

frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

…
inlinesp<InputManager> getInputManager() const { return mInputManager; }
…….
static voidnativeStart(JNIEnv* env, jclass clazz, jint ptr) {
    NativeInputManager* im =reinterpret_cast<NativeInputManager*>(ptr);
 
    status_t result =im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env,"Input manager could not be started.");
    }
}
……….


这里将java的mPtr转换成NativeInputManager,继而获得在native层保存的mInputManager,调用其start方法。

 

继续看InputManager.cpp

status_tInputManager::start() {
    status_t result =mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);
    ……….
 
    result =mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ……..
}

 

这里的start方法,将mDispatcherThread和mReaderThread运行起来,同时给了PRIORITY_URGENT_DISPLAY这个级别跟SurfaceFlinger的priority是一样.可见是及时响应的

 

        

接下来看下,InputDispatcherThread

frameworks/base/services/input/InputDispatcher.cpp

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

为什么这个Thread这么奇葩,因为android对thread有封装,有兴趣的话,可以了解下。

这里mDispatcher就是在实例化InputDispatcherThread作为参数传入的InputDispatcher。

void InputDispatcher::dispatchOnce() {
   nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {// acquire lock
       AutoMutex _l(mLock);
       mDispatcherIsAliveCondition.broadcast();
 
       // Run a dispatch loop if there are no pending commands.
       // The dispatch loop might enqueue commands to run afterwards.
       if (!haveCommandsLocked()) {
           dispatchOnceInnerLocked(&nextWakeupTime);
       }
 
       // Run all pending commands if there are any.
       // If any commands were run then force the next poll to wake upimmediately.
       if (runCommandsLockedInterruptible()) {
           nextWakeupTime = LONG_LONG_MIN;
       }
    }// release lock
 
   // Wait for callback or timeout or wake. (make sure we round up, not down)
   nsecs_t currentTime = now();
   int timeoutMillis = toMillisecondTimeoutDelay(currentTime,nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}


其他的挺好理解的。注意两个标红的代码段

dispatchOnceInnerLocked:分发键盘事件-----稍后分析,这部分会从上到下涉及面相当大。先将如何读取信息的部分看一下。

pollOnce:获取键盘事件,如果没有消息的话就是阻塞,底层的实现是基于pipe机制的。

 

小结下,InputDispatch会调用dispatchOnceInnerLocked去分发消息;在消息分完完成后,又使用mLooper继续获取消息。当mLooper.pollOnce有读取到消息的时候就会往管道里头写新的内容,唤醒正在等待键盘事件的线程;当没有消息的时候,就无限的阻塞着,线程进入空闲等待状态。

 

事件读取

先说InputReader。为什么先这个,因为这个短小精悍,InputDispatcher太长了。处理了InputReader会对后续InputDispatcher也有帮助。

 

InputReaderThread

frameworks/base/services/input/ InputReaderThread.cpp

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


这里调用了InputReader的loopOnce方法。

void InputReader::loopOnce() {
   ………….
   size_t count = mEventHub->getEvents(timeoutMillis,mEventBuffer, EVENT_BUFFER_SIZE);
 
    {// acquire lock
       ……….
       if (count) {
           processEventsLocked(mEventBuffer, count);
       }
…………………
         }
}

这里,从mEventHub中获得了event并存在mEventBuffer中,然后,将mEventBuffer和count作为参数传递给processEventsLocked处理。

 

看下getEvent是怎么获取的?

/frameworks/base/services/input/EventHub.cpp

size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
 
    AutoMutex _l(mLock);
 
    struct input_event readBuffer[bufferSize];
 
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now =systemTime(SYSTEM_TIME_MONOTONIC);
 
        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;
…………
            closeAllDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark
            mNeedToScanDevices = true;
            break; // return to the callerbefore we actually rescan
        }//#这里的mNeedToReopenDevices标记为true,那么就关闭所有的设备,并标记mNeedToScanDevices为true,重新扫描设备
 
        // Report any devices that had lastbeen added/removed.
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting deviceclosed: id=%d, name=%s\n",
                 device->id,device->path.string());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id== mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan =true;
            if (--capacity == 0) {
                break;
            }
        }//#若mClosingDevices不为空,那么将所有的mClosingDevices重置,并删除。
 
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark
            mNeedToSendFinishedDeviceScan =true;
        }//#若mNeedToScanDevices判定为true,那么调用scanDevicesLocked
 
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting deviceopened: id=%d, name=%s\n",
                 device->id,device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id== mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan =true;
            if (--capacity == 0) {
                break;
            }
        }
 
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan =false;
            event->when = now;
            event->type =FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
 
        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex <mPendingEventCount) {
            const struct epoll_event&eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 ==EPOLL_ID_INOTIFY) {
                if (eventItem.events &EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Receivedunexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }
 
            if (eventItem.data.u32 ==EPOLL_ID_WAKE) {
                if (eventItem.events &EPOLLIN) {
                    ALOGV("awoken afterwake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead =read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1&& errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Receivedunexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }
 
            ssize_t deviceIndex =mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("Received unexpectedepoll event 0x%08x for unknown device id %d.",
                        eventItem.events,eventItem.data.u32);
                continue;
            }
 
            Device* device =mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN){
                int32_t readSize =read(device->fd, readBuffer,
                        sizeof(struct input_event)* capacity);
                if (readSize == 0 || (readSize< 0 && errno == ENODEV)) {
                    // Device was removedbefore INotify noticed.
                    ALOGW("could not getevent, removed? (fd: %d size: %d bufferSize: %d "
                            "capacity: %d errno:%d)\n",
                            device->fd,readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN&& errno != EINTR) {
                        ALOGW("could notget event (errno=%d)", errno);
                    }
                } else if ((readSize %sizeof(struct input_event)) != 0) {
                    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];
                        ALOGV("%s got:time=%d.%06d, type=%d, code=%d, value=%d",
                                device->path.string(),
                                (int)iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type,iev.code, iev.value);
                        ………..
                        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;
                            }
                        }
                        if(device->timestampOverrideSec || device->timestampOverrideUsec) {
                            iev.time.tv_sec =device->timestampOverrideSec;
                            iev.time.tv_usec =device->timestampOverrideUsec;
                            if (iev.type ==EV_SYN && iev.code == SYN_REPORT) {
                                device->timestampOverrideSec = 0;
                               device->timestampOverrideUsec = 0;
                            }
                            ALOGV("appliedoverride time %d.%06d",
                                   int(iev.time.tv_sec), int(iev.time.tv_usec));
                        }
 
#ifdefHAVE_POSIX_CLOCKS
                        ……….
#else
                        event->when = now;
#endif
                        event->deviceId =deviceId;
                        event->type =iev.type;
                        event->code =iev.code;
                        event->value =iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        // The result buffer isfull.  Reset the pending event index
                        // so we will try toread the device again on the next iteration.
                        mPendingEventIndex -=1;
                        break;
                    }
                }
            } else if (eventItem.events &EPOLLHUP) {
                ALOGI("Removing device %sdue to epoll hang-up event.",
                       device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpectedepoll event 0x%08x for device %s.",
                        eventIte
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值