您的位置:首页 > 移动开发 > Android开发

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

2012-10-12 19:09 701 查看
前段时间有个在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_N***_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_N***_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等相关文章等着您,我们还可以在线交流。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: