Android 事件捕捉和处理流程

来源:互联网 发布:iphone4s越狱软件源 编辑:程序博客网 时间:2024/05/21 06:37

1.    InputManagerService(IMS)

1.1     IMS初始化

          在系统启动的时候,SystemServer会启动窗口管理服务WindowManagerService(WMS),同时启动InputManagerService(IMS) ,并且把IMS实例传入WMS之中,以便后面调用。然后调用start()方法开启工作线程,

WMS在启动的时候就会通过系统输入管理器IMS来总负责监控键盘消息。

这些键盘消息一般都是分发给当前激活的Activity窗口来处理的,因此,当前激活的Activity窗口在创建的时候,会到WindowManagerService中去注册一个接收键盘消息的通道,表明它要处理键盘消息,而当InputManager监控到有键盘消息时,就会分给给它处理。

 

 

1.2      IputDispatcherThread分发消息

InputDispatcher线程是负责把键盘消息分发给当前激活的Activity窗口的,它的调用流程是通过Looper.pollOnce方法读取事件,在使用Looper类中,会创建一个管道,当调用Looper类的pollOnce函数时,如果管道中没有内容可读,那么当前线程就会进入到空闲等待状态;当有键盘事件发生时,InputReader就会往这个管道中写入新的内容,这样就会唤醒前面正在等待键盘事件发生的线程

1.3     InputReaderThread读取消息

InputReaderThread概括起来就是一个独立的循环线程加上一些必要的辅助类,它的工作相对单一,即不断的轮询相关设备节点是否有新的事件发生。

它的核心是InputReader类。InputReader实际上不直接去访问设备节点,而是通过EventHub来完成这一工作。EventHub通过读取/dev/input/下的相关文件来判读是否有新事件,来通知InputReader

1.4      设置激活窗口

什么是激活窗口,就是当前需要把消息传递给谁处理的界面,在InputManager启动以后,就开始负责监控键盘输入事件了。当InputManager监控到有键盘消息时,就会先找到当前被激活的窗口,然后找到其在InputManager中对应的键盘消息接收通道,通过这个通道在InputManager中的一端来通知在应用程序消息循环中的另一端,就把键盘消息分发给当前激活的Activity窗口了。

1.5      消息通道InputChannel

  应用程序会为这个Activity激活窗口和InputManager之间创建一个键盘消息接收通道,这个通道的一端由一个Server端的InputChannel构成,另一端由Client端的InputChannel构成,Server端的InputChannel注册在由InputManager所管理的InputDispatcher中,而Client端的InputChannel注册在由应用程序主线程的消息循环对象Looper中。

2.    PhoneWindowManager中按键处理

2.1     Power键处理

以电源键KeyEvent.KEYCODE_POWER为例介绍一下放入队列前的预处理:

按下时:

快速启动:sys.quickboot.enable

如果当前按键电源键并且是灭屏状态,

当按下时获取锁,并延迟0.5秒启动快速启动操作。

但是当弹出按键时,释放锁,如果按键时间没有超过0.5秒,则删除快速启动操作。

手电筒:

当按下电源键并且是灭屏状态,如果此时手电筒打开,则发送关闭手电筒广播。

3.  导航栏隐藏、显示

4截屏功能:

同时按下电源键和音量下键,延迟2.5*0.5秒执行截图

5.  静音和挂断电话

         如果当前有来电,则静音。如果正在通话则挂断电话。

6.  长按操作

如果当前不是灭屏状态,并且没有执行挂断电话的操作,并且同时没有触发音量上下键,则延迟0.5秒执行长按操作,打开长按电源键界面。

松开时:

如果截图操作未执行,则删除此操作。

如果长按操作未执行,则删除此操作。

返回结果:

电源执行完后不会放入队列,所以刚开始进行了如下操作:

result &=~ACTION_PASS_TO_USER;

    判断当前是否是灭屏状态,是的话返回ACTION_WAKE_UP,不是返回

ACTION_GO_TO_SLEEP

 

caseKeyEvent.KEYCODE_POWER: {

//结果不会传递给应用

                result &=~ACTION_PASS_TO_USER; 

                if (down) {

                   mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,event.getDownTime(),

                           isImmersiveMode(mLastSystemUiFlags));

//  屏幕如果亮着,则判断是否是同时按键音量下键,打开截图功能

                    if (isScreenOn &&!mPowerKeyTriggered

                            &&(event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {

                        mPowerKeyTriggered =true;

                        mPowerKeyTime =event.getDownTime();

                       interceptScreenshotChord();

                    }

//如果当前有来电,则来电铃声静音,如果当前正在通话,则挂断电话

                    ITelephony telephonyService= getTelephonyService();

                    boolean hungUp = false;

                    if (telephonyService !=null) {

                        try {

                            if(telephonyService.isRinging()) {

                                // PressingPower while there's a ringing incoming

                                // call should silence the ringer.

                               telephonyService.silenceRinger();

                            /// M:[ALPS00093981] @{

                            } else if((isScreenOn

                                ||  mScreenOffReason ==OFF_BECAUSE_OF_PROX_SENSOR)

                            /// @}

                                &&(mIncallPowerBehavior

                                    &Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0

                                    && telephonyService.isOffhook()){

                                // Otherwise,if "Power button ends call" is enabled,

                                // the Powerbutton will hang up any current active call.

                                hungUp =telephonyService.endCall();

                            }

                        } catch(RemoteException ex) {

                            Log.w(TAG,"ITelephony threw RemoteException", ex);

                        }

                    }

    //如果没有进行上面的任何处理,则触发长按电源键操作,打开关机界面

                   interceptPowerKeyDown(!isScreenOn || hungUp

                            ||mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);

                } else {

                    mPowerKeyTriggered = false;

//松开按键是,如果延迟的截图操作还没执行,则删除

                   cancelPendingScreenshotChordAction();

//如果延迟的长按电源键还没执行,则删除

                    if(interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {

//返回唤醒或睡眠结果

                        result = (result &~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;

                    }

                    mPendingPowerKeyUpCanceled= false;

                }

                break;

            }

2.2     Home按键处理

KeyEvent.KEYCODE_HOME为例介绍一下分发前的预处理:

 

一:松开时

1 如果打开了任务管理界面,则关闭

2 如果有来电,则不做处理,返回继续分发

3 跳转到HOME界面

:如果是锁屏界面,则返回不继续分发

三:如果第一次按下home键,而且是双击,则执行双击操作。否则执行预加载最近使用的任务

四:如果是长按home键,则打开任务管理器

 

if (keyCode == KeyEvent.KEYCODE_HOME) {

            //如果应用要处理home键,则应用需要设置“FLAG_HOMEKEY_DISPATCHED”属性,

           系统不处理,直接传递给应用处理

/// M: [ALPS00054781]Dispatch the home key to theapplication @{

            if (win!= null && win.getAttrs() != null) {

               final int flag = win.getAttrs().flags;

                if((flag & WindowManager.LayoutParams.FLAG_HOMEKEY_DISPATCHED) != 0) {

                   // the window wants to handle the home key, so dispatch it to it.

                   return 0;

                }

            }

            /// @}

 

            // Ifwe have released the home key, and didn't do anything else

            //while it was pressed, then it is time to go home!

//松开按键后

            if(!down) {

               cancelPreloadRecentApps();

 

               mHomePressed = false;

                if(mHomeConsumed) {

                   mHomeConsumed = false;

                   return -1;

                }

 

                if(canceled) {

                   Log.i(TAG, "Ignoring HOME; event canceled.");

                   return -1;

                }

 

                //If an incoming call is ringing, HOME is totally disabled.

                //(The user is already on the InCallScreen at this point,

                //and his ONLY options are to answer or reject the call.)

              //如果此时有来电,则不处理home

                try{

                   ITelephony telephonyService = getTelephonyService();

                   if (telephonyService != null && telephonyService.isRinging()) {

                       Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");

                       return -1;

                   }

                }catch (RemoteException ex) {

                   Log.w(TAG, "RemoteException from getPhoneInterface()", ex);

                }

 

                //Delay handling home if a double-tap is possible.

//如果运行双击home事件,延迟运行返回桌面操作

                if(mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {

                   mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case

                   mHomeDoubleTapPending = true;

                   mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,

                            ViewConfiguration.getDoubleTapTimeout());

                   return -1;

                }

 

                //Go home!

               launchHomeFromHotKey();

               return -1;

            }

 

            // If asystem window has focus, then it doesn't make sense

            //right now to interact with applications.

//如果当前处理锁屏界面,则不处理home

           WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;

            if(attrs != null) {

               final int type = attrs.type;

                if(type == WindowManager.LayoutParams.TYPE_KEYGUARD

                       || type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM

                       || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {

                   // the "app" is keyguard, so give it the key

                   return 0;

                }

               final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;

                for(int i=0; i<typeCount; i++) {

                    if (type ==WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {

                       // don't do anything, but also don't pass it to the app

                       return -1;

                   }

                }

            }

 

            //Remember that home is pressed and handle special actions.

           //第一次按下home键时

            if(repeatCount == 0) {

               mHomePressed = true;

//如果运行双击事件,则执行双击事件打开任务管理器

                if(mHomeDoubleTapPending) {

                   mHomeDoubleTapPending = false;

                   mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);

                   handleDoubleTapOnHome();

                }else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI

                       || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {

//预加载将要显示的任务管理器中的任务

                   preloadRecentApps();

                }

            } elseif ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {

//如果是长按home键,则打开任务管理器

                if(!keyguardOn) {

                   handleLongPressOnHome();

                }

            }

           //返回-1,则不在往下传递

            return-1;

}

3.2 层点击事件处理流程

1.       当TouchEvent发生时,首先在Activity层处理,传递由Activity的 dispatchTouchEvent方法进行分发,如果dispatchTouchEvent返回true,则交给这个Activity的onTouchEvent处理,如果 dispatchTouchEvent返回 false,则交给这个ViewGroup处理。

2.       ViewGroup首先由dispatchTouchEvent方法进行分发,和Activity不同的是如果dispatchTouchEvent返回 false,则传递给interceptTouchEvent方法来决定是否要拦截这个事件,
如果 interceptTouchEvent返回 true,也就是拦截掉了,则交给它的onTouchEvent来处理,
如果 interceptTouchEvent返回 false,那么就传递给子 view,由子 view 的dispatchTouchEvent再来开始这个事件的分发。

在view层,如果事件传递onTouchEvent上了,这个方法返回了 false,那么这个事件会从这个 view往上传递,都是onTouchEvent来接收,直到事件被处理。

0 0
原创粉丝点击