解决Cordova多页面注册backbutton事件,一个页面响应过事件以后,其他页面不响应的问题

来源:互联网 发布:淘宝买电视没有合格证 编辑:程序博客网 时间:2024/05/19 23:56

开发环境:

Android Studio,Cordova5.3.3

问题描述:

页面1:

<html><script type="text/javascript" src="cordova.js"></script><script>document.addEventListener('deviceready', function(){document.addEventListener("backbutton", onBackKeyDown, false);},false);function onBackKeyDown(){//}</script></html>
页面2:

<html><script type="text/javascript" src="cordova.js"></script><script>document.addEventListener('deviceready', function(){document.addEventListener("backbutton", onBackKeyDown, false);},false);function onBackKeyDown(){//}</script></html>
当我在第一个界面点击返回键,程序正常响应,跳转到第二个页面时,再次点击返回键无效果,甚至连音量加减键都不能使用。然而第一个界面不触发返回事件,跳转第二个界面正常响应。网上也有朋友遇到类似的问题,但是均没有得到解决。


问题分析:

还是相信那句话,在源码面前没有秘密,所以我从源码着手查找问题原因及解决办法。

首先我从cordova.js进行分析,因为我们是通过这个文件注册backbutton事件的。cordova.js 1532行:

        var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';        // Inject a listener for the backbutton on the document.        var backButtonChannel = cordova.addDocumentEventHandler('backbutton');        backButtonChannel.onHasSubscribersChange = function() {            // If we just attached the first handler or detached the last handler,            // let native know we need to override the back button.            exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]);        };
可以看出4.x版本以上,cordova.js是采用CoreAndroid进行返回键注册的,调用的方法是overrideBackbutton方法。
下面看CoreAndroid.java,231行:

    public void overrideBackbutton(boolean override) {        LOG.i("App", "WARNING: Back Button Default Behavior will be overridden.  The backbutton event will be fired!");        webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);    }
可以看出,已将backbutton事件注册了,具体注册源码可自行查看。


下面看下返回事件是如何从native传到webview的。

public Boolean onDispatchKeyEvent(KeyEvent event) - CordovaWebViewImpl.java

private void sendJavascriptEvent(String event) - CordovaWebViewImpl.java

public void fireJavascriptEvent(String action) - CoreAndroid.java

private void sendEventMessage(String action) - CoreAndroid.java

public void sendPluginResult(PluginResult pluginResult) - CallbackContext.java

webView.sendPluginResult(pluginResult, callbackId);//最终就是调用这句代码向webview发送backbutton事件的。

当我跟踪调试代码的时候时,我发现第二次未响应的时候,上面的代码确实执行了,但callbackId还是之前的值。callbackId是每次cordova.js初始化的时候生成的,并且我还发现,每次切换界面的时候,CoreAndroid等所有插件都是重新初始化的,一会儿拿出证据来。下面看代码:

private void sendJavascriptEvent(String event) {    if (appPlugin == null) {        appPlugin = (CoreAndroid)pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME);    }    if (appPlugin == null) {        LOG.w(TAG, "Unable to fire event without existing plugin");        return;    }    appPlugin.fireJavascriptEvent(event);}

这里可以看出,只要调用过一次sendJavascriptEvent()方法后,appPlugin对象就已经存在了CordovaWebViewImpl对象里面了,那么上面的出现问题的原因也就解释清楚了。


解决办法:

在CordovaWebViewImpl.java的public void loadUrlIntoView(final String url, boolean recreatePlugins)中,

public void loadUrlIntoView(final String url, boolean recreatePlugins) {    LOG.d(TAG, ">>> loadUrl(" + url + ")");    if (url.equals("about:blank") || url.startsWith("javascript:")) {        engine.loadUrl(url, false);        return;    }        recreatePlugins = recreatePlugins || (loadedUrl == null);    if (recreatePlugins) {        // Don't re-initialize on first load.        if (loadedUrl != null) {            resetCoreAndroidPlugin();            pluginManager.init();        }        loadedUrl = url;    }
此处可以看出每次加载页面时,使用pluginManager.init();方法来初始化插件。

这里我是用我自己添加的resetCoreAndroidPlugin()方法,来重置CordovaWebViewImpl里面的appPlugin变量,resetCoreAndroidPlugin()方法实现如下

    public void resetCoreAndroidPlugin(){        appPlugin = null;    }


我在github上fork了cordova-android源码,已经pull request,点击查看

文笔不好,如有不明白之处可以共同探讨。才疏学浅,如有不对的地方,请指正!



请关注我的新浪微博

0 1
原创粉丝点击