Android WebView注入JS按钮报错:downloadListener未定义
问题根源
- JS作用域限制:动态绑定的事件回调中,无法直接访问全局暴露的
downloadListener对象,需显式通过window引用。 - 内部类访问问题:局部非静态内部类可能存在权限或生命周期问题,改为静态内部类更稳妥。
- JS执行时机不当:直接通过
post执行JS可能在页面DOM未完全加载时运行,导致找不到目标video元素,同时接口暴露的时序也可能存在问题。
修复方案
1. 重构DownloadListener为静态内部类
避免非静态内部类持有外部类引用引发内存泄漏,同时确保@JavascriptInterface注解的方法能被WebView正确识别。
2. 在JS中通过window访问Java接口
修改事件回调代码,用window.downloadListener替代直接调用downloadListener,确保能访问到全局暴露的Java对象。
3. 延迟JS执行到页面加载完成后
将JS代码的执行逻辑放在WebViewClient的onPageFinished方法中,保证DOM已加载完成,能正确定位到目标video元素。
修复后的完整代码
private void setup(WebView webView, String videoUrl) { webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); // 改为静态内部类,避免内存泄漏并确保接口可访问 static class DownloadListener { @JavascriptInterface public void onDownloadButtonClick(String buttonId, String videoSrc) { Log.d("DownloadListener", "Button with id " + buttonId + " was clicked for video with src " + videoSrc); // 在这里处理下载逻辑 } } // 提前将Java接口暴露到JS环境 webView.addJavascriptInterface(new DownloadListener(), "downloadListener"); // 设置WebViewClient,确保页面加载完成后执行JS webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 构造注入的JS代码 String jsCode = "function addDownloadOverlayToVideo(videoSrc) { " + " var video = document.querySelector('video[src=\"' + videoSrc + '\"]'); " + " if (!video) { " + " console.log('未找到对应src的video元素: ' + videoSrc); " + " return; " + " } " + " var parent = video.parentNode; " + " parent.style.width = '100%'; " + " var overlay = parent.querySelector('.download-overlay'); " + " if (!overlay) { " + " overlay = document.createElement('div'); " + " overlay.className = 'download-overlay'; " + " overlay.style.position = 'absolute'; " + " overlay.style.display = 'inline-block'; " + " overlay.style.top = '5px'; " + " overlay.style.right = '5px'; " + " overlay.style.margin = '5px'; " + " overlay.style.zIndex = '9999'; " + " var button = document.createElement('button'); " + " button.innerHTML = 'Download'; " + " var id = 'download-button-' + Date.now(); // 动态生成ID避免重复 " + " button.id = id; " + " button.style.backgroundColor = '#FF0000'; " + " button.style.padding = '10px 20px'; " + " button.style.borderRadius = '5px'; " + " button.style.color = '#FFFFFF'; " + " button.style.fontSize = '18px'; " + " button.addEventListener('mousedown', function() { " + " this.style.transform = 'translateY(2px)'; " + " this.style.backgroundColor = '#000000'; " + " // 显式通过window访问Java接口 " + " window.downloadListener.onDownloadButtonClick(this.id, video.src); " + " }); " + " overlay.appendChild(button); " + " parent.style.position = 'relative'; " + " parent.appendChild(overlay); " + " } " + "} " + "addDownloadOverlayToVideo('" + videoUrl + "');"; // 在UI线程执行JS代码 webView.post(() -> webView.evaluateJavascript(jsCode, null)); } }); }
额外注意事项
- Android版本兼容:Android 4.2及以上版本,只有添加
@JavascriptInterface注解的Java方法才能被JS访问,否则会被安全机制拦截。 - 元素重复问题:原代码中按钮ID固定,若页面存在多个视频会导致ID冲突,改为动态生成ID可避免该问题。
- 错误容错:添加video元素不存在的判断,防止JS报错中断后续逻辑执行。
内容的提问来源于stack exchange,提问作者TPX
相关产品推荐
相关产品推荐

