前段时间有个在android上面做网页的同事,对我说在JavaScript的里面的onKeyDown不能接收上下左右按键,当时我还觉得不好思议,这是网页的一个标准,android对接
webkit怎么可能改变原有的标准那,当时只是随口说说也没有怎么在意
结果前一段时间,客户写了一个网页包含onKeyDown处理的函数,结果在android平台上怎么也不能接收到事件,当时我突然想到以前有这么回事,看来真有这个问题阿
于是我在android的framework找到相关的webkit的代码,进行分析,在webview.java的onKeyDown函数中找到了这个问题的根源
现在我把部分的关键代码展示一下:
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)
{
switchOutDrawHistory();
if (nativePageShouldHandleShiftAndArrows()) {
letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
return true;
}
if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
pageUp(true);
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
pageDown(true);
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
nativeClearCursor(); // start next trackball movement from page edge
return pinScrollTo(0, mScrollY, true, 0);
case KeyEvent.KEYCODE_DPAD_RIGHT:
nativeClearCursor(); // start next trackball movement from page edge
return pinScrollTo(mContentWidth, mScrollY, true, 0);
}
}
if (mSelectingText) {
int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
-1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
int multiplier = event.getRepeatCount() + 1;
moveSelection(xRate * multiplier, yRate * multiplier);
return true;
}
if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
playSoundEffect(keyCodeToSoundsEffect(keyCode));
return true;
}
// Bubble up the key event as WebView doesn't handle it
return false;
}
代码的内容不做详细的分析了,我们看到这些代码中有很多return,这说明上下左右键自己做处理了没有交给底层WebCore去处理
真悲剧,怪不得WebCore老先生等白了头也拿不到这几个键
WebView.java 文件中的onKeyDown函数代码量很多最关键的交付给老大WebCore的是
mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
这个函数执行了,就把键值交给老大了。
尝试着把这段代码提前到靠前的位置发现页面是可以收到的,但是这么做还是有很多隐患,毕竟没有经过大总量测试
今天发现有一个好消息在android4.1.1 已经解决了这个问题
看来google 也已经认识到随便修改标准不是太好,最终还是服从标准
对于android4.1.1有关webkit的代码形式上做了大量的优化,看来google也是意识到开始的webkit对接到android平台上,做的有多麻烦,多累
在android4.1.1上的看webkit的java部分发现已经感觉很舒服了,有点在传统linux上做webkit的感觉了,起码能看到比较清晰的思路了
闲话少说,还是介绍些有点技术含量的东西
WebView.java怎么把事件传递给老大WebCore的
我们就从mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);这句话入手
看函数的调用是在WebViewCore.java的线程中
case KEY_DOWN:
key((KeyEvent) msg.obj, true);
break;
这个线程中收到,那我们抓紧看看key函数的处理
private void key(KeyEvent evt, boolean isDown) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
int keyCode = evt.getKeyCode();
int unicodeChar = evt.getUnicodeChar();
if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null
&& evt.getCharacters().length() > 0) {
// we should only receive individual complex characters
unicodeChar = evt.getCharacters().codePointAt(0);
}
if (!nativeKey(keyCode, unicodeChar, evt.getRepeatCount(), evt.isShiftPressed(),
evt.isAltPressed(), evt.isSymPressed(),
isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
}
if (mWebView != null && evt.isDown()) {
Message.obtain(mWebView.mPrivateHandler,
WebView.UNHANDLED_NAV_KEY, keyCode,
0).sendToTarget();
}
return;
}
// bubble up the event handling
// but do not bubble up the ENTER key, which would open the search
// bar without any text.
mCallbackProxy.onUnhandledKeyEvent(evt);
}
}
nativeKey函数是通过jni调用到底层的函数,是连接的关键
那接着这个关键函数走
顺便补充点常识,WebViewCore.java对应的底层函数WebViewCore.cpp
这个是注册过程
{ "nativeKey", "(IIIZZZZ)Z",
(void*) Key },
注册函数Key调用如下:
static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
jboolean isDown)
{
#ifdef ANDROID_INSTRUMENT
TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
#endif
return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
unichar, repeatCount, isDown, isShift, isAlt, isSym));
}
还不是很直接还是如要调用key函数,真是好事多磨阿
bool WebViewCore::key(const PlatformKeyboardEvent& event)
{
WebCore::EventHandler* eventHandler;
WebCore::Node* focusNode = currentFocus();
DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
if (focusNode) {
WebCore::Frame* frame = focusNode->document()->frame();
WebFrame* webFrame = WebFrame::getWebFrame(frame);
eventHandler = frame->eventHandler();
VisibleSelection old = frame->selection()->selection();
bool handled = eventHandler->keyEvent(event);
if (isContentEditable(focusNode)) {
// keyEvent will return true even if the contentEditable did not
// change its selection. In the case that it does not, we want to
// return false so that the key will be sent back to our navigation
// system.
handled |= frame->selection()->selection() != old;
}
return handled;
} else {
eventHandler = m_mainFrame->eventHandler();
}
return eventHandler->keyEvent(event);
}
找个半天的终于找到,看内部函数的实现都是用老大的作用域,在这里补充点知识,在通用webkit上面按键的处理和图形系统都基本绑定在一起
比如DirectFB,本身包含事件的接收,所谓的UI,图形和事件处理本来就不分家,事件的接收是从底层传递给老大WebCore在android的webkit
上面好像比较特殊,android系统上事件的处理流程稍微有点了解的都知道,事件的最终的来源也是来自于底层c++层,发给上面的app,
由于webkit也是android系统的一个应用,所以按键来源是从Java层传递而来,这只是形式不同而已,也没有必要为这些细节而牵肠挂肚。
上面key函数执行的前提就是按键已经进入老大地盘了,在老大地盘不假,老大不可能每件事情都要亲自过问,按键处理这件事就交给EventHandler
类好了,EventHandler只有科长权利,还是在page(局长)领衔下,不要小看page局长我们webkit里面的frame,page等经典类都在此
我们逐行分析下代码
WebCore::EventHandler* eventHandler;
WebCore::Node* focusNode = currentFocus();
这个很好理解EventHandler科长,先去挂个职称,我们知道事件的操作在网页上离不开FoucsNode,也就是要解决的问题
下面的代码就是从局长掌管下的frame,得到指示我们需要做的事情
eventHandler = frame->eventHandler();
if (focusNode) {
WebCore::Frame* frame = focusNode->document()->frame();
WebFrame* webFrame = WebFrame::getWebFrame(frame);
eventHandler = frame->eventHandler();
VisibleSelection old = frame->selection()->selection();
bool handled = eventHandler->keyEvent(event);
if (isContentEditable(focusNode)) {
// keyEvent will return true even if the contentEditable did not
// change its selection. In the case that it does not, we want to
// return false so that the key will be sent back to our navigation
// system.
handled |= frame->selection()->selection() != old;
}
return handled;
} else {
eventHandler = m_mainFrame->eventHandler();
EventHandler科长拿到上级指示,就可以行使自己权利处理按键了,心想终于到了自己的一亩三分了
return eventHandler->keyEvent(event);
这个时候已经完全进入EventHandler管辖区域了,下面就是涉及到真正老大WebCore内部原理问题
在讲述这些内部机制之前,我会首先对WebCore的内部事件的分类,分发做些大体的介绍,等介绍完这些基本知识后再接着讲解这个KeyEvent到底如何处理
今天先弄到这儿吧