前面一篇文章我对消息分发机制做了一个开头,如果大家还没有阅读,可以先去阅读一下:
Android中按键消息分发机制1。上一篇在结尾说明了如果WMS没有处理掉该消息,那么这个消息是会传到客户端窗口的,那么客户端窗口时在哪里接受这个消息的呢?我先告诉你是在MessageQueue中的next方法获取的,我们先停在这里回忆一下吧。
我在前一篇的开始对Android2.2中消息分发机制做了简要说明,在2.2中,这些消息都是在java层处理的,并将获得的按键消息放在MessageQueue中,但是2.3以后这些都是在native层处理的,难道还是放在MessageQueue中的?当然不是,所以导致从2.3以后,MessageQueue中的next函数发生了改变,你们可以找来2.2和2.3以后的代码做比较,一个很明显的变化就是在2.3的代码中会调用一个nativePollOnce函数,我们来好好分析一下这个MessageQueue吧,首先看构造函数:
MessageQueue() {
nativeInit();
}
构造函数非常简单,就是调用了nativeInit()函数,进入该函数瞧瞧:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (! nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
这段代码主要是创建了一个NativeMessageQueue对象,并将它保存在了MessageQueue中的mptr变量中,这是jni技术中用的非常多的技术。
然后进入到MessageQueue的next函数看看:
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (i
这里较之前版本的代码,最大的改变就是多了一个nativePollOnce函数,我们进入这个函数看看:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
主要就是调用了nativeMessageQueue的pollOnce函数,进入看看:
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
这个里面就出现了一个非常重要的变量:mLooper,这个mLooper和我们之前在java中的Looper不是一回事,它通过pollOnce从管道中读取消息,如果没有读取到就会发生阻塞。如果读取到了消息就会返回,然后经过辗转调用,就会调用到ViewRoot中的mInputHandler中的handKey函数(过程比较复杂,在注册客户端inputChannel的使用传入了一个InputHandler接口,当读取到消息后,就回调其中的方法)。
写到这里我先总结一下MessageQueue中的next方法:
UI线程自启动之后,就会通过Looper(java层的)不断从MessageQueue中获取消息,首先是获取native层的用户消息,如果没有获取到,就阻塞,如果获取到,就会调用ViewRoot中的mInputHandler中的handKey方法,此时你可能有疑问,如果在本地层阻塞了,那么当我们使用handler往MessageQueue中放消息,岂不是执行不了,放心,如果你往MessageQueue中存放消息,此时会唤醒该线程的,不信你可以看看enqueueMessage这个方法的最下面:
if (needWake) {
nativeWake(mPtr);
}
也就是说会将线程从native层唤醒。唤醒之后就会从MessageQueue中取出消息并调用Handler的handlerMessage函数。
下面我们继续分析一下InputHandler中的handKey是怎么处理消息的:贴出源代码:
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, Runnable finishedCallback) {
startInputEvent(finishedCallback);
dispatchKey(event, true);
}
在该函数中调用的是ViewRoot中的dispatchKey函数,进过辗转的调用,最终会调用到mView.dispatchKeyEvent。(中间过程很简单,如果你有比明白的地方,可以留言)。这个mView是什么,如果你不明白可以我阅读我的另外一篇文章《Android中窗口的创建过程》。对于应用程序窗口这个mView就是PhoneWindow.DecorView,对于非应用程序窗口则是一个ViewGroup的一个实现,那我们就以应用程序窗口为例吧。
进入DecorView的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) {
…
final Callback cb = getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
首先调用getCallback函数,这个函数返回的就是Activity,不明白的可以阅读我的另外一篇文章《Android的窗口创建过程》,那么cb肯定不会是空的,所以就会调用cb.dispatchKeyEvent,如果Activity处理了这个消息,那么直接返回true,如果没有处理,那么会调用PhoneWindw的onkeyDown和onKeyUp函数。
进入Activity中查看它的dispatchKeyEvent的处理流程吧
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
首先调用PhoneWindow的superDispatchKeyEvent,这个函数的实现如下:
public boolean superDispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
所以最终是调用的ViewGroup的dispatchKeyEvent,进入ViewGroup看看。
public boolean dispatchKeyEvent(KeyEvent event) {
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
return super.dispatchKeyEvent(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
return mFocused.dispatchKeyEvent(event);
}
return false;
}
其中如果ViewGroup本身有焦点,那么直接调用父类的dispatchKeyEvent,不然就是调用孩子中的有焦点的那个View的dispatchKeyEvent。我们先回退一下吧,退到Activity中的dispatchKeyEvent中,也就是说如果ViewGroup处理了这个消息,那么就直接返回true,不然调用event.dispatch函数。我们进入到这个函数看看吧
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);
if (state != null)
{
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0)
{
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);
} else if (isLongPress() && state.isTracking(this))
{
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
if (state != null) {
state.handleUpEvent(this);
}
return receiver.onKeyUp(mKeyCode, this);
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this)) {
return true;
}
if (code != KeyEvent.KEYCODE_UNKNOWN) {
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);
if (handled) {
mAction = ACTION_UP;
receiver.onKeyUp(code, this);
}
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
}
return false;
}
return false;
}
在这个方法中就是调用了receiver的onKeyDown,onKeyUp等方法,此时这里的receiver就是Activity的。我们继续回退,到DecorView中的dispatchKeyEvent中。可以看到如果Activity也没有处理这个消息,那么最终就是调用PhoneWinow中的onKeyUp和onKeyDown函数。
我们继续分析一下View中的dispatchKeyEvent这个过程就算完了
public boolean dispatchKeyEvent(KeyEvent event) {
// If any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
if (android.util.Config.LOGV) {
captureViewInfo("captureViewKeyEvent", this);
}
if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
return event.dispatch(this, mAttachInfo != null
? mAttachInfo.mKeyDispatchState : null, this);
}
在该函数中会先检查mOnKeyListener是否为空,如果不为空,就会调用mOnKeyListener中的onKey方法,如果onKey方法处理了这个消息,直接返回true,如果没有处理,则调用event.dispatch方法,我们上面已经分析了,这个方法里面就是调用receiver的onKeyDown和onKeyUp等方法。
分析过程差不多完了,可能过程有点凌乱,没办法,这个过程实在有点复杂,如果大家有哪里看不懂,可以给我留言。