【android 9】【input】【8.发送按键事件2——InputDispatcher线程】

系列文章目录

本人系列文章-优快云博客


目录

系列文章目录

1.简介

1.1流程介绍

1.2 时序图

2.普通按键消息发送部分源码分析(按键按下事件)

2.1 开机后分发线程阻塞的地方

 2.2 InputDispatcher::dispatchOnceInnerLocked

 2.3 InputDispatcher::dispatchKeyLocked

 2.4 InputDispatcher::dispatchOnce

2.5 runCommandsLockedInterruptible

 2.6 InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible

 2.7 InputDispatcher::dispatchOnce

 2.8 InputDispatcher::dispatchKeyLocked

 2.9 InputDispatcher::findFocusedWindowTargetsLocked

2.10 dispatchEventLocked

 2.11 prepareDispatchCycleLocked

 2.12 enqueueDispatchEntriesLocked

2.13  enqueueDispatchEntryLocked

 2.14 startDispatchCycleLocked

2.15 InputPublisher::publishKeyEvent

 2.16 InputChannel::sendMessage


1.简介

从上一篇幅我们知道了,普通按键事件在inputreader线程中会将按下事件和抬起事件放入mInboundQueue队列中,然后唤醒InputDispatcher线程去进行派发。本篇我们便来介绍一下InputDispatcher线程是如何派发的。

1.1流程介绍

1.当InputDispatcher线程被唤醒时,第一次执行dispatchOnce时,会生成一个去查询按键事件派发策略的命令并放入命令队列中,然后开始第二次循环。

2.在第二次循环的时候,会从命令队列中取出命令,然后通过jni走到IMS,再走到WMS,再走到phonewindowMnager中询问此按键的派发策略。然后开始第三次循环。

3.如果询问的按键派发策略是立即派发,则会寻找焦点窗口。

4.查找到合适的目标窗口后,会从保存窗口信息的容器中取出窗口信息,窗口信息保存着socket的然后通过sokcet对,将按键消息从IMS发送到应用程序端。

此sokcet对是应用在创建的时候,会调用WMS的addView函数,此函数会创建两个socket对,其中一个fd通过binder通信返回给应用端,应用端会将此fd监听查看是否有消息,另外一个会传递给IMS,IMS也会监听其fd是否有消息。

1.2 时序图

 (图片可保存到本地放大观看)

2.普通按键消息发送部分源码分析(按键按下事件)

为了便于读者清晰的了解发送流程,此章节会删除不执行的代码,便于理解。

2.1 开机后分发线程阻塞的地方

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;//下一次唤醒该线程的时间,也可以说是下一次线程循环的执行时间点
    { 
        
        mDispatcherIsAliveCondition.broadcast();//分发线程处于活跃状态,进行广播
		
        if (!haveCommandsLocked()) {//此时无命令。作用:检查inputdispatcher的缓存队列中是否有还未处理的命令,
		//只有无命令时才会执行dispatchOnceInnerLocked方法,进行事件的分发。
		
            dispatchOnceInnerLocked(&nextWakeupTime);// 传入的nextWakeupTime决定了下次派发线程循环的执行时间点,
        }

        if (runCommandsLockedInterruptible()) {//如果此时缓存队列中有命令,则立即执行该命令,
		//并将下一次线程循环的执行时间点设置为LONG_LONG_MIN,将使派发线程立刻开始下次线程。
		//最小值定义为0,最大值是unsigned long long的最大值:1844674407370955161
            nextWakeupTime = LONG_LONG_MIN;
        }
    } 

    nsecs_t currentTime = now();//获取当前时间点
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//下一次唤醒时间点-当前时间点=线程休眠时间
    mLooper->pollOnce(timeoutMillis);//调用pollOnce使线程休眠,等待回调,唤醒或超时。
	//Looper的pollOnce()的实质就是epoll_wait()。 因此派发线程的休眠在三种情况下可能被唤醒:
	1.调用Looper::wake()函数主动唤醒(有输入事件注入派发队列中时),
	2.到达nextWakeupTime的事件点时唤醒
	3.epoll_waitepoll_wait()监听的fd有epoll_event发生时唤醒(这种唤醒方式将在介绍ANR机制时讨论)。
}

从上一篇我们知道,当 inputreader线程向mInboundQueue中每次插入消息后,都会调用wake来唤醒分发线程。我们首先看一下按键按下的事件分发流程。

 2.2 InputDispatcher::dispatchOnceInnerLocked

此函数的主要作用是:

1.取出消息,然后通过按键的派发策略,和时间等生成对应的分发策略,默认为不丢弃。

2.然后再调用dispatchKeyLocked进行事件的派发。

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();

	//如果没有待发送的事件,则从mInboundQueue队列中取出一个事件,进行分发
    if (! mPendingEvent) {

       }
		
		else {
            //从派发队列中将位于队首的一条EventEntry取出并保存在mPendingEvent成员变量中。 
            // mPendingEvent表示处于派发过程中的一个输入事件
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();//更新一下队列长度
        }

        
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) //设备消息类型为发送给应用程序
		{//判断此key是否需要发送到应用程序。
            pokeUserActivityLocked(mPendingEvent);//用来决定是否要唤醒设备或者点亮屏幕,最终调用的是PowerManagerService。
        }
		

        
        resetANRTimeoutsLocked();//重置ANR超时时间
    }


    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;//默认丢弃原因为不丢弃
	

    switch (mPendingEvent->type) {

    case EventEntry::TYPE_KEY: {//按键类型
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
		
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//进一步分发按键事件,结果赋值给 done
          // 无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是false
        break;
    }

    default:
        break;
    }

    if (done) {//无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是false

        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

 2.3 InputDispatcher::dispatchKeyLocked

此函数的作用是:

1.生成一个commond命令,并入队命令队列,询问此按键按下事件的分发策略。注意,一共是有两个策略,一个是派发策略,派发策略主要是询问是否是系统消费,是否要派发给应用程序等,而此时的分发策略是询问是否丢弃派发,是否等会派发等(如组合按键)

2.终止此时按键事件的派发,开启下一轮循环,先询问按键的分发策略,再派发此按键按下事件。

//此次派发会生成一个commond命令,并入队。去询问key的派发策略,因此此key事件咱未派发。commandEntry->keyEntry = entry;中保存了此次要派发的entry的信息。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
    if (! entry->dispatchInProgress) {//dispatchInProgress默认为false,表示事件是否正在派发

        entry->dispatchInProgress = true;//设置事件为正在派发中
    }


    // 给policy一个机会,去截断key.
	//如果事件的interceptKeyResult是不知道,但是事件的policyFlags是要发送到应用程序的,则生成一个CommandEntry,然后返回false,等待命令的执行
	//如果事件的interceptKeyResult是不知道,事件不是发送给应用程序的,则设置falg为INTERCEPT_KEY_RESULT_CONTINUE。
	//如果事件的interceptKeyResult是INTERCEPT_KEY_RESULT_SKIP,表示跳过此事件,则设置其丢弃原因为DROP_REASON_POLICY
	
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {//初始化的时候值是INTERCEPT_KEY_RESULT_UNKNOWN
	//如果此事件尚未进行过派发策略查询,则通过发送一个命令的方式查询派发策路 
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            CommandEntry* commandEntry = postCommandLocked(//生成了一个commandEntry
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);

			
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // 终止此次派发,开始下一轮的循环等待策略查询完成
        }
	}
}
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);//入队命令队列
    return commandEntry;
}

 此时我们回到 2.2 InputDispatcher::dispatchOnceInnerLocked章节,所以我们可以看出来,第一次派发的按键时,要询问该按键的派发策略,所以done的值是flase

//此时回到上面928行的这个函数,注意此时命令队列中存在一条询问key事件派发策略的命令。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//进一步分发按键事件,结果赋值给 done
          // 无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是false
	/**
	//此时是false,
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
	*/
}

 2.4 InputDispatcher::dispatchOnce

然后开启了下一次循环,此时命令队列中存在查询按键按下的分发策略的命令,所以执行runCommandsLockedInterruptible函数

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;//下一次唤醒该线程的时间,也可以说是下一次线程循环的执行时间点
    { 
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();//分发线程处于活跃状态,进行广播
		
		
		/*
        if (!haveCommandsLocked()) {//此时有命令。作用:检查inputdispatcher的缓存队列中是否有还未处理的命令,
		//只有无命令时才会执行dispatchOnceInnerLocked方法,进行事件的分发。
		
            dispatchOnceInnerLocked(&nextWakeupTime);// 传入的nextWakeupTime决定了下次派发线程循环的执行时间点,
        }*/

        if (runCom
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值