Android应用程序键盘(Keyboard)消息处理机制分析(17)

简介:

  Step 11. InputDispatcher.dispatchOnceInnerLocked

        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,  
  2.     nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {  
  3.     ......  
  4.   
  5.     // Ready to start a new event.  
  6.     // If we don't already have a pending event, go grab one.  
  7.     if (! mPendingEvent) {  
  8.         if (mInboundQueue.isEmpty()) {  
  9.             ......  
  10.         } else {  
  11.             // Inbound queue has at least one entry.  
  12.             EventEntry* entry = mInboundQueue.headSentinel.next;  
  13.   
  14.             ......  
  15.   
  16.             mInboundQueue.dequeue(entry);  
  17.             mPendingEvent = entry;  
  18.         }  
  19.   
  20.         ......  
  21.     }  
  22.   
  23.     ......  
  24.   
  25.     switch (mPendingEvent->type) {  
  26.     ......  
  27.   
  28.     case EventEntry::TYPE_KEY: {  
  29.         KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);  
  30.         ......  
  31.         done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,  
  32.             &dropReason, nextWakeupTime);  
  33.         break;  
  34.                                }  
  35.   
  36.     ......  
  37.     }  
  38.   
  39.     ......  
  40. }  

        我们忽略了这个函数的次要逻辑,主要关注键盘事件的主要处理流程。首先,如果前面发生的键盘事件都已经处理完毕,那么这里的mPendingEvent就为NULL,又因为前面我们把刚刚发生的键盘事件加入了mInboundQueue队列,因此,这里mInboundQueue不为NULL,于是,这里就把mInboundQueue队列中的键盘事件取出来,放在mPendingEvent变量中:

  1. mInboundQueue.dequeue(entry);  
  2. mPendingEvent = entry;  

        由于这里发生的是键盘事件,即mPendingEvent->type的值为EventEntry::TYPE_KEY,于是,在接下来的switch语句中就会执行dispatchKeyLocked函数来分发键盘消息。

 

        Step 12. InputDispatcher.dispatchKeyLocked

        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. bool InputDispatcher::dispatchKeyLocked(  
  2.         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,  
  3.         DropReason* dropReason, nsecs_t* nextWakeupTime) {  
  4.     ......  
  5.   
  6.     // Identify targets.  
  7.     if (! mCurrentInputTargetsValid) {  
  8.         int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,  
  9.             entry, nextWakeupTime);  
  10.   
  11.         ......  
  12.     }  
  13.   
  14.     // Dispatch the key.  
  15.     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);  
  16.     return true;  
  17. }  

         InputDispatcher类中的mCurrentInputTargetsValid成员变量表示InputDispatcher是否已经标志出谁是当前激活的Activity窗口,如果没有,就需要通过findFocusedWindowTargetsLocked函数来把它找出来。当把当前激活的Activity窗口找出来以后,接下来就调用dispatchEventToCurrentInputTargetsLocked函数把键盘事件分发给它了。

 

        我们先来看一InputDispatcher是如何找到当前激活的Activity窗口的,然后再分析它把键盘事件分发给当前激活Activity窗口的过程。

        Step 13. InputDispatcher.findFocusedWindowTargetsLocked

        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,  
  2.         const EventEntry* entry, nsecs_t* nextWakeupTime) {  
  3.     mCurrentInputTargets.clear();  
  4.   
  5.     int32_t injectionResult;  
  6.   
  7.     // If there is no currently focused window and no focused application  
  8.     // then drop the event.  
  9.     if (! mFocusedWindow) {  
  10.         if (mFocusedApplication) {  
  11.             ......  
  12.             injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  13.                 mFocusedApplication, NULL, nextWakeupTime);  
  14.             goto Unresponsive;  
  15.         }  
  16.   
  17.         ......  
  18.         injectionResult = INPUT_EVENT_INJECTION_FAILED;  
  19.         goto Failed;  
  20.     }  
  21.   
  22.     // Check permissions.  
  23.     if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {  
  24.         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;  
  25.         goto Failed;  
  26.     }  
  27.   
  28.     // If the currently focused window is paused then keep waiting.  
  29.     if (mFocusedWindow->paused) {  
  30.         ......  
  31.         injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  32.             mFocusedApplication, mFocusedWindow, nextWakeupTime);  
  33.         goto Unresponsive;  
  34.     }  
  35.   
  36.     // If the currently focused window is still working on previous events then keep waiting.  
  37.     if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {  
  38.         ......  
  39.         injectionResult = handleTargetsNotReadyLocked(currentTime, entry,  
  40.             mFocusedApplication, mFocusedWindow, nextWakeupTime);  
  41.         goto Unresponsive;  
  42.     }  
  43.   
  44.     // Success!  Output targets.  
  45.     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;  
  46.     addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));  
  47.   
  48.     ......  
  49.   
  50.     return injectionResult;  
  51. }  

        回忆前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 9中,当前处于激活状态的应用程序会通过调用InputDispatcher类setInputWindows函数把把当前获得焦点的Activity窗口设置到mFocusedWindow中去,因此,这里的mFocusedWindow不为NULL,于是,就通过了第一个if语句的检查。

 

        第二个if语句检查权限问题,原来,这个键盘事件除了是由硬件触发的外,也可以由其它进程注入进来的,如果这个键盘事件是由其它进程注入进来的,那么entry->injectState就不为NULL,它里面包含了事件注册者的进程ID和用户ID,于是,这里就会调用checkInjectionPermission来检查这个事件注入者的进程ID和用户ID,看看它是否具有这个权限。这里我们不考虑这种情况,因此,这里的entry->injectState为NULL,于是,这个if语句的检查也通过了。

        第三个if语句检查当前激活的Activity窗口是否是处于paused状态,如果是的话,也不用进一步处理了。一般情况下,当前激活的Activity窗口都是处于resumed状态的,于是,这个if语句的检查也通过了。

        第四个if语句检查当前激活的Activity窗口是否还正在处理前一个键盘事件,如果是的话,那就要等待它处理完前一个键盘事件后再来处理新的键盘事件了。这里我们也假设当前激活的Activity窗口不是正在处理前面的键盘事件,因此,这个if语句的检查也通过了。

        最后,就调用addWindowTargetLocked函数把当前激活的Activity窗口添加到InputDispatcher类的mCurrentInputTargets成员变量中去。

        Step 14. InputDispatcher.addWindowTargetLocked

        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,  
  2.         BitSet32 pointerIds) {  
  3.     mCurrentInputTargets.push();  
  4.   
  5.     InputTarget& target = mCurrentInputTargets.editTop();  
  6.     target.inputChannel = window->inputChannel;  
  7.     target.flags = targetFlags;  
  8.     target.xOffset = - window->frameLeft;  
  9.     target.yOffset = - window->frameTop;  
  10.     target.pointerIds = pointerIds;  
  11. }  

        这个函数简单,就是把传进来的参数window添加到mCurrentInputTargets中去就完事了,后面InputDispatcher就会从mCurrentInputTargets中取出恰当的Activity窗口,然后把键盘事件分发给它。

        回到Step 12中的dispatchKeyLocked函数,它接下来就调用dispatchEventToCurrentInputTargetsLocked来进一步处理了。

        Step 15. InputDispatcher.dispatchEventToCurrentInputTargetsLocked

        这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

  1. void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,  
  2.         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {  
  3.    ......  
  4.   
  5.    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  
  6.        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  
  7.   
  8.        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  
  9.        if (connectionIndex >= 0) {  
  10.            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  11.            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  
  12.                resumeWithAppendedMotionSample);  
  13.        } else {  
  14.            ......  
  15.    }  
  16. }  

        这个函数的实现也比较简单,前面我们已经把当前需要接受键盘事件的Activity窗口添加到mCurrentInputTargets中去了,因此,这里就分别把它们取出来,然后调用prepareDispatchCycleLocked函数把键盘事件分发给它们处理。

 

        前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封装成了一个Connection,然后以这个InputChannel中的Receive Pipe Fd作为键值把这个Connection对象保存在mConnectionsByReceiveFd中。这里,既然我们已经通过mCurrentInputTargets得到了表示当前需要接收键盘事件的Activity窗口的InputTarget对象,而且这个InputTarget对象的inputChannel就表示当初在InputDispatcher中注册的Server端InputChannel,因此,这里就可以把这个Connection对象取出来,最后调用prepareDispatchCycleLocked函数来进一步处理。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966635,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
275 4
|
1月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
77 14
|
2月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
95 21
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
1月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
36 8
|
1月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
1月前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
56 0
|
8月前
|
移动开发 安全 Android开发
构建高效Android应用:Kotlin协程的实践与优化策略
【5月更文挑战第30天】 在移动开发领域,性能优化始终是关键议题之一。特别是对于Android开发者来说,如何在保证应用流畅性的同时,提升代码的执行效率,已成为不断探索的主题。近年来,Kotlin语言凭借其简洁、安全和实用的特性,在Android开发中得到了广泛的应用。其中,Kotlin协程作为一种新的并发处理机制,为编写异步、非阻塞性的代码提供了强大工具。本文将深入探讨Kotlin协程在Android开发中的应用实践,以及如何通过协程优化应用性能,帮助开发者构建更高效的Android应用。
|
8月前
|
API 调度 Android开发
打造高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第27天】在移动开发领域,性能优化和响应速度是衡量应用质量的关键因素。随着Kotlin语言的普及,协程作为其核心特性之一,为Android开发者提供了一种全新的并发处理方式。本文深入探讨了Kotlin协程在Android应用开发中的优势,并通过实例演示如何在实际项目中有效利用协程提升应用性能和用户体验。
|
8月前
|
移动开发 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第21天】在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着Kotlin语言在Android平台的广泛采纳,其并发处理的强大工具—协程(Coroutines),已成为提升应用响应性和效率的关键因素。本文将深入分析Kotlin协程的核心原理,探讨其在Android开发中的优势,并通过实例演示如何有效利用协程来优化应用性能,打造更加流畅的用户体验。
79 4

热门文章

最新文章