手机音量键的监听原理

本篇涉及cordova/platform模块和CoreAdnroid.java插件的讲解,通过该模块和插件实现volumeup,volumedown,backbtn,searchbtn的监听。webview通过监听原生按键事件在将事件动作传递给js执行js注册的方法达到监听目的。

步骤1:webview注册原生按键监听

-----------------------------------------以下代码见SystemWebView.java------------------------------------------

...此处省略部分代码...


    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        //CordovaWebViewImpl.java对具体的事件动作分发
        Boolean ret = parentEngine.client.onDispatchKeyEvent(event);
        if (ret != null) {
            return ret.booleanValue();
        }
        return super.dispatchKeyEvent(event);
    }
...此处省略部分代码...

------------------------------------------SystemWebView.java-------------------------------------------------
------------------------------------------CordovaWebViewImpl-------------------------------------------------

          @Override
        public Boolean onDispatchKeyEvent(KeyEvent event) {
            int keyCode = event.getKeyCode();
            boolean isBackButton = keyCode == KeyEvent.KEYCODE_BACK;
            //按键摁下
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                if (isBackButton && mCustomView != null) {
                    return true;
                } else if (boundKeyCodes.contains(keyCode)) {
                    return true;
                } else if (isBackButton) {
                    return engine.canGoBack();
                }
            //按键松开
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                if (isBackButton && mCustomView != null) {
                    hideCustomView();
                    return true;
                //boundKeyCodes为HashSet对象,当js注册了volumeup等监听后,会将对应keycode存在该变量内
                } else if (boundKeyCodes.contains(keyCode)) {
                    String eventName = null;
                    switch (keyCode) {
                        case KeyEvent.KEYCODE_VOLUME_DOWN:
                            eventName = "volumedownbutton";
                            break;
                        case KeyEvent.KEYCODE_VOLUME_UP:
                            eventName = "volumeupbutton";
                            break;
                        case KeyEvent.KEYCODE_SEARCH:
                            eventName = "searchbutton";
                            break;
                        case KeyEvent.KEYCODE_MENU:
                            eventName = "menubutton";
                            break;
                        case KeyEvent.KEYCODE_BACK:
                            eventName = "backbutton";
                            break;
                    }
                    if (eventName != null) {
                        sendJavascriptEvent(eventName);//发送事件给js
                        return true;
                    }
                } else if (isBackButton) {
                    return engine.goBack();
                }
            }
            return null;
        }
------------------------------------------CordovaWebViewImpl.java---------------------------------------

--------------------------------------------------CoreAndroid.java--------------------------------


...此处省略部分代码...

    /**
       该方法作用是将对应button比如volumeup,volumedown灯添加到boundKeyCodes(CordovaWebViewImpl.java)当中
     */
    public void overrideButton(String button, boolean override) {
        LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden.  The volume event will be fired!");
        if (button.equals("volumeup")) {
            webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
        }
        else if (button.equals("volumedown")) {
            webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
        }
        else if (button.equals("menubutton")) {
            webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_MENU, override);
        }
    }


...此处省略部分代码...


    /**
     * Executes the request and returns PluginResult.
     *
     * @param action            The action to execute.
     * @param args              JSONArry of arguments for the plugin.
     * @param callbackContext   The callback context from which we were invoked.
     * @return                  A PluginResult object with a status and message.
     */
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        PluginResult.Status status = PluginResult.Status.OK;
        String result = "";
        try {
            if (action.equals("clearCache")) {
                this.clearCache();
            }
           ...此处省略部分代码...
  //js在执行exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1])
//该动作时候将由该方法接收动作,对应button比如volumeup,volumedown等将添加到boundKeyCodes(CordovaWebViewImpl.java)当中
            else if (action.equals("overrideButton")) {
                this.overrideButton(args.getString(0), args.getBoolean(1));
            }
            else if (action.equals("overrideBackbutton")) {
                this.overrideBackbutton(args.getBoolean(0));
            }
            else if (action.equals("exitApp")) {
                this.exitApp();
            }
    else if (action.equals("messageChannel")) {
                synchronized(messageChannelLock) {
                    messageChannel = callbackContext;
                    if (pendingResume != null) {
                        sendEventMessage(pendingResume);
                        pendingResume = null;
                    }
                }
                return true;
            }

            callbackContext.sendPluginResult(new PluginResult(status, result));
            return true;
        } catch (JSONException e) {
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
            return false;
        }
    }

    ...此处省略部分代码...

--------------------------------------------------CoreAndroid.java----------------------------------

步骤2:js注册volumeup等事件监听

document.addEventListener("volumeupbutton", listener, false);

当我们给volumeup绑定时候实际上触发了platform模块的以下方法


        function bindButtonChannel(buttonName) {
            //创建volumeupbutton隧道
            var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
            //给volumeupbutton绑定监听器时候触发了该法方法
            volumeButtonChannel.onHasSubscribersChange = function() {
                //该方法通知native将buttonName(volumeup,volumedown)变量值加入到native变量boundKeyCodes对象当中
                //之后native监听按键的时候总会先判断当前的按键是否在于该对象当中,有才会继续做处理。
                //APP_PLUGIN_NAME = 'CoreAndroid'即CoreAndroid.java插件
                exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]);
            };
        }

        bindButtonChannel('volumeup');
        bindButtonChannel('volumedown');

步骤3:建立接收按键事件的回调通知

在platform模块中可以找到如下代码:

        //cordova就绪后触发了两个以下回调
        channel.onCordovaReady.subscribe(function() {
            //参数:成功回调方法,失败回调方法,插件名,执行动作,参数
            //该方法实质作用是注册一个回调上下文即CallbackContext,并保存于CoreAndroid.java对象的messageChannel变量中,
            //之后原生触发的按键动作都将同过该回调上下文messageChannel来反馈给js
            exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
            //通知native显示界面
            exec(null, null, APP_PLUGIN_NAME, "show", []);
        });



function onMessageFromNative(msg) {
    var cordova = require('cordova');
    var action = msg.action;

    switch (action)
    {
        // Button events
        case 'backbutton':
        case 'menubutton':
        case 'searchbutton':
        // App life cycle events
        case 'pause':
        // Volume events
        case 'volumedownbutton':
        case 'volumeupbutton':
            cordova.fireDocumentEvent(action);//触发注册的volumeupbutton等监听器
            break;
        case 'resume':
            if(arguments.length > 1 && msg.pendingResult) {
                if(arguments.length === 2) {
                    msg.pendingResult.result = arguments[1];
                } else {
                    // The plugin returned a multipart message
                    var res = [];
                    for(var i = 1; i < arguments.length; i++) {
                        res.push(arguments[i]);
                    }
                    msg.pendingResult.result = res;
                }

                // Save the plugin result so that it can be delivered to the js
                // even if they miss the initial firing of the event
                lastResumeEvent = msg;
            }
            cordova.fireDocumentEvent(action, msg);
            break;
        default:
            throw new Error('Unknown event action ' + action);
    }
}

results matching ""

    No results matching ""