分类:android 202人阅读 评论(0) 收藏 举报 android4.0 按键消息响应流

1.开始肯定先说的是驱动这块,硬件是软件服务的,在Android这块C和java交互,有两种方式:

1.1:驱动--JNI--服务-事件分发-上层应用处理。

1.2:上层直接调用通过lib库的方式实现,中间使用回调机制,这种方式在Camera中有,下次再详解。

先来看一下驱动按键映射部分的详解如下:

映射实际是由KeyLayoutMap::map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置 文件 qwerty.kl决定键值的映射关系。你可以通过修改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。

  1. staticjboolean
  2. android_server_KeyInputQueue_readEvent(JNIEnv* env,jobject clazz,
  3. jobject event)
  4. {
  5. gLock.lock();
  6. sp hub = gHub;
  7. if(hub == NULL) {
  8. hub =newEventHub;
  9. gHub = hub;
  10. }
  11. gLock.unlock();
  12. int32_t deviceId;
  13. int32_t type;
  14. int32_t scancode, keycode;
  15. uint32_t flags;
  16. int32_t value;
  17. nsecs_t when;
  18. boolres = hub->getEvent(&deviceId,&type, &scancode,&keycode,
  19. &flags, &value,&when);
  20. env->SetIntField(event, gInputOffsets.mDeviceId,(jint)deviceId);
  21. env->SetIntField(event, gInputOffsets.mType,(jint)type);
  22. env->SetIntField(event, gInputOffsets.mScancode,(jint)scancode);
  23. env->SetIntField(event, gInputOffsets.mKeycode,(jint)keycode);
  24. env->SetIntField(event, gInputOffsets.mFlags,(jint)flags);
  25. env->SetIntField(event, gInputOffsets.mValue,value);
  26. env->SetLongField(event, gInputOffsets.mWhen,
  27. (jlong)(nanoseconds_to_milliseconds(when)));
  28. returnres;
  29. }

2.下面要讲重头代码WindowManagerService.java(frameworks\base\services\java\com\android\server),这个服务有承上启下的作用,读取用户输入的信息,是通过创建一个InputDeviceReader线程,KeyQ构建时,会启动一个线程去读取用户消息,具体代码在KeyInputQueue.mThread,在构造函数中,mThread会start,接下来,接研究一下mThread.run:
//用户输入事件消息读取线程
ThreadmThread = new Thread("InputDeviceReader") {
public void run() {
RawInputEvent ev = new RawInputEvent();
while (true) {//开始消息读取循环
try {
InputDevice di;
//本地方法实现,读取用户输入事件
readEvent(ev);
//根据ev事件进行相关处理
...
synchronized (mFirst) {//mFirst是keyQ队列头指针
...
addLocked(di, curTimeNano,ev.flags,RawInputEvent.CLASS_TOUCHSCREEN, me);
...
}
}
}
}

3.在读取完用户输入的信息,在WindowManagerService中就要分发消息,具体实现是交给了InputDispatcherThread这个线程处理,下来详解InputDispatcherThread处理流程:

-->WindowManagerService.main
--mInputThread = newInputDispatcherThread();//创建一个消息分发线程,读取并处理mQueue中消息

InputDispatcherThread.run
-->windowManagerService.process{
...
while (true){
// 从mQueue(KeyQ)获取一个用户输入事件,正上调用我上面提到的getEvent方法,若队列为空,线程阻塞挂起
QueuedEvent ev = mQueue.getEvent(
(int)((!configChanged && curTime< nextKeyTime)
? (nextKeyTime-curTime) : 0));
...
try {
if (ev != null) {
...
if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN){//touch事件
eventType = eventType((MotionEvent)ev.event);
} else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
ev.classType == RawInputEvent.CLASS_TRACKBALL) {//键盘输入事件
eventType = LocalPowerManager.BUTTON_EVENT;
} else {
eventType = LocalPowerManager.OTHER_EVENT;//其他事件
}
...
switch (ev.classType) {
case RawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event, 0, 0);//键盘输入,派发key事件
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
dispatchPointer(ev, (MotionEvent)ev.event, 0,0);//touch事件,派发touch事件
break;
case RawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev, (MotionEvent)ev.event, 0,0);//滚轮事件,派发Trackball事件
break;
case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
configChanged = true;
break;
default:
mQueue.recycleEvent(ev);//销毁事件
break;
}

}
} catch (Exception e) {
Slog.e(TAG,
"Input thread received uncaught exception: " + e, e);
}
}
}

4.KeyEvent事件的传递主要可以划分为三步:过滤器、View树、Activity.

  过滤器部分对应的是(frameworks/base/policy/base/phone/com/Android/internal/policy/impl/PhoneWindowManager.java)PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。它们的代码可以在中看到。

这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。

在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器,如果这个过滤器不吃掉的话,就会继续前进,进入View树,如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。

当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等


---------------------------------------

Android 4.0中按键的处理流程

分类:Android 4.0按键消息 2442人阅读 评论(0) 收藏 举报

按键在Android系统中,有着不同的代表意义。以前的全键盘的手机代码没有阅读过,所以也不是很了解。本人介绍的是在触摸屏的手机上的按键消息的处理流程。

在现在触摸屏成为主流的输入设备的情况下,很多厂商都在努力的做到取消物理按键的工作,但是目前就本人的学习情况来看,完全取消在目前看来还是不是那么现实。

有如下几点原因:

首先,本人说明的是目前原生的Android系统上。

其次,Android系统为了节省电量,在电源管理的过程中设置了休眠的方式。而休眠的时候触摸屏同样进入休眠状态。因此,不能够接收到用户的输入消息。

再次,目前的物理按键(主要指power,volume。home)是通过电源管理芯片进行控制的。触摸屏不是。

因此,如果没有现在的物理按键的情况下,如果想把设备从休眠的状态下唤醒基本上来讲是不可能的。

下面来正式的记录下本人在学习的过程中记录的点点滴滴。

首先,简要的介绍一下按键的处理流程。先简单的分为两大类:一类是虚拟按键。另一类是物理按键。

无论是虚拟按键还是物理按键都是要经过驱动层注册为输入设备,然后上报到kernel/drivers/input/input.c中。这里有相关函数的定义。然后通过、sys上报到frameworks/services/input/EventHub.cpp中,在这里会对设备进行扫描并且判断是哪种设备,然后在InputReader.cpp中对原始数据进行读取。在framewoks/services/input/InputDispatcher.cpp中实现数据的派发。在framework/base/core/jni/android_view_KeyEvent.cpp中实现通过JNI机制向上层的KeyEvent.java提供数据。并且在frameworks/base/core/java/android/view/KeyEvent.java中向上层的APP开发人员提供接口。

当然,虚拟键盘中有一个映射关系,键盘的按键值也会上报给上面的应用层,而对于物理按键往往是在frameworks层就被截取并且加以处理了。

普通的按键事件在Android系统中的调用流程(本人不太会处理visio绘图的保存问题)大致如下:

Android 4.0中按键的处理流程_第1张图片

下图是人家goole的图 是比俺画的好啊……

Android 4.0中按键的处理流程_第2张图片

但是对于物理按键的处理流程,目前主要查阅的代码的结果是在PhoneWindowManager.java中进行截获并处理的。


1.基本流程

1)内核处理按键,通过设备文件的方式提供给framework

2)framework层的KeyInputQueue.java启动线程从设备文件中读出键码,然后把读出的键码按kl文件转成相应键值(JNI调用EventHub.cpp),最后写入事件队列

ps:读取键盘具体应该是eventHub类处理

3)framework层的WindowManagerService.java启动线程从事件队列中读出键值,然后根据当前focus分发给相应窗口

ps:刚才是 读 键码,现在是键值。

4)UI通过KeyCharacterMap.java处理kcm规则将用户基本按键与功能键(Shift,Alt)组合,得出最终按键

2.两个配置文件

通常更换一种新的硬件,可能其键盘布局及键码与标准版本不同,不用更改代码,只要修改以下配置文件即可(如果增加新的未定义功能的按键,则需要修改代码)

1)xxx.kl

a)代码位置

sdk/emulator/keymaps/kl结尾文件(2.2版本模拟器使用)

b)功能

硬件全键盘的键码与键值的对应规则文件(如0x21对应A)

2)xxx.kcm

a)代码位置

sdk/emulator/keymaps/kcm结尾文件(2.2版本模拟器使用)

b)功能

硬件全键盘的键值对应表(如按下Alt,Shift时按键对应的键值)

PS:又提到kl..kcm前面来自驱动层,kcm这里有组合键,还有home..

3.整个流程相关代码

1)frameworks/base/core/java/android/view/KeyEvent.java(按键事件定义)

2)frameworks/base/services/java/com/android/server/KeyInputQueue.java(事件读取线程)

//PS:相当于getMessage,待定。

3)frameworks/base/services/java/com/android/server/WindowManagerService.java(事件分发线程)

//PS:相当于postMessage,sendMessage,待定。

4)frameworks/base/core/java/android/view/KeyCharacterMap.java(功能键转换kcm)

5 ) frameworks / base / libs / ui / EventHub . cpp ( 键码与键值转换 )

更多相关文章

  1. android利用线程池高效实现异步任务
  2. 一点见解: Android事件分发机制(二)
  3. Android开发知识(八):Android事件处理机制:事件分发、传递、拦截、处
  4. 【Android】Android主线程真的不让进行耗时操作吗?
  5. Android简明开发教程十九:线程 Bezier曲线
  6. Android 针对ListActivity中ListView 点击事件和长按事件
  7. Android 如何自定义一个简单的组件和自定义的点击事件(中级)
  8. Android中的任务,进程,线程以及服务

随机推荐

  1. Android eclipse 没有Annotation Process
  2. Android自带音乐播放器代码分析(1)
  3. Eclipse开发Android应用程序入门:重装上
  4. parse push 消息推送学习笔记(Android消息
  5. android摇一摇功能实现
  6. android:背景选择器-selector
  7. Android 中提高代码安全性-混淆代码
  8. Android NestedScrolling解决滑动冲突问
  9. android中使用gif
  10. Android的raw下视频文件的读写-日记