android 事件传递机制

在系统启动过程中,会加载驱动程序,初始化硬件设备,会进入bool EventHub::openPlatformInput(void)这个函数,该函数主要功能是扫描/dev/input该目录,获取输入设备。如何获取呢?通过linux API res = scan_dir(device_path); 该函数叫 while((de = readdir(dir))) { strcpy(filename, de->d_name); open_device(devname); } 不断读取目录文件,然后通过open_device()打开设备。具体打开设备函数是fd = open(deviceName, O_RDWR);以读写方式打开,该函数会调用驱动里file_operations里的实现函数。 此时所有输入设备已经打开。 在WindowManagerService服务类运行的时候,在构造函数中会创建内部类KeyQ对象. 该类继承之KeyInputQueue类。当然要进入该类的构造函数。在KeyInputQueue类的构造函数中会启动Thread mThread=new Thread("InputDeviceReader")这个匿名内部类线程,有该线程读驱动事件并把它放到消息队列中。具体实现是: Thread mThread = new Thread("InputDeviceReader") { public void run() { RawInputEvent ev = new RawInputEvent(); while (true) { InputDevice di; readEvent(ev); } } 该线程持续运行,循环通过readEvent(); 该函数是个native方法,具体实现为 static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event) { gLock.lock(); sp hub = gHub; if (hub == NULL) { hub = new EventHub; gHub = hub; } gLock.unlock(); int32_t deviceId; int32_t type; int32_t scancode, keycode; uint32_t flags; int32_t value; nsecs_t when; bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when); } 以上步骤获得了事件QueuedEvent。 获得事件后通过 private void addLocked(InputDevice device, long when, int flags, int classType, Object event) { boolean poke = mFirst.next == mLast; QueuedEvent ev = obtainLocked(device, when, flags, classType, event); QueuedEvent p = mLast.prev; while (p != mFirst && ev.when < p.when) { p = p.prev; } ev.next = p.next; ev.prev = p; p.next = ev; ev.next.prev = ev; ev.inQueue = true; } 该函数加到消息队列中,该消息队列就是个双向连表,头和尾分别是 final QueuedEvent mFirst; final QueuedEvent mLast; 该函数就是把新消息插到尾的前面。 消息队列有了。还需要读队列。在WindowManagerService.java 类中同时又开了个内部线程 mInputThread = new InputDispatcherThread(); mInputThread.start(); 该线程和刚才的写队列线程是并行的。该线程起来后,通过 public void run() { while (true) { try { process(); } catch (Exception e) { Log.e(TAG, "Exception in input dispatcher", e); } } } 这个process()函数来读消息。具体实现是 private void process() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); while (true) { QueuedEvent ev = mQueue.getEvent( (int)((!configChanged && curTime < nextKeyTime) ? (nextKeyTime-curTime) : 0)); } 该getEvent()函数的具体实现是 QueuedEvent getEvent(long timeoutMS) { long begin = SystemClock.uptimeMillis(); final long end = begin+timeoutMS; long now = begin; synchronized (mFirst) { while (mFirst.next == mLast && end > now) { QueuedEvent p = mFirst.next; mFirst.next = p.next; mFirst.next.prev = mFirst; p.inQueue = false; return p; } } 持续的读消息,如果没消息就阻塞,有消息,就读取消息,所谓读取消息就得到引用,然后把该消息从双向连表中删除。得到消息后根据消息输入设备类型把消息发送到具体AP 中。如 switch (ev.classType) { case RawInputEvent.CLASS_KEYBOARD: dispatchKey((KeyEvent)ev.event, 0, 0); mQueue.recycleEvent(ev); break; case RawInputEvent.CLASS_TOUCHSCREEN: dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_TRACKBALL: dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_CONFIGURATION_CHANGED: configChanged = true; break; default: mQueue.recycleEvent(ev); break; } 当然这其中涉及很多细节,有兴趣可以看看。同时读写队列的互斥机制也值得学习。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值