首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Android >

Android键盘系统浅析(下)

2012-08-24 
Android键盘系统浅析(上)Android中的键盘系统的生命周期从系统的启动开始到系统的关闭一直存在着,因为,在

Android键盘系统浅析(上)

Android中的键盘系统的生命周期从系统的启动开始到系统的关闭一直存在着,因为,在这整个过程中我们希望按下的没一个按键都会有相应的事件发生。通过调研发现,Android键盘系统贯穿着Android框架的最底层——Linux内核(驱动)到上层JAVA架构层——SystemServer。
与android键盘系统关系紧密的几个文件从下层往上层依次为:Goldfish_event.c(处于Linux内核)、EventHub.cpp(硬件抽象层)、com_android_server_KeyInputQueue.cpp(JNI本地方法)KeyInputQueue.java、WindowManagerService.java(java框架层)。发现一个很有意思的问题,Android并没有做一个纯的键盘系统,而是将键盘系统的实现放在了事件处理模块之中。这从这几个关键文件的文件名我们不难看出。

从上往下的调用过程分析如下:

在WindowManagerService.java中启动了窗口管理,其中有一个语句如下:
mQueue = new KeyQ();
追踪到 KeyQ 可知:
private class KeyQ extends KeyInputQueue
(注:在下面的描述中,父类是指KeyInputQueue,子类是指KeyQ)
继续追踪KeyInputQueue
我们会发现,这是一个相当庞大的类,在这个类中创建了一个线程:
Thread mThread = new Thread("InputDeviceReader")

由构造方法:
? ? public Thread(String threadName) {
? ?? ???if (threadName == null) {
? ?? ?? ?? ?throw new NullPointerException();
? ?? ???}
? ?? ???create(null, null, threadName, 0);
}

我们可以知道,Thread mThread = new Thread("InputDeviceReader")创建了一个名为InputDeviceReader的线程。
但这个线程是怎么创建起来的呢?因为我们看到,线程对象是在父类中new的。从父类的源码中,我们可以看到有一个构造方法,在这个构造方法中,??调用了mThred对象的方法,即:mThread.start();
我们来看看这个构造方法的代码:
KeyInputQueue(Context context, HapticFeedbackCallback??hapticFeedbackCallback) {
? ?? ???if (MEASURE_LATENCY) {
? ?? ?? ?? ?lt = new LatencyTimer(100, 1000);
? ?? ???}

? ?? ???BAD_TOUCH_HACK = context.getResources().getBoolean(
? ?? ?? ?? ?? ? com.android.internal.R.bool.config_filterTouchEvents);
? ?? ???
? ?? ???mHapticFeedbackCallback = hapticFeedbackCallback;
? ?? ???
? ?? ???readExcludedDevices();
? ?? ???
? ?? ???PowerManager pm = (PowerManager)context.getSystemService(
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???Context.POWER_SERVICE);
? ?? ???mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???"KeyInputQueue");
? ?? ???mWakeLock.setReferenceCounted(false);

? ?? ???mFirst = new QueuedEvent();
? ?? ???mLast = new QueuedEvent();
? ?? ???mFirst.next = mLast;
? ?? ???mLast.prev = mFirst;

? ?? ???mThread.start();
? ? }


在子类KeyQ的构造方法中,调用了父类的这个构造方法:? ?
private class KeyQ extends KeyInputQueue
? ?? ?? ?? ?implements KeyInputQueue.FilterCallback {
? ?? ???PowerManager.WakeLock mHoldingScreen;
//下面是子类KeyQ的构造方法
KeyQ() {
? ?? ?? ?? ?super(mContext, WindowManagerService.this);
? ?? ?? ?? ?PowerManager pm =
(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
? ?? ?? ?? ?mHoldingScreen =
pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
? ?? ?? ?? ?? ?? ???"KEEP_SCREEN_ON_FLAG");
? ?? ?? ?? ?mHoldingScreen.setReferenceCounted(false);
? ?? ???}
自然的,就创建了一个父类中的线程mThread。因此,在new一个子类对象的时候:
mQueue = new KeyQ();就调用了父类中的这个线程。
? ? ? ?







在这个线程中,调用了本地方法readEvent;
Thread mThread = new Thread("InputDeviceReader") {
? ?? ???public void run() {
? ? ? ? ? ? ? ? ? ? ? ? …………………..

? ? ? ? ? ? ? ? ? ? ? ?
RawInputEvent ev = new RawInputEvent();
? ?? ?? ?? ?while (true) {
? ?? ?? ?? ?? ? try {
? ?? ?? ?? ?? ?? ???InputDevice di;
? ?? ?? ???? ? ? ? ? ? ? ? ? ? ? ? //这是一个本地方法
? ?? ?? ?? ?? ?? ???readEvent(ev);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? …………………..

? ? ? ? ? ? ? ? }
? ? ? ? }
因些我们追踪到该JNI调用:com_android_server_KeyInputQueue.cpp
在该Native方法readEvent中,调用了EeventHub.cpp。
…………………..
sp<EventHub> hub = gHub;? ? ? ? ? ? ? ? //sp是一个类,功能是smartpointer
…………………..
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when);
…………………..

因此我们进入EeventHub.cpp中查看方法:getEvevt

bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
? ?? ???int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
? ?? ???int32_t* outValue, nsecs_t* outWhen)
接下来,getEvevt中调用了openPlatformInput(),openPlatformInput()中调用了scan_dir(),scan_dir()中调用了open_device。
我们注意到,函数的调用以及进入到EeventHub.cpp,这属于硬件抽象层,它可以直接对Linux内核驱动进行调用。


我们再回到线程mThread上来,在
mQueue = new KeyQ();
之后,接下来调用了
send = preprocessEvent(di, ev);
这里,实际上是调用了mQueue中的 preprocessEvent。
这是因为,子类KeyQ在继承KeyInputQueue时,重写了方法:preprocessEvent
@Override
? ?? ???boolean preprocessEvent(InputDevice device, RawInputEvent event)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? …………………..

? ? ? ? ? ? ? ? }
接下来:与Key有关的代码有:
// Is it a key event?
? ?? ?? ?if (type == RawInputEvent.EV_KEY &&
? ?? ?? ?? ?? ???(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
? ?? ?? ?? ?? ???(scancode < RawInputEvent.BTN_FIRST ||
? ?? ?? ?? ?? ?? ?? ?? ? scancode > RawInputEvent.BTN_LAST)) {
? ?? ?? ?? ? boolean down;
? ?? ?? ?? ? if (ev.value != 0) {
? ?? ?? ?? ?? ???down = true;
? ?? ?? ?? ?? ???di.mKeyDownTime = curTime;
? ?? ?? ?? ? } else {
? ?? ?? ?? ?? ???down = false;
? ?? ?? ?? ? }
? ?? ?? ?? ? int keycode = rotateKeyCodeLocked(ev.keycode);
? ?? ?? ?? ? addLocked(di, curTimeNano, ev.flags,
? ?? ?? ?? ?? ?? ?? ?RawInputEvent.CLASS_KEYBOARD,
? ?? ?? ?? ?? ?? ?? ?newKeyEvent(di, di.mKeyDownTime, curTime, down,
? ?? ?? ?? ?? ?? ?? ?? ?? ???keycode, 0, scancode,
? ?? ?? ?? ?? ?? ?? ?? ?? ???((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? KeyEvent.FLAG_WOKE_HERE : 0));? ?? ?? ?? ?? ?? ?? ?? ?? ?
? ?? ?? ?? ???}

这mThread之后创建了另外一个线程InputDispatcherThread
…………………..
? ?? ???mQueue = new KeyQ();

? ?? ???mInputThread = new InputDispatcherThread();

? ?? ???PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
? ?? ???thr.start();
…………………..

在InputDispatcherThread中,有关于键盘的一个处理:
实际上这个线程是从??KeyQ 的事件队列中读取按键事件。
…………………..
switch (ev.classType) {
? ?? ?? ?? ?? ?? ?? ?? ?? ? case RawInputEvent.CLASS_KEYBOARD:
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???KeyEvent ke = (KeyEvent)ev.event;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (ke.isDown()) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?lastKey = ke;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?downTime = curTime;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?keyRepeatCount = 0;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?lastKeyTime = curTime;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?nextKeyTime = lastKeyTime
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???+ ViewConfiguration.getLongPressTimeout();
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?if (DEBUG_INPUT) Log.v(
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? TAG, "Received key down: first repeat @ "
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? + nextKeyTime);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???} else {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?lastKey = null;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?downTime = 0;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?// Arbitrary long timeout.
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?lastKeyTime = curTime;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?nextKeyTime = curTime + LONG_WAIT;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?if (DEBUG_INPUT) Log.v(
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? TAG, "Received key up: ignore repeat @ "
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? + nextKeyTime);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???dispatchKey((KeyEvent)ev.event, 0, 0);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???mQueue.recycleEvent(ev);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???break;

热点排行