牛骨文教育服务平台(让学习变的简单)
博文笔记

【转载两篇关于android按键事件传递的文章,流程非常完整】Android按键事件传递流程(一)

创建时间:2017-08-23 投稿人: 浏览次数:462
  2017-07-01 16:45 230人阅读 评论(0) 收藏 举报  分类:

目录(?)[+]

做Android开发的少不了对触摸、按键事件进行处理,对于手机来说,主要是手势移动、触摸按下等,而TV主要通过遥控器、按键操作,按键事件不同于触摸事件,必须先获得焦点,然后才能移动、选择。

android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层,内核层传递过程不在本文研究范围内,本文主要对按键事件在Framework层、应用层的传递过程进行分析(本文基于Android5.1.1版本),带着问题出发:

1.    Framework层如何获得输入事件

2.    Framework层获得按键事件后如何处理、存储

3.    Framework层如何分发、传递给应用层

4.    Framework层服务端管道和应用程序客户端管道如何创建、注册

5.    应用层如何从Framework层接收按键事件

6.    应用层接收到按键事件后如何传递

7.    特殊按键如何处理

8.    总结

1.    Framework层如何获得输入事件

要分析输入系统,最好先从输入事件系统服务InputManagerService入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。

1.1    InputManagerService的初始化

Android系统启动时,android部分第一个用户空间进程zygote(注:如果从Linux角度出发,第一个用户空间进程是init)首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerService、PowerManagerService、PackageManagerService、InputManagerService等,相关代码在SystemServer.Java的run方法中,与InputManagerService相关部分:

1) inputManager = new InputManagerService(context);
 
2) wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
 
3) ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 
4) inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
 
5) inputManager.start();

1.1.1    InputManagerService的构造方法

this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象

mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

用Looper中的消息队列作为参数,调用本地方法nativeInit,返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。

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

把InputManagerInternal.class和LocalService对象作为一对映射添加到ArrayMap<Class<?>, Object>中

1.1.2    nativeInit方法实现

static jlongnativeInit(JNIEnv* env, jclassclazz,
        jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) {
    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);
}

android_os_MessageQueue_getMessageQueue方法:

sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
    jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
    return reinterpret_cast<NativeMessageQueue*>(ptr);
}

获得了C++层NativeMessageQueue对象,NativeMessageQueue对象是在创建Looper对象时创建的,在本博客Handler机制中已经分析过

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

创建了NativeInputManager对象,返回到Java层的nativeInit,并将地址赋给mPtr;getLooper()获得c++层的Looper对象,在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中

NativeInputManager的构造函数:

NativeInputManager::NativeInputManager(jobjectcontextObj,
        jobjectserviceObj, 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;
    }
 
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法;创建c++层中全局变量mContextObj, mServiceObj;创建EventHub对象并作为参数创建InputManager对象。

1.1.3    EventHub.cpp的构造函数

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);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
 
    mINotifyFd = inotify_init();
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);
 
    struct epoll_eventeventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
 
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
 
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);
 
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);
 
    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
 
    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kernel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

初始化必要的变量,采用epoll机制系统调用接口epoll_create创建epoll对象,返回文件描述符mEpollFd,代表epoll对象;采用inotify机制监听文件或目录的移动、读取、写入或删除等事件,inotify机制主要包含2个系统调用接口:inotify_init, inotify_add_watch

inotify_init:创建一个inotify对象,如果成功,返回一个文件描述符,作为该对象;返回-1表示出错

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)

inotify_add_watch:把监控项添加到mINotifyFd对象的监控列表中,第二个参数DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;第三个参数是一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。IN_DELETE:当被监控目录内删除文件或目录时触发该事件;IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。

如果inotify_add_watch执行成功,返回一个非负监控描述符(假如为wd),代表被监控的项,如果将来不需要再监听了,可以使用inotify_rm_watch(fd, wd)删除所添加的监控项wd。

struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

把inotify对象mINotifyFd添加到epoll对象的兴趣列表中,此处采用inotify与epoll机制结合起来检查文件

eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

创建了管道对象读取端文件描述符mWakeReadPipeFd并添加到epoll对象的兴趣列表中

读到此处,提出2个问题:

Q1    epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait,猜测在代码某处肯定有epoll_wait,否则epoll机制无法工作

Q2    如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用

记住这两个问题,有助于在分析多进程控制时的代码走向

1.1.4    InputManager.cpp的构造器

mInputManager = new InputManager(eventHub, this, this);
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();
}

第一个参数是刚创建的EventHub对象,第二、三个参数都是this,当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager

用这两个传递过去的对象类型作为参数分别创建了InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象

1.1.5    InputDispatcher的构造函数

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    mLooper = new Looper(false);
 
    mKeyRepeatState.lastKeyEntry = NULL;
 
    policy->getDispatcherConfiguration(&mConfig);
}

把传递过来的NativeInputManager对象赋给mPolicy,该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型,然后又初始化了很多变量,其中包括:

mLooper = new Looper(false);

创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。

注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。

Q3    既然有了epoll_ctl,那肯定某处会调用epoll_wait

1.1.6    InputReader的构造函数

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

第一个参数是EventHub对象,第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型

mQueuedListener = new QueuedInputListener(listener);

把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量

1.1.7    InputManager.cpp的initialize函数

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

用InputReader对象作为参数创建InputReaderThread线程对象,用InputDispatcher作为参数创建InputDispatcherThread线程对象,InputReaderThread的构造函数:

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
    Thread(/*canCallJava*/ true), mReader(reader) {
}

把InputReader对象赋给mReader变量

InputDispatcherThread的构造函数:

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
    Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

把InputDispatcher对象赋给mDispatcher变量

进程执行到此处,InputManagerService的初始化就完成,整个过程都是在创建、初始化各种对象。

Q4    既然创建了线程对象,那么就应该在某处启动线程,否则线程无法运行,在哪里启动线程

1.2    WindowManagerService的main方法

wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
public static WindowManagerServicemain(final Contextcontext,
 final InputManagerServiceim,
 final boolean haveInputMethods, final boolean showBootMsgs,
 final boolean onlyCore) {
    final WindowManagerService[] holder = new WindowManagerService[1];
    DisplayThread.getHandler().runWithScissors(new Runnable() {
        @Override
 public void run() {
    holder[0] = new WindowManagerService(context, im,
 haveInputMethods, showBootMsgs, onlyCore);
 }
    }, 0);
 return holder[0];
}
mInputManager = inputManager;

main方法创建了WindowManagerService对象,第二个参数是InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager

1.3    ServiceManager的addService方法

ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

把InputManagerService服务注册到ServiceManager中

1.4    InputManagerService的setWindowManagerCallbacks方法

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

把InputMonitor对象传递到InputManagerService中去,方便回调,比如7.2节通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法

1.5     InputManagerService的start方法

start方法中有两句:

nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);

nativeStart的本地实现:

static void nativeStart(JNIEnv* env, jclassclazz, jlongptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
    status_tresult = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

ptr在1.1.1节已经提到过,是NativeInputManager对象地址,只不过是长整形,此处再转化成NativeInputManager型指针

Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调

Q5    创建了看门狗,有什么用?

nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart  —->  InputManager.cpp的start()

1.5.1    InputManager.cpp的start

status_tInputManager::start() {
    status_tresult = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
 
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);
 
        mDispatcherThread->requestExit();
        return result;
    }
 
    return OK;
}

首先启动分发器线程InputDispatcherThread,调用run方法后,开始执行threadLoop方法,如果threadLoop返回true,就再次执行threadLoop方法直到requestExit方法停止线程;再调用InputReaderThread的run方法启动接收器线程。这回答了1.1.7节Q4问题,只要创建了线程,那就应该有启动线程的地方。

threadLoop中调用了mDispatcher->dispatchOnce()

1.5.2    InputDispatcher.cpp的dispatchOnce

void InputDispatcher::dispatchOnce() {
    nsecs_tnextWakeupTime = 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 up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock
 
    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_tcurrentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}
if (!haveCommandsLocked()) {
    dispatchOnceInnerLocked(&nextWakeupTime);
}

一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked

1.5.2.1    InputDispatcher.cpp的dispatchOnceInnerLocked

if (! mPendingEvent) {
    if (mInboundQueue.isEmpty()) {
 if (isAppSwitchDue) {
 // The inbound queue is empty so the app switch key we were waiting
 // for will never arrive.  Stop waiting for it.
 resetPendingAppSwitchLocked(false);
 isAppSwitchDue = false;
 }
 
 // Synthesize a key repeat if appropriate.
 if (mKeyRepeatState.lastKeyEntry) {
 if (currentTime >= mKeyRepeatState.nextRepeatTime) {
 mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
 } else {
 if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
 *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
 }
 }
 }
 
 // Nothing to do if there is no pending event.
 if (!mPendingEvent) {
 return;
 }
    } else {
 // Inbound queue has at least one entry.
 mPendingEvent = mInboundQueue.dequeueAtHead();
 traceInboundQueueLengthLocked();
    }
 
    // Poke user activity for this event.
    if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
 pokeUserActivityLocked(mPendingEvent);
    }
 
    // Get ready to dispatch the event.
    resetANRTimeoutsLocked();
}

mPendingEvent初始化为空,mInboundQueue队列也为空,下面语句:

if (! mPendingEvent) {
    if (mInboundQueue.isEmpty()) {

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。