本篇只会讲到触发ViewGroup的dispatchTouchEvent为止,因为接下去的一搜一大把,或者说有点基础的应该都了解。
-
从触摸开始
首先,请你打开命令行工具。输入adb shell
进入到shell命令,然后输入getevent
,会监听打印触摸屏幕的event信息。
add device 1: /dev/input/event5
name: "msm8974-taiko-mtp-snd-card Headset Jack"
add device 2: /dev/input/event4
name: "msm8974-taiko-mtp-snd-card Button Jack"
add device 3: /dev/input/event3
name: "hs_detect"
add device 4: /dev/input/event1
name: "touch_dev"
add device 5: /dev/input/event0
name: "qpnp_pon"
add device 6: /dev/input/event2
name: "gpio-keys"
当你使出你的一阳指点击屏幕的时候,变回不断的去获取到你的点击事件,就像这样:
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0039 000005cf
/dev/input/event1: 0003 0035 0000020b
/dev/input/event1: 0003 0036 0000068d
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0036 0000068c
/dev/input/event1: 0003 0030 00000005
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0039 ffffffff
/dev/input/event1: 0000 0000 00000000
这些操作全都是Linux Kernel去做的,只要你点击了屏幕了,硬件设备变回产生硬件终端,Kernel收到硬件终端之后,会对其进行加工,包装成event事件之后添加到/dev/input/目录下,就像如上所示的event1。
[](http://www.cnblogs.com/tnxk/archive/2012/10/26/2741326.html)
* ####Android系统的监听
Android会不断的去监控/dev/input/目录下的所有的设备节点,一旦发现有新的设备节点可读时就会立马读出事件并进行处理。
* ######WMS
而这里的复杂步骤涉及到frameWork层,我们就从WMS开始吧,
先是有SystemServer启动的WMS。SystemServer.java的startOtherServices()
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
并且同样在这个方法中初始化了InputManagerService,掌管输入事件的服务。
inputManager = new InputManagerService(context);
我们看到`WindowManagerService`的main方法传入的就是这个inputManager。
在InputManagerService的构造方法中,用到了
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
native的方法,nativce层不是重点,我这边就快速的将过去了。
在`frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp`中(没有在本地编译过源码的同学可以去 http://androidxref.com/ 查看,基于当前最新的7.1.1)
static jlong nativeInit(JNIEnv* env, jclass /* clazz /,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager
messageQueue->getLooper());
im->incStrong(0);
eturn reinterpret_cast
}
然后看内部类nativeInputManger
NativeInputManager::NativeInputManager(jobject contextObj,
...
sp
mInputManager = new InputManager(eventHub, this, this);
}
我们看到创建了一个EventHub类,并且将其交给InputManger并生成一个InputManger对象。
/frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
const sp
const sp
const sp
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
一个分发对象,一个reader对象,并且调用initialize方法
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
创建读线程和分发线程
至此,所有的初始化先都ok了,在SystemServer.java,创建了InputManagerService之后没几行就调用了 `inputManager.start();`,
public void start() {
...
nativeStart(mPtr);
...
}
又看到了native。。。来吧继续相当枯燥的native,我要快进了,我有点写的想吐。。
`frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp`
static void nativeStart(JNIEnv* env, jclass /* clazz /, jlong ptr) {
NativeInputManager im = reinterpret_cast
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
`/frameworks/native/services/inputflinger/InputManager.cpp
`
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
启动了读线程和分发线程
`/frameworks/native/services/inputflinger/InputReader.cpp`
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
void InputReader::loopOnce() {
...
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
...
}
不断的loop去通过EventHub去getEvents(越来越偏了,getEvents不继续往下了,知道这个深度已经对于非framework工程师来说已经够了)
在getEvents方法中去从dev/input/目录下读取设备节点并加工,并返回给InputReader进行处理。
之后的处理过程以及一系列跳转也是相当复杂,由于本文的初衷并非详解最底层的东西,
故而此处一并略过直接到底层将event回传给java层的最末
* ######InputEventReceiver
在此我们只需要知道由InputChannel构建起了UI进程和底层system_server进程的socket通道。
最终会从NativeInputEventReceiver.cpp处调起InputEventReceiver的方法
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
InputEventReceiver是个抽象类,我们在ViewRootImpl中定义了如下
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
那么我们就看看enqueueInputEvent到底做了些什么操作:
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
...
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
...
deliverInputEvent(q);
}
...
}
private void deliverInputEvent(QueuedInputEvent q) {
...
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
最终就在这个deliver方法中,而这个stage是个InputStage对象,这个类内部是链表结构,最终会将q分发到可以处理的窗口ViewPostImeInputStage,由它的processPointerEvent方法来处理
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
final View eventTarget =
(event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
mCapturingView : mView;
...
boolean handled = eventTarget.dispatchPointerEvent(event);
...
return handled ? FINISH_HANDLED : FORWARD;
}
调用的view的dispatchPointerEvent方法:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
而我们知道,window的最底层的View就是DecorView,那么这个时候调用的应该就是DecorView的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
还记的callBack是谁么,在Activity的attach方法中,`mWindow.setCallback(this);
`
这个callback就是activity本身,所以我们要去Activity中查看它的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
这里分了两步,先去getWindow().superDispatchTouchEvent(ev),这一步会从PhoneWindow->DecorView->ViewGroup(DecorView继承FrameLayout就是ViewGroup),最后实际上是触发了ViewGroup的dispatchTouchEvent方法,也就是activity会先将事件交给DecorView去处理,如果被消耗掉,就返回true。如果没有消耗这个事件,就回调Activity自己的onTouchEvent。
* ######总结
那么把上面的一大坨我们简略的来讲如下的流程:
* 用户触摸屏幕产生设备节点中断并保存到/dev/input/目录下
* 底层的EventHub监听目录,将事件读出并加工返回给随着WMS一起启动的底层的InputReader
* InputReader处理加工之后交给InputDispatcher来进行分发,通过socket通知UI进程的InputEventReceiver接收到事件
* InputEventReceiver将回调事件一步步传递给Activity来进行分发
* Activity先将事件交给DecorView来进行处理,如果DecorView消耗则返回true,否则自己回调onTouchEvent方法
以上!