# Android的按键消息分发机制

标签(空格分隔):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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容