• Android按键事件传递流程(二)


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

    由3.2和4.5.4节可知,当InputDispatcher通过服务端管道向socket文件描述符发送消息后,epoll机制监听到了I/O事件,epoll_wait就会执行返回发生事件的个数给eventCount,主线程开始执行epoll_wait后面的代码:

    fd是客户端socket文件描述符,不是mWakeReadPipeFd,因此if语句不成立,进入else子句。mRequests不为空(3.4.2.2节中已经把Request保存在了mRequests中),pushResponse函数把request取出来赋给response,再放到mResponses容器中保存。

    mMessageEnvelopes被初始化为空,也没有加入数据,依然为空,这句:

    不成立,跳过while循环。

    mResponses不为空,for循环取出里面的response对象,然后执行:

    fd就是服务端socket文件描述符,events是发生的事件,data为空,response.request.callback就是addFd中的第4个实参NativeInputEventReceiver对象也是LooperCallback对象,这句话就是回调主线程中的NativeInputEventReceiver对象的handleEvent函数

    5.1    NativeInputEventReceiver的handleEvent

    先对events事件进程必要的检查,如果包含ALOOPER_EVENT_ERROR或ALOOPER_EVENT_HANGUP表示管道关闭,这种情况下丢弃事件

    如果为ALOOPER_EVENT_INPUT事件,调用consumeEvents继续执行;如果为ALOOPER_EVENT_OUTPUT表示客户端已经收到数据,需要发送一个完成信号给服务端

    5.2    NativeInputEventReceiver的consumeEvents

    在for循环中调用InputConsumer对象的consume函数从socket客户端获取InputDispatcher发送来的事件保存到inputEvent对象中,先分析consume方法,然后返回来再看后面的代码

    在consume函数中有这句:

    receiveMessage的源码中有这句:

    在3.2节通过send向服务端socket发送了数据,那么在此处通过recv从客户端socket上获取的消息并保存到mMsg中,如果成功,再根据事件类型选择case语句:

    把消息保存到KeyEvent对象中,再付给outEvent,如果一切成功,返回OK到5.2节这句mInputConsumer.consume的后面继续执行:

    mReceiverWeakGlobal就是传递过来的WindowInputEventReceiver对象的本地引用

    如果读取所有事件成功,再根据事件的类型选择相应执行语句,如果是按键事件,就调用android_view_KeyEvent_fromNative把按键事件传递给java层KeyEvent对象并初始化,然后返回该对象赋给变量inputEventObj,继续执行到这句:

    receiverObj.get()得到WindowInputEventReceiver对象,gInputEventReceiverClassInfo.dispatchInputEvent就是待调方法的id号,CallVoidMethod函数的作用就是调用第一个参数WindowInputEventReceiver对象的dispatchInputEvent方法,后面两个实参会传递给该方法,如果这一切都成功,就把按键事件传递到java层处理。

    由于WindowInputEventReceiver中没有实现dispatchInputEvent,因此直接调用父类InputEventReceiver的dispatchInputEvent方法

    5.3    InputEventReceiver的dispatchInputEvent

    onInputEvent在WindowInputEventReceiver中已经实现,就调用WindowInputEventReceiver中的onInputEvent方法,onInputEvent调用了enqueueInputEvent

    5.4    ViewRootImpl的enqueueInputEvent

    obtainQueuedInputEvent方法把输入事件封装成QueuedInputEvent对象并放入到QueuedInputEvent对象池中,然后取出一个QueuedInputEvent事件后按照次序排列,再调用doProcessInputEvents方法从队列中循环取出事件发送给输入法或者应用程序等不同阶段进行处理。

    doProcessInputEvents的调用过程:

    doProcessInputEvents  —-> deliverInputEvent  —-> stage.deliver(q)

    当前事件还没有处理,因此不包含FLAG_FINISHED标致,if语句不成立;正常情况下不会丢弃当前事件,第一个else子句也不成立,执行最后一个else子句。apply的第二个参数是onProcess方法,ViewRootImpl中有8个onProcess方法,具体调用哪个?

    在setView方法最后创建了很多InputStage对象:

    InputStage是抽象基类,ViewPostImeInputStage,EarlyPostImeInputStage,ViewPreImeInputStage等对象都是为了处理输入事件在不同阶段而创建的,比如:ViewPostImeInputStage表示发送输入事件给view树进行处理,这些输入事件都是在输入法处理之后的。ViewPreImeInputStage表示输入事件必须在输入法处理之前发送给view树处理。

    ViewPreImeInputStage表示在输入法之前处理,ImeInputStage表示进入输入法处理,ViewPostImeInputStage表示发送给视图。如果有输入法窗口,就先传输给ViewPreImeInputStage处理,如果没有,传输给ViewPostImeInputStage,一般情况下,都是传给ViewPostImeInputStage。

    此处会调用ViewPostImeInputStage的onProcess来处理:

    if语句成立,调用processKeyEvent

    5.5     ViewPostImeInputStage的processKeyEvent

    processKeyEvent方法非常重要了,做了很多任务

    mView是DecorView,所有view的根,这段话就是把按键事件传给view处理,从此处开始就正式转交给应用层,在第6节将进行详细分析

    处理Ctrl组合按键

    处理所有回退按键事件,主要是一些还没有处理的特殊按键。比如相机拍照、拨号按键等。如果特殊按键没有在PhoneWindowManager、view树、窗口中处理,就传到此处

    处理方向键和TAB键,找到获得焦点的view并把这几个按键传递过去,如果没有view有焦点,就找一个最合适的view并把按键传递过去

    小结:

    应用程序客户端通过NativeInputEventReceiver的InputConsumer方法从客户端管道InputChannel中获取事件消息,经过过滤,转化成应用层按键类型,再把按键事件传递到输入法窗口,应用层

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

    由5.5节这句:

    可知,按键事件传递到了mView的dispatchKeyEvent方法,mView就是PhoneWindow内部类DecorView对象,因此,应用层按键事件就从DecorView的dispatchKeyEvent方法开始

    也可以这样理解,InputDispatcher先找到当前获得焦点的窗口,把事件发送给该窗口,窗口在启动activity时会创建,按键事件就传递到了获得焦点的窗口对应的所有view的根类DecorView,也可以说传递给了获得焦点的窗口对应的Activity对象。

    Q10    mView是什么时候创建的?如何传递的?见6.6节

    6.1    DecorView的dispatchKeyEvent

    前面两个if语句是快捷按键的处理

    getCallback返回的是CallBack对象cb,cb对象代表一个Activity或Dialog对象,一般情况下不为空。本文主要讨论Activity,不再使用“Activity或Dialog对象”这样的术语;

    mFeatureId:代表应用程序的特征标识或者整个屏幕的标识,如果是应用程序,就为-1,具体赋值过程为:

    Activity的onCreate —-> setContentView —-> PhoneWindow的setContentView —-> installDecor() —->

    generateDecor() —-> new DecorView(getContext(), -1)

    如果Activity对象不为空,mFeatureId为-1,调用Activity对象的dispatchKeyEvent方法,将在6.2节分析;

    如果为空,就调用super.dispatchKeyEvent(event)即父类ViewGroup的dispatchKeyEvent,将在6.2.1节分析。

    如果返回结果为true,表明已经消耗,按键事件不再往后传递,否则执行到:

    这一步传递到PhoneWindow的onKeyDown、onKeyUp方法,将在6.4节分析。

    Q11    cb对象为什么是Activity?

    在这篇文章:启动Activity的流程(Launcher中点击图标启动)

    过程18中创建Activity对象后,调用了Activity对象的attach进行初始化,在attach中有:

    PolicyManager类的静态方法makeNewWindow —-> Policy的makeNewWindow —-> new PhoneWindow(context)

    makeNewWindow 最终创建了一个PhoneWindow对象,setCallback方法把该this对象即Activity传过去赋值给mCallback,然后getCallback返回mCallback即该Activity对象

    通过这段话可知,当启动某个应用的Activity时,系统会创建一个PhoneWindow对象与之对应并拥有一个该对象引用,在PhoneWindow对象中通过该对象引用回调Activity的方法,比如dispatchKeyEvent。

    6.2    Activity的dispatchKeyEvent

    Activity的dispatchKeyEvent起到拦截按键作用,如果这一步不处理,将分发给view或viewGroup处理。

    如果有按键、触摸、轨迹球事件分发给Activity时,在具体事件处理之前,会回调onUserInteraction,一般情况下,用户需要自行实现该方法,与onUserLeaveHint一起配合使用辅助Activity管理状态栏通知。在按键按下、抬起时都会触发该方法回调;但在触摸时,只有触摸按下时被回调,触摸移动、抬起时不会回调。

    如果有Menu键且状态栏ActionBar消耗了该键,就直接返回true;否则继续往下处理

    获得当前Activity对应的Window对象,也就是PhoneWindow,把按键事件传递给PhoneWindow对象,主要对Back按键松开的特殊处理,如果没有消耗,连同其它按键事件一起传递到super.dispatchKeyEvent即父类ViewGroup,具体处理过程在6.2.1节;

    如果父类ViewGroup也没有处理,传递到KeyEvent的dispatch方法,具体处理过程在6.3节。

    6.2.1    ViewGroup的dispatchKeyEvent

    对按键事件进行一致性检查,这种检查防止同样的错误多次发生,如果有错误,日志会打印出来

    PFLAG_FOCUSED表示获得焦点,PFLAG_HAS_BOUNDS表示大小、边界被确定了,如果ViewGroup本身有焦点且其大小已确定,就调用该ViewGroup的父类即View的dispatchKeyEvent来处理,具体在6.2.1.1中分析

    mFocused是ViewGroup内部包含的获得焦点的子view,如果该子view获得焦点且大小、边界已确定,就调用该子view的dispatchKeyEvent处理。子view既可以是ViewGroup、LinearLayout、RelativeLayout等布局也可以是view、TextView、Button、ListView等,第二个if语句就是递归传递到所有布局和view中

    假如某Activity的布局是自定义一个LinearLayout,称为A,其内部包含一个LinearLayout,称为B,B中包含一个TextView C,C有焦点。dispatchKeyEvent传递流程是:ViewGroup —-> A —-> B —-> C

    传递到view时的具体情况在6.2.2节

    6.2.2    view的dispatchKeyEvent

    ListenerInfo类专门描述所有view相关监听器信息的类,比如OnFocusChangeListener、OnScrollChangeListener、OnClickListener、mOnTouchListener等。

    当某个view比如button、imageView设置了某个监听器时,在setOnXXXListener方法中就会调用getListenerInfo方法创建ListenerInfo对象并赋值给mListenerInfo变量,因此,该变量肯定不为空,如果没有设置监听器,那就为空,假设有监听器,下面这条语句成立:

    如果view设置了监听器,且enable属性为true,会优先调用OnKeyListener的onKey方法处理,如果没有设置该监听器或者该监听器没有消耗掉,按键继续传递到KeyEvent的dispatch

    6.2.2.1    KeyEvent的dispatch

    receiver:既可能是Activity对象,又可能是view对象,当前是view对象,因为是view的dispatchKeyEvent传过来的,在6.3节,传递过来的是Activity对象

    state:通过getKeyDispatcherState返回KeyEvent内部类DispatcherState对象,该对象用来进行高级别的按键事件处理,如长按事件等;在getKeyDispatcherState方法内部,mAttachInfo是AttachInfo对象,当view关联到窗口时的一系列信息,AttachInfo类用来描述、跟踪这些信息,一般情况下不为空

    第三个参数target,实参是this,既是Activity或view对象,也是KeyEvent的内部类CallBack类型

    case语句先处理按键按下ACTION_DOWN动作:

    先清掉FLAG_START_TRACKING标记,再调用view的onKeyDown方法,此方法在6.2.2.2节进行分析

    如果onKeyDown返回true,表明已经消耗,res为true

    如果view的onKeyDown已经消耗掉,且是第一次按下,mFlags包含FLAG_START_TRACKING,就调用startTracking跟踪按键事件,这样做便于判断是否是长按事件的条件,如果有长按事件,并且正在跟踪当前按键,就调用view的onKeyLongPress处理:

    源码总是返回false,不作任何处理,用户可以根据需求重写该方法来实现长按

    case语句处理按键松开ACTION_UP动作:

    回调view的onKeyUp方法,mKeyCode是按键码值,this是KeyEvent对象,此方法也在6.2.2.2节分析,此时,dispatch主要作用是回调view的onKeyDown、onKeyUp,注意与6.3节的区别。

    6.2.2.2    view的onKeyDown、onKeyUp

    view的onKeyDown:

    主要针对KEYCODE_DPAD_CENTER、KEYCODE_ENTER事件进行处理:

    如果当前按键事件包含KEYCODE_DPAD_CENTER、KEYCODE_ENTER,并且view设置了DISABLED属性,直接返回true,说明该view已经被按下;

    如果view设置了单击CLICKABLE或长按状态LONG_CLICKABLE,就调用setPressed把该view设置为PRESSED状态,同时更新其绘制状态(显示状态,比如更换了背景图片、颜色等);如果仅是长按状态,系统在500秒后执行下面几种情形:

    a. 如果有长按监听器OnLongClickListener,就回调onLongClick,如果成功,系统会发出一个触觉反馈;

    b. 如果没有长按监听器,就显示一个菜单。

    如果按键事件不包含KEYCODE_DPAD_CENTER、KEYCODE_ENTER,onKeyDown不作任何处理,直接return false

    view的onKeyUp:

    在onKeyUp方法中,setPressed(false)去除view的pressed状态,同时更新其绘制状态(显示状态)

    如果在onKeyDown中没有处理长按事件,那么mHasPerformedLongPress为false,就把长按事件对象从消息的队列中移除。最后,调用performClick:

    如果设置了OnClickListener监听器,就回调onClick方法。

    由此可知,当遥控器按键(KEYCODE_DPAD_CENTER、KEYCODE_ENTER)松开时,如果设置了OnClickListener监听器,会调用onClick方法。

    6.3    keyEvent的dispatch

    该方法已经在6.2.2节分析过,只不过receiver对象已经不再是view,而是Activity,因为是从Activity的dispatchKeyEvent传递而来,此时,dispatch主要作用是回调Activity的onKeyDown、onKeyUp

    6.3.1    Activity的onKeyDown,onKeyUp

    如果Activity里面的任何view、布局都没有处理按键,就会传递到Activity的onKeyDown,onKeyUp。比如,当在EditText中输入文字时,Activity的onKeyDown,onKeyUp不会接收到按键事件,因为EditText有自己的处理按键事件的方法,如果此时把焦点从EditText移走,onKeyDown,onKeyUp就会接收到按键事件。

    onKeyDown源码:

    这段话需要结合onKeyUp来看:

    如果在Android2.1之前的版本(不包含),按下BACK键后调用onBackPressed直接退出Activity;

    如果在Android 2.1(包含)之后的版本,先调用startTracking方法把mFlags置为FLAG_START_TRACKING,系统会跟踪按键传递过程,直到松开按键进入到onKeyUp方法时才会调用onBackPressed,可以重写该方法退出Activity,也就是说,只有松开BACK按键时才会退出Activity,如果不松开不会退出Activity。

    随后根据按键模式mDefaultKeyMode决定做哪些事情,此处不是重点,本文不作分析

    onKeyUp源码

    if语句就是配合onKeyDown使用的,如果在Android 2.1(包含)之后的版本,松开按键时才会退出Activity;其他按键直接返回false,一般重写onKeyUp实现自己的需求。

    如果onKeyDown,onKeyUp没有消耗掉按键事件,就逆向返回到KeyEvent的dispatch中处理,如果dispatch也没有消耗掉,就返回到Activity —-> DecorView —-> PhoneWindow,进入到 PhoneWindow中处理。

    6.4    PhoneWindow的onKeyDown、onKeyUp

    经过上述方法处理,如果返回false,说明所有应用程序层的view、viewGroup、Activity都没有消耗掉,按键事件传递到了当前窗口window中进行处理

    onKeyDown源码:

    onKeyDown/onKeyUp方法主要针对当前获得焦点的窗口对一些特殊按键进行处理,包括音量+/-,多媒体控制按键,MENU,BACK

    注意:PhoneFallbackEventHandler中也是对特殊按键进行处理,但是那是针对所有所有的窗口,包括当前获得焦点的窗口,而PhoneWindow只针对当前获得焦点的窗口

    6.5    小结

    应用层按键事件传递时涉及到很多情况,大概传递流程:
    a. 应用层按键事件传递到view树根DecorView后分为两步:view树内部;view树外部(获得焦点的窗口)
    如果view树内部没有消耗,就传递到view树外部,即传递给获得焦点的窗口的onKeyDown/onKeyUp;

    b. view树内部一般先传递到当前Activity对象,如果没有消耗,传递到Activity的onKeyDown/onKeyUp;

    c. Activity对象内部先分发给ViewGroup,viewGroup如果本身有焦点就传递给其父类view;
    如果viewGroup本身没有焦点,就传递给其获得焦点的子view。子view分为两种情况:
    如果子view是LinearLayout等常见布局,就递归传递过去,最后传递给获得焦点的view视图;
    如果子view是纯粹的view视图,就传递给该视图;

    d. view视图内部,如果设置了OnKeyListener监听器,就传递给OnKey;
    如果没有OnKeyListener监听器,就分发给KeyEvent的dispatch,dispatch主要回调view的onKeyDown/onKeyUp;

    e. 在view的onKeyDown/onKeyUp中,如果是DPAD_CENTER,KEYCODE_ENTER,直接处理;
    否则,更新绘制状态、执行长按处理、执行onClick方法等。

    该小结没有考虑所有条件,只是大概给出传递流程,因为很多时候重写某个方法返回true不再传递下去,因此也就没有过多步骤。

    6.6    mView的创建过程

    启动Activity的流程(Launcher中点击图标启动)这篇文章中的过程18的handleResumeActivity方法中,有这段语句:

    r.activity就是新启动的目标Activity对象,getWindow返回mWindow对象,mWindow的创建过程:

    PolicyManager对象的makeNewWindow —-> sPolicy.makeNewWindow(context)

    sPolicy是Policy对象,程序调到了Policy的makeNewWindow:

    创建了一个PhoneWindow对象并返回给mWindow,再赋值给r.window

    r.window.getDecorView()方法调用PhoneWindow对象的getDecorView方法

    如果mDecor为空,就调用installDecor方法新创建一个DecorView对象,否则,直接返回该对象。

    r.window.getDecorView() —-> PhoneWindow的installDecor方法 —->  mDecor = generateDecor() —->

    return new DecorView(getContext(), -1)

    在PhoneWindow中创建了一个DecorView对象并返回给decor变量

    通过这两句可知,启动一个新的Activity时,系统会创建一个对应的widow窗口对象(实际是PhoneWindow对象),这是一对一关系;同时,如果已经有了DecorView就复用之,否则,新创建一个DecorView对象(DecorView最终继承于View,也是一个View对象),这是多对一关系。

    getWindowManager返回mWindowManager变量,mWindowManager的赋值语句为:

    已经可知mWindow是一个PhoneWindow对象,这样就调到了PhoneWindow的getWindowManager方法,PhoneWindow中没有实现getWindowManager,直接调用父类Window的getWindowManager:

    WindowManagerImpl继承了WindowManager,createLocalWindowManager方法源码:

    创建了一个与Activity对应的WindowManagerImpl对象。

    调用WindowManagerImpl对象的addView方法,源码:

    mGlobal是WindowManagerGlobal的单例对象,addView方法中有:

    root是ViewRootImpl对象,调用其setView方法把DecorView对象传递过去并赋值给mView

    小结:mView就是与Activity对应的DecorView对象,在创建PhoneWindow对象时创建的。

    7    特殊按键如何处理

    特殊按键处理方法主要有:

    interceptKeyBeforeQueueing

    interceptKeyBeforeDispatching

    PhoneWindow的onKeyDown/onKeyUp

    PhoneFallbackEventHandler的dispatchKeyEvent

    每个方法用在不同的时刻

    7.1    interceptKeyBeforeQueueing

    在2.2节提到,NativeInputManager传递过来后赋给了mPolicy变量,interceptKeyBeforeQueueing在NativeInputManager中也实现了,interceptKeyBeforeQueueing用以处理系统级按键,比如HOME、TVSOURCE等

    7.1.1    com_android_server_input_InputManagerService.cpp的interceptKeyBeforeQueueing

    设置按键标志为活动状态

    android_view_KeyEvent_fromNative方法中,通过调用JNI接口的CallStaticObjectMethod方法获得第二个参数gKeyEventClassInfo.obtain返回的值并把值赋给eventObj,gKeyEventClassInfo.obtain是什么?在register_android_view_KeyEvent函数中通过findclass得知,gKeyEventClassInfo.clazz就是java层KeyEvent类的类本地引用,gKeyEventClassInfo.obtain就是KeyEvent中的obtain方法在本地的method id号;CallStaticObjectMethod函数调用KeyEvent中obtain方法,返回值是java层KeyEvent对象,因此,android_view_KeyEvent_fromNative返回值为java层的KeyEvent对象在本地的引用,赋给keyEventObj

    由1.1.1和1.1.2节可知,mServiceObj就是传递过来的InputManagerService对象,gServiceClassInfo.interceptKeyBeforeQueueing保存了InputManagerService对象中interceptKeyBeforeQueueing方法的method id号,CallIntMethod方法调用InputManagerService中interceptKeyBeforeQueueing方法并把返回值赋给wmActions变量

    根据返回值wmActions决定特殊按键的走向,如果wmActions为WM_ACTION_PASS_TO_USER即1,那么把policyFlags设为POLICY_FLAG_PASS_TO_USER,意思是说该按键应该传输给应用程序处理,不在framework层处理,比如,HOME按键不应该传给应用程序,而应在framework层处理,不针对某一个应用程序,针对的整个系统,在任何应用程序界面下按下HOME都能起作用。

    7.1.2    InputManagerService的interceptKeyBeforeQueueing

    mWindowManagerCallbacks是InputMonitor对象,在1.4节提到过,该对象在InputManagerService对象创建后通过其setWindowManagerCallbacks传递过去,便于回调InputMonitor对象中的方法

    7.3    InputMonitor的interceptKeyBeforeQueueing

    mPolicy是WindowManageService对象在初始化时创建的PhoneWindowManager对象,因此,最终调到了PhoneWindowManager的interceptKeyBeforeQueueing

    interceptKeyBeforeQueueing作用:当按键事件从设备中读取后,对按键进行最早期拦截预处理,因为某些特殊按键直接影响设备状态,比如,电源键、唤醒键,此外,还包括拨号键、挂号键、音量键等

    7.2    interceptKeyBeforeDispatching

    在3.1节提到过doInterceptKeyBeforeDispatchingLockedInterruptible函数,这也是拦截按键方法

    7.2.1    InputDispatcher的doInterceptKeyBeforeDispatchingLockedInterruptible

    在1.1.5节提到,NativeInputManager传递过来后赋给了mPolicy变量,所以:

    调到了NativeInputManager中的interceptKeyBeforeDispatching,经过与7.1节interceptKeyBeforeQueueing类似的调用过程,interceptKeyBeforeDispatching函数最终会调用到PhoneWindowManager中同名方法。

    interceptKeyBeforeDispatching作用:在input dispatcher thread把按键分发给窗口之前拦截,根据某种策略决定如何处理按键事件。

    具体策略为:

    如果返回值delay为-1,interceptKeyResult设置为INTERCEPT_KEY_RESULT_SKIP,表示按键事件已经消耗掉,不需要传给应用程序;

    如果delay等于0,interceptKeyResult设置为INTERCEPT_KEY_RESULT_CONTINUE,表示按键事件没有特殊处理,继续传递给应用程序;

    如果delay大于0,interceptKeyResult设置为INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,表示按键事件需要重新执行一次,同时设置按键下一次执行时间为now() + delay

    interceptKeyBeforeDispatching中拦截的特殊按键有:HOME, MENU, SEARCH, SOURCE通道, 亮度调节等

    7.3    PhoneWindow的onKeyDown/onKeyUp

    在6.4节已经分析过,针对当前获得焦点的窗口进行处理

    7.4    PhoneFallbackEventHandler的dispatchKeyEvent

    在5.5节,有这样一句:

    mFallbackEventHandler就是PhoneFallbackEventHandler对象,这是最后拦截特殊按键进行处理的方法,与 PhoneWindow区别的是,PhoneFallbackEventHandler针对所有窗口的

    7.5    小结

    按键事件的特殊处理可用逻辑执行顺序简单表达:

    PhoneWindowManager的interceptKeyBeforeQueueing —->PhoneWindowManager的interceptKeyBeforeDispatching —-> PhoneWindow的onKeyDown/onKeyUp —-> PhoneFallbackEventHandler的dispatchKeyEvent

    这个逻辑不是必须的,主要看按键是否消耗、按键的具体功能需求,有时候只需要在PhoneWindowManager中处理即可

    8    总结

    8.1    按键事件传递流程总结

    a. InputReaderThread获取输入设备事件后过滤,保存到InboundQueue队列中

    b. InputDispatcherThread从InboundQueue取出数据,先进行特殊处理,然后找到获得焦点的窗口,
    再把数据临时放到OutboundQueue中,然后取出数据打包后发送到服务端socket

    c. 应用程序主线程中的WindowInputEventReceiver从客户端socket上读取按键数据,再传递给应用层

    d. 应用层获取到事件后先分发给view树处理,再处理回退事件

    8.2    输入事件核心组件

    InputManagerService:输入事件的服务端核心组件,直接创建看门狗Watchdog、NativeInputManager对象,间接创建EventHub、InputManager以及InputDispatcherThread、InputReaderThread线程,通过InputManager方法间接启动接收器线程、分发器线程;对系统特殊按键的处理;注册服务端管道

    NativeInputManager:本地InputManager,创建c++层InputManager、EventHub对象

    C++ 层InputManager:创建InputDispatcher、InputReader对象,创建并启动InputDispatcherThread、InputReaderThread线程

    Java层InputManager:提供输入设备信息、按键布局等

    InputReader:输入事件接收者,从EventHub中获得原始输入事件信息

    InputDispatcher:分发事件给应用层,发送事件的服务端

    EventHub:事件的中心枢纽,收集了所有输入设备的事件,包括虚拟仿真设备事件。

    InputEventReceiver:接收输入事件的客户端

    InboundQueue:InputReaderThread读取事件后保存到InboundQueue中等到InputReaderThread接收

    outboundQueue:InputDispatcherThread从InboundQueue中取出数据放到outboundQueue中等待发送,然后从outboundQueue取出数据发送到服务端InputChannel等待应用层(或者称为客户端)接收

    c++层InputChannel:一个输入通道,包含一对本地unix socket对象,服务端InputDispatcherThread向socket对象写入数据,客户端InputEventReceiver从客户端socket读取数据

    8.3    按键分类

    一般按键:一般会传递到应用程序中进行处理,比如,在app中经常调用菜单键弹出菜单,按下方向键使得焦点移动等;主要有数字键,方向键,确认OK键,返回BACK键,菜单MENU键等;

    特殊按键:一般不传递到应用程序中,直接在framework层处理,不与某个应用有直接的关联,应用范围适用整个系统,比如,HOME键,按下HOME就返回到桌面,不限制于某个应用;音量键,控制系统音量,不特别针对某个应用;主要有电源POWER键,HOME键,音量VOLUME键,静音MUTE键等;

    TV一般按键:在TV的应用中处理的键,比如,在DTV的Activity中通过EPG调用节目信息,通过SUBTITLE调用字幕界面等;包含INFO信息键,喜爱节目FAV键,EPG键,字幕SUBTITLE,音轨TRACK键,多媒体播放键等;

    TV特殊按键:不传递到TV的应用中,在framework层进行处理,比如SOURCE通道键,在任何界面下按下SOURCE都能够调出通道界面切换通道。

    在Android手机上,常见的一般按键是MENU、BACK,特殊按键包括电源键,HOME键,音量键等

    在TV上,特殊按键主要指HOME键,SOURCE键,电源键,音量键等

    8.4    工作记录

    a.  如果主界面包括epg,channellist, TV多个窗口,需要在这些小界面上跳动焦点,如何实现

    重写Activity的dispatchkeyEvent和onKeyDown或onKeyUp,控制某个小界面获得焦点

    b.  如果app中按键不响应,通过getEvent命令检查是否有输入设备,是否能够获得按键事件,如果能,可以确保底层没问题,问题出现在应用层或Framework层,检查PhoneWindowManager, dispatchKeyEvent;如果getEvent检查不到设备,检查驱动(前提是确保硬件等正常);如果有设备,但getEvent获取不到事件,检查系统能否接收到红外信号,同时确定kl文件是否配对。

    c.  如要在任何app下都可以启动、杀死某个应用,可在PhoneWindowManager或PhoneFallbackEventHandler中对按键进行特殊处理,采用该应用的包名、类名,直接控制该应用

    转自:feeyan

  • 相关阅读:
    如何退出天擎
    git彻底删除或变更子模块
    湖北校园网PC端拨号算法逆向
    PPPoE中间人拦截以及校园网突破漫谈
    vscode打开django项目pylint提示has not "object" member
    从客户端取到浏览器返回的oauth凭证
    教程视频如何压制体积更小
    windows中的软链接硬链接等
    关于博客园和独立博客的一些打算
    拉勾抓职位简单小爬虫
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5684622.html
Copyright © 2020-2023  润新知