最近在状态栏上面添加了一些系统常用的按键,用的比较多的,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();
}