当前位置: 代码迷 >> Web前端 >> android webkit JavaScript 不能处理onkeydown的下上左右键,引发的话题
  详细解决方案

android webkit JavaScript 不能处理onkeydown的下上左右键,引发的话题

热度:824   发布时间:2012-10-19 16:53:35.0
android webkit JavaScript 不能处理onkeydown的上下左右键,引发的话题
前段时间有个在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到底如何处理
今天先弄到这儿吧


  相关解决方案