天天看点

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到底如何处理

今天先弄到这儿吧

  第一时间获得更多原创文章,请关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或者搜索微信号coder_online即可关注,里面有大量Android,Chromium,Linux等相关文章等着您,我们还可以在线交流。

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

继续阅读