咱们先来写个测试应用,主要文件以下:java
MainActivity.javaandroid
package com.test.keyevent; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { Log.d("KEYEVENT", "MainActivity:onKeyDown"); return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { Log.d("KEYEVENT", "MainActivity:onKeyUp"); return super.onKeyUp(keyCode, event); } @Override public void onUserInteraction() { Log.d("KEYEVENT", "MainActivity:onUserInteraction"); super.onUserInteraction(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { Log.d("KEYEVENT", "MainActivity:dispatchKeyEvent"); return super.dispatchKeyEvent(event); } }
2. MyFrameLayout.javashell
package com.test.keyevent; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.widget.FrameLayout; public class MyFrameLayout extends FrameLayout { public MyFrameLayout(Context context) { super(context, null); } public MyFrameLayout(Context context, AttributeSet attrs) { super(context, attrs, 0); } public MyFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchKeyEvent(KeyEvent event) { Log.d("KEYEVENT", "MyFrameLayout:dispatchKeyEvent"); return super.dispatchKeyEvent(event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub Log.d("KEYEVENT", "MyFrameLayout:onKeyDown"); return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { // TODO Auto-generated method stub Log.d("KEYEVENT", "MyFrameLayout:onKeyUp"); return super.onKeyUp(keyCode, event); } }
3. MyEditText.javaapp
package com.test.keyevent; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.widget.EditText; public class MyEditText extends EditText { public MyEditText(Context context) { super(context, null); // TODO Auto-generated constructor stub } public MyEditText(Context context, AttributeSet attrs) { super(context, attrs, 0); // TODO Auto-generated constructor stub } public MyEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { Log.d("KEYEVENT", "MyEditText:onKeyDown"); return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { Log.d("KEYEVENT", "MyEditText:onKeyUp"); return super.onKeyUp(keyCode, event); } @Override public boolean dispatchKeyEvent(KeyEvent event) { Log.d("KEYEVENT", "MyEditText:dispatchKeyEvent"); return super.dispatchKeyEvent(event); } }
4. activity_main.xmlide
<com.test.keyevent.MyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <com.test.keyevent.MyEditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@android:color/white" android:textColor="@android:color/black" android:hint="请输入..." /> </com.test.keyevent.MyFrameLayout>
安装并打开应用。函数
在adb shell中,输入input keyevent 29 往系统注入一个KeyEvent(29是a的键值,能够在系统源码KeyEvent.java中查到),咱们获得以下日志:oop
03-02 20:18:48.991: D/KEYEVENT(5840): MainActivity:dispatchKeyEvent性能
03-02 20:18:48.991: D/KEYEVENT(5840): MainActivity:onUserInteraction测试
03-02 20:18:48.991: D/KEYEVENT(5840): MyFrameLayout:dispatchKeyEvent优化
03-02 20:18:48.991: D/KEYEVENT(5840): MyEditText:dispatchKeyEvent
03-02 20:18:48.992: D/KEYEVENT(5840): MyEditText:onKeyDown
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:onUserInteraction
03-02 20:18:49.040: D/KEYEVENT(5840): MyFrameLayout:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MyEditText:dispatchKeyEvent
03-02 20:18:49.040: D/KEYEVENT(5840): MyEditText:onKeyUp
03-02 20:18:49.040: D/KEYEVENT(5840): MainActivity:onKeyUp
下面咱们一步一步分析。
首先被调用的是MainActivity的dispatchKeyEvent的函数。KeyEvent是如何传递到Acitivity中的,这是比较复杂的,能够参看http://blog.csdn.net/luoshengyang/article/details/6882903这篇博文。但要注意,这篇文章是更具android2.3来写的,对于咱们常见的4.2以上的系统,通过我分析比较,是有一些不一样的。
4.2系统中,native层InputManager不是在windowManagerService中建立的,而是在SystemServer中建立了个InputManagerService,并在里面建立初始化了InputManager。InputManager的启动也是由SystemServer来启动的。而且InputReaderThread的loopOnce()支持一次读取多条event,优化了性能。还有不少其余的不一样的地方,可是大致逻辑上仍是同样的。
这块好应用开发关系不太大,就不细说了。最终,Activity的ViewRootImpl的deliverKeyEventPostIme方法中会调用 DecorView的dispatchKeyEvent方法,而DecorView的dispatchKeyEvent方法,能够看下源码,它回调了Activity的 dispatchKeyEvent方法,因而,咱们看到了这行日志。
咱们看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); }
里面调用了onUserInteraction方法。咱们能够覆写这个方法,这样就能够在KeyEvent被派发以前,作一些操做,默认是什么都不作。
从日志看出,事件从Activity又回到了咱们的View中。为何要从Activity中走一下,这样给Activity一个机会,能够控制KeyEvent的派发。
如何回去的,咱们接着看Acitivity.dispatchKeyEvent方法
Window win = getWindow(); if (win.superDispatchKeyEvent(event)) { return true; }
win就是Activity的mWindow对象,是一个PhoneWindow。咱们看PhoneWindow的superDispatchKeyEvent(KeyEvent event)方法:
public boolean superDispatchKeyEvent(KeyEvent event) { return mDecor.superDispatchKeyEvent(event); }
mDecor就是咱们熟悉的DecorView对象啦,看它的superDispatchKeyEvent:
public boolean superDispatchKeyEvent(KeyEvent event) { if (super.dispatchKeyEvent(event)) { return true; } ...... }
由于DecorView是FrameLayout子类,FrameLayout是ViewGroup的子类,因此super.dispatchKeyEvent(event)会调用到ViewGroup的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onKeyEvent(event, 1); } if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { // 若是本身有焦点,则调用父类View的dispatchKeyEvent if (super.dispatchKeyEvent(event)) { return true; } } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) == PFLAG_HAS_BOUNDS) { // 子视图有焦点,则调用子视图 if (mFocused.dispatchKeyEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } return false; }
焦点在MyEditText上,而MyEditText包含于MyFrameLayout中,因此最终mFocused.dispatchKeyEvent(event)会调用到MyFrameLayout.dispatchKeyEvent方法。
MyFrameLayout也是FrameLayout子类,因此MyFrameLayout.dispatchKeyEvent和上面同样,会调用ViewGroup的dispatchKeyEvent。此次mFocused就是MyEditText,调用它的dispatchKeyEvent方法。
MyEditText是View子类,会调用View的dispatchKeyEvent方法:
public boolean dispatchKeyEvent(KeyEvent event) { ...... if (event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this)) { return true; } ...... return false; }
View.dispatchKeyEvent会调用KeyEvent的dispatch方法:
public final boolean dispatch(Callback receiver, DispatcherState state, Object target) { switch (mAction) { case ACTION_DOWN: { ...... boolean res = receiver.onKeyDown(mKeyCode, this); ...... } }
receiver就是以前的View,也就是MyEditText,也就回调了MyEditText的onKeyDown方法。
至此,ACTION_DOWN的KeyEvent分析完毕。
ACTION_UP的KeyEvent和Down的基本同样,只是应为View的onKeyUp方法返回了false,因此最终会调用到Acitivity的onKeyUp方法,因此才会有最后一条日志的输出。