美文网首页Android Tech
# Android的按键消息分发机制

# Android的按键消息分发机制

作者: 背影杀手不太冷 | 来源:发表于2016-06-24 10:25 被阅读3915次

    标签(空格分隔):Android


    分发机制原理分析请参考博客一博客二博客三
    还是不太懂里面的原理分析???


    已经理解的部分:###

    1、Android中用户消息主要分为按键消息和触摸消息,他们两者之间在分发的过程中稍有不同,按键消息在发往客户端时先要调用WMS中的某些函数,如果WMS中并没有处理这个消息,那么才发往客户端的。
        按键消息直接发送给当前窗口,而触摸消息则根据触摸坐标位置来匹配所有窗口,并判断坐标落到哪个窗口区域中,然后把消息发送给相应的窗口。对于按键消息还会涉及到“生理长按”的检测,比如一直按住某个键,那么会产生一些列的按键消息,然而第1个和第2个消息之间往往会间隔较长的时间,这种设计是人类本身的生理特点决定的,因为从按下到弹起的过程中,如果CPU处理太快,会导致产生多次该消息,这往往不是用户所期望的,因此Android把这种消息处理延迟加入到了消息处理前端中,应用程序不需要关心第一次的延迟,只需按普通的DOWN消息处理。
    

    当按下物理按键的菜单键、Home键、返回键时会触发onKeyDown事件。
    执行顺序是这样的:
    当物理按键按下时
    首先触发dispatchKeyEvent
    然后触发onUserInteraction
    再次onKeyDown
    如果按下紧接着松开,则是俩步
    紧跟着触发dispatchKeyEvent
    然后触发onUserInteraction
    再次onKeyUp
    所以dispatchKeyEvent只是监控案件不管是activity还是activitygroup都会触发。

    不太肯定关于dispatchKeyEvent()::::###

    注意上面的过程有事件分发的机制,假如是ActivityGroup时,应该要在父级的activity重写dispatchKeyEvent方法返回false(可能是返回true)(此事件是交由下一级处理),不然子级的activity是不会有反应的。例如在TabHost的时候

    关于onKeyDown()、onBackPressed、onKeyUp()、onCreateOptionsMenu、###

    可以在这四个方法中判断KeyEvent.KEYCODE_BACK、KeyEvent.KEYCODE_MENU、KeyEvent.KEYCODE_SEARCH等,用于处理按下返回键、菜单键、search键。下面我们以onKeyDown()为例。

    一般我们直接在activity 中直接重现onKeyDown事件,
      有个返回值问题:renturn true,和return false。
      暂时理解的意思是:返回true 是表示处理完这个按键事件,这个按键事件被吃掉。eyEvent事件就不会传到Activity中,也即是在Activity中获取不到这个KeyEvent事件,也就是在Activity中重写onKeyDown无效。
      返回false 是表示暂时处理完这个按键事件,但是没有处理完全,按键事件没有被吃掉,Activity还会响应按键事件。KeyEvent事件还会传到Activity中,即在Activity中可以获取KeyEvent事件,也就是在Activity中重写onKeyDown有效
      如果我们想在View中拦截监听onKeyDown事件,setOnKeyListener(new OnKeyListener() ),然后重写onKey()方法,写完自己的处理逻辑后返回true,再次拦截按键消息,不再传递给activity
      
      好现在说说一般的情况下不再view拦截按键消息时,在activity的处理方式:先说一下OnkeyDown方法和OnBackPressed方法的区别;onKeyDown是兼容Android 1.0到Android 2.1,而OnBackPressed是仅适用于2.0或以上,不需要处理返回值问题

      public boolean onKeyDown(int keyCode, KeyEvent event)
        {
        //按下的如果是BACK,同时没有重复
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)
            {
         这里写自己的操作
           return true;//表示activity已经处理完毕
     }
     //否则父类调用默认的处理方法
      return super.onKeyDown(keyCode, event);
        }
    

    同时还有一种情况,就是在以前开发的程序中使用的是onKeyDown方法,但是后续版本为了兼容OnBackPressed方法。就需要两者之间进行嵌套。具体的方法如下:

       @Override
       public boolean onKeyDown(int keyCode, KeyEvent event) {
       // 是否触发按键为back键
      if (keyCode == KeyEvent.KEYCODE_BACK) {
       onBackPressed();
       return true;
       } else {// 如果不是back键正常响应
       return super.onKeyDown(keyCode, event);
       }
    
      }
    利用时间差方法完成两次返回键退出,防止误操作。
    // 退出时间
        private long currentBackPressedTime = 0;
        // 退出间隔
        private static finalint BACK_PRESSED_INTERVAL = 2000;
         //重写onBackPressed()方法,继承自退出的方法
        @Override
        publicvoid onBackPressed() {
        // 判断时间间隔
        if (System.currentTimeMillis()- currentBackPressedTime > BACK_PRESSED_INTERVAL) {
            currentBackPressedTime = System.currentTimeMillis();
            Toast.makeText(this, "再按一次返回键退出程序", Toast.LENGTH_SHORT).show();
        } else {
            // 退出
            finish();
        }
    }
    

    onKeyDown()除了监控返回键之外还可以监控菜单键,Home键,Search键。但是监控Home键比较麻烦,不像一般的监控返回键与菜单键一样简单。

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK) {
            // 监控返回键
        new Builder(TestActivity.this).setTitle("提示")
                    .setIconAttribute(android.R.attr.alertDialogIcon)
                    .setMessage("确定要退出吗?")
                    .setPositiveButton("确认", new DialogInterface.OnClickListener() {
                  @Override
         public void onClick(DialogInterface dialog, int which) {
                            TestActivity.this.finish();
                        }})
                    .setNegativeButton("取消", null)
                    .create().show();
            return false;
        } else if(keyCode == KeyEvent.KEYCODE_MENU) {
            // 监控菜单键
            Toast.makeText(TestActivity.this, "Menu", Toast.LENGTH_SHORT).show();
            else if(keyCode == KKeyEvent.KEYCODE_SEARCH) {
            // 监控Search键
            Toast.makeText(TestActivity.this, "Menu", Toast.LENGTH_SHORT).show();
            return false;
        }
        return super.onKeyDown(keyCode, event);
    }
    

    监听Home键###

    Android对屏幕下方常用的四个按键消息处理是不一致的:

    • 搜索按键的消息在onKeyDown或者onKeyUp中接收;
    • 菜单按键的消息在onCreateOptionsMenu、onKeyDown或onKeyUp方法中接收;
    • 返回按键的消息可以在onBackPressed、onKeyDown或onKeyUp方法中接收。
    • 对于Home按键消息的处理,既不能通过onKeyDown、onKeyUp接收到,android也没有提供专有的方法接收按键消息,个人估计home按键算是一个app异常信息处理的后门,比如ANR后,按其它按钮没有比按Home按键好使,所以android为了能够提供更好的用户体验,没有提供供用户监听home按键消息的方法。
        网上提供了各种各样监听Home按键消息的方法,比如说:重写onAttachedToWindow方法,监控Home按键。但是效果不太好
    @Override
    public void onAttachedToWindow() {
        this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
        super.onAttachedToWindow();
    }
    

    可以考虑这种方法:参考博客
      在每次点击Home按键时都会发出一个action为Intent.ACTION_CLOSE_SYSTEM_DIALOGS的广播,它是关闭系统Dialog的广播,我们可以通过注册它来监听Home按键消息,我自定义了一个home按键监听工具类,代码如下,使用说明参见类名上方的使用说明: import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter;

    home按键监听工具类:

    /**
     * Home按键监听类
     * 使用说明:
     * 1、初始化HomeListen
     * HomeListen homeListen = new HomeListen( this );
     * homeListen.setOnHomeBtnPressListener( new setOnHomeBtnPressListener(){
     *      @Override
     *      public void onHomeBtnPress( ){
     *          // 按下Home按键回调
     *      }
     *      
     *      @Override
     *      public void onHomeBtnLongPress( ){
     *          // 长按Home按键回调
     *      }
     * });
     * 
     * 2、在onResume方法中启动HomeListen广播:
     * homeListen.start();
     * 
     * 3、在onPause方法中停止HomeListen广播:
     * homeListen.stop( );
     * */
    public class HomeListen {
        public HomeListen(Context context) {
            mContext = context;
            mHomeBtnReceiver = new HomeBtnReceiver( );
            mHomeBtnIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        }
        
        public void setOnHomeBtnPressListener( OnHomeBtnPressLitener onHomeBtnPressListener ){
            mOnHomeBtnPressListener = onHomeBtnPressListener;
        }
        
        public void start( ){
            mContext.registerReceiver( mHomeBtnReceiver, mHomeBtnIntentFilter );
        }
        
        public void stop( ){
            mContext.unregisterReceiver( mHomeBtnReceiver );
        }
        
        class HomeBtnReceiver extends BroadcastReceiver{
            @Override
            public void onReceive(Context context, Intent intent) {
                receive( context, intent );
            }
        }
        
        private void receive(Context context, Intent intent){
            String action = intent.getAction();
            if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
                String reason = intent.getStringExtra( "reason" );
                if (reason != null) {
                    if( null != mOnHomeBtnPressListener ){
                        if( reason.equals( "homekey" ) ){
                            // 按Home按键
                            mOnHomeBtnPressListener.onHomeBtnPress( );
                        }else if( reason.equals( "recentapps" ) ){
                            // 长按Home按键
                            mOnHomeBtnPressListener.onHomeBtnLongPress( );
                        }
                    }
                }
            }
        }
        
        public interface OnHomeBtnPressLitener{
            public void onHomeBtnPress( );
            public void onHomeBtnLongPress( );
        }
        
        private Context mContext = null;
        private IntentFilter mHomeBtnIntentFilter = null;
        private OnHomeBtnPressLitener mOnHomeBtnPressListener = null;
        private HomeBtnReceiver mHomeBtnReceiver = null;
    }
    

    在Activity中做如下调用即可:

    public class HomeListenActivity extends Activity {
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_home_listen_layout);  
            initHomeListen( );
        }
        
        @Override
        protected void onResume( ) {
            super.onResume();
            mHomeListen.start( );
        }
      
        @Override  
        protected void onPause() {  
            super.onPause();
            mHomeListen.stop( );
        }
        
        private void initHomeListen( ){
            mHomeListen = new HomeListen( this );
            mHomeListen.setOnHomeBtnPressListener( new OnHomeBtnPressLitener( ) {
                @Override
                public void onHomeBtnPress() {
                    showToast( "按下Home按键!" );
                }
                
                @Override
                public void onHomeBtnLongPress() {
                    showToast( "长按Home按键!" );
                }
            });
        }
        
        private void showToast( String toastInfoStr ){
            Toast.makeText( this, toastInfoStr, Toast.LENGTH_LONG ).show( );
        }
    
        private HomeListen mHomeListen = null;
    }
    

    相关文章

      网友评论

        本文标题:# Android的按键消息分发机制

        本文链接:https://www.haomeiwen.com/subject/cvyodttx.html