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

开发环境:

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,点击查看

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



请关注我的新浪微博 width="100%" height="120" class="share_self" frameborder="0" scrolling="no" src="http://widget.weibo.com/weiboshow/index.php?language=&width=0&height=120&fansRow=2&ptype=1&speed=0&skin=1&isTitle=1&noborder=1&isWeibo=0&isFans=0&uid=2237047381&verifier=f873c807&dpc=1">

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,以下是使用 Vue 初始化一个简单的项目,页面上显示 "Hello World",并将其打包为一个安卓 App 的步骤: 1. 首先安装 Vue CLI。在终端执行以下命令: ``` npm install -g @vue/cli ``` 2. 创建一个新的 Vue 项目。在终端执行以下命令: ``` vue create my-app ``` 这将会根据 Vue CLI 提供的默认配置创建一个新的 Vue 项目。 3. 进入项目目录并启动开发服务器。在终端执行以下命令: ``` cd my-app npm run serve ``` 这将会启动开发服务器,并在浏览器打开一个页面。 4. 修改 HelloWorld 组件。在 `src/components/HelloWorld.vue` 文件,将 `template` 标签的内容修改为 "Hello World"。 ``` <template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script> export default { name: 'HelloWorld', data() { return { msg: 'Hello World' } } } </script> <style scoped> </style> ``` 5. 构建项目。在终端执行以下命令: ``` npm run build ``` 这将会使用 Vue CLI 提供的默认配置,构建出一个可以在浏览器运行的静态文件资源。 6. 安装 Cordova。在终端执行以下命令: ``` npm install -g cordova ``` 7. 创建 Cordova 项目。在终端进入任意目录并执行以下命令: ``` cordova create myApp com.example.myApp MyApp ``` 这将创建一个名为 `myApp` 的 Cordova 项目,并在 `com.example.myApp` 命名空间下创建一个名为 `MyApp` 的安卓应用程序。 8. 进入 Cordova 项目的目录,并将编译后的 Vue 项目的静态资源文件拷贝到 Cordova 项目的 `www` 目录下: ``` cd myApp cp -R /path/to/your/vue/project/dist/* www/ ``` 9. 添加安卓平台。在终端执行以下命令: ``` cordova platform add android ``` 10. 构建安卓应用程序。在终端执行以下命令: ``` cordova build android ``` 这将会根据你的 Cordova 项目配置和你的安卓 SDK,构建出一个安卓应用程序。构建完成后,可以在 `platforms/android/app/build/outputs/apk/debug/` 目录下找到 `app-debug.apk` 文件,这就是你的安卓应用程序。 以上就是使用 Vue 初始化一个简单的项目,页面上显示 "Hello World",并将其打包为一个安卓 App 的步骤。你可以根据自己的实际情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值