最近在状态栏上面添加了一些系统常用的按键,用的比较多的,Home, Menu, Back三个按键,
分别模拟物理按键按下时的消息,
参考下面两篇文章,可以实现在状态栏上添加自定义的按键,
1.
在android的状态栏(statusbar)中增加menu,home和back快捷键的方法
http://blogold.chinaunix.net/u2/78893/showart_2349971.html
2.
在android的状态栏(statusbar)中增加menu,home和back快捷键的方法,及按键延迟原因分析
http://hi.baidu.com/tigerpan/blog/item/63caae81b57580b16c811937.html
但是在状态栏上面点击按钮的时候,却经常出现死锁,假死的情况,在第二篇文章的时候,提到,
我们先看一下执行流程
在com.android.server.WindowManagerService.injectKeyEvent(KeyEvent, boolean)函数中我们可以看到实际上是调用dispatchKey(newEvent, pid, uid)函数,这个dispatchKey函数中:
需要先找到当前的焦点focusObj(focusObj:WindowState 就是在维护窗口ViewRoot与WindowManagerService之前的关联,这二者的通信都在WindowState中可以找到);再用focusObj把KeyEvent传递给当前焦点窗口。但在传递之前会先调用mKeyWaiter.waitForNextEventTarget()函数等待当前焦点处理完上一次事件(这包括KeyEvent,MotionEvent...等),如何判断是否处理完上一次事件, 用if (mFinished && !mDisplayFrozen) if (targetWin != null)这两个if语句判断,而最关键就是这个mFinished ,这里还得查看ViewRoot,在ViewRoot中分发事件后会调用sWindowSession.finishKey(mWindow);告诉WindowManagerService,在doFinishedKeyLocked()函数中将mFinished = true,如果mFinished不为true,则会wait(curTimeout),这就是出现停顿的直接原因
通过打印Log显示是我们的KeyEvent.Action_Down事件一直不能mFinished,这就要跟踪ViewRoot,发现每次在模拟的KeyEvent.Action_Down事件的Message根本不能被处理,原因是StatusBarView的TouchEvent没有处理完成,并且与模拟的KeyEvent.Action_Down事件在MessageQueue的next函数中卡住了而ViewRoot本身就是一个Handler,大量的Message从此经过,而StatusBar的ViewRoot既要处理Touch事件也要处理模拟按键,就出现了这种竞态死锁的现象。
延迟发送模拟的KeyEvent是我目前采用的解决方式,上面的例子中sendKey函数已经体现
大概就是按键点击的时候(我自己个人理解),如果在onClick事件里面立刻直接发送KeyEvent的话,会导致onClick的消息处理无法正常返回,从而导致后面的消息也无法被获取处理,这是产生假死停顿的原因,所以参考上面的方法,新创建一个线程,延时发送KeyEvent(我直接用文2的方法问题也没有得到很好的解决).
private void sendVKeyDelay(int key) { final int keyCode = key; Thread sendKeyDelay = new Thread(){ public void run() { try { Thread.sleep(100); long now = SystemClock.uptimeMillis(); KeyEvent keyDown = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0); IWindowManager wm = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); wm.injectKeyEvent(keyDown, false); KeyEvent keyUp = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0); wm.injectKeyEvent(keyUp, false); } catch (InterruptedException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }; sendKeyDelay.start(); }