You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何开发通过多窗口(window.open())下载多文件的Android WebView应用?

嘿,我懂你想要实现的功能——让Android WebView支持JavaScript的window.open()打开多个窗口,以此触发多文件下载对吧?我来帮你把这个需求落地,看看你的代码需要做哪些关键调整和补充:

实现Android WebView支持window.open()多窗口与多文件下载

1. 先说说你现有代码的核心不足

你的代码只做了基础的WebView初始化,但默认情况下Android WebView有几个限制会阻碍你的需求:

  • 不允许JavaScript主动打开新窗口
  • 就算开启了新窗口支持,也需要我们自定义窗口的创建逻辑
  • WebView不会自动处理文件下载请求,必须手动拦截并处理

2. 完整实现代码与步骤

第一步:完善MainActivity的WebView配置

下面是补全后的代码,包含了多窗口支持、下载处理和权限申请的完整逻辑:

package aviad.com.test;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.webkit.DownloadListener;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_STORAGE_PERMISSION = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); // 记得先创建对应的布局文件

        WebView mainWebView = findViewById(R.id.webview);
        WebSettings webSettings = mainWebView.getSettings();

        // 开启核心JS支持
        webSettings.setJavaScriptEnabled(true);
        // 允许JS自动打开新窗口
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        // 开启多窗口支持
        webSettings.setSupportMultipleWindows(true);
        // 可选:设置缓存模式,根据你的需求调整
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);

        // 处理JS调用window.open()的新窗口请求
        mainWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, android.os.Message resultMsg) {
                // 为新窗口创建单独的WebView实例
                WebView newWindowWebView = new WebView(MainActivity.this);
                // 给新WebView配置和主WebView一致的JS支持
                WebSettings newWebSettings = newWindowWebView.getSettings();
                newWebSettings.setJavaScriptEnabled(true);
                newWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
                newWebSettings.setSupportMultipleWindows(true);

                // 给新WebView设置下载监听器,拦截下载请求
                newWindowWebView.setDownloadListener(new DownloadListener() {
                    @Override
                    public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                        startFileDownload(url, userAgent, contentDisposition, mimetype);
                    }
                });

                // 将新WebView关联到窗口消息,让系统识别这个新窗口
                WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
                transport.setWebView(newWindowWebView);
                resultMsg.sendToTarget();
                return true;
            }
        });

        // 为主WebView设置下载监听器
        mainWebView.setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                startFileDownload(url, userAgent, contentDisposition, mimetype);
            }
        });

        // 加载你的目标网页(可以是本地HTML或网络URL)
        mainWebView.loadUrl("file:///android_asset/download_page.html");

        // 检查并申请存储权限(Android 6.0+必须)
        checkStoragePermission();
    }

    // 用系统DownloadManager处理下载,这是最稳定的方式
    private void startFileDownload(String url, String userAgent, String contentDisposition, String mimetype) {
        android.app.DownloadManager.Request downloadRequest = new android.app.DownloadManager.Request(android.net.Uri.parse(url));
        downloadRequest.setMimeType(mimetype);
        // 自动提取文件名
        String fileName = android.webkit.URLUtil.guessFileName(url, contentDisposition, mimetype);
        downloadRequest.setTitle(fileName);
        downloadRequest.setDescription("文件下载中...");
        // 设置下载到公共下载目录,方便用户找到
        downloadRequest.setDestinationInExternalPublicDir(android.os.Environment.DIRECTORY_DOWNLOADS, fileName);
        // 通知栏显示下载进度和完成提示
        downloadRequest.setNotificationVisibility(android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

        // 发起下载请求
        android.app.DownloadManager downloadManager = (android.app.DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        if (downloadManager != null) {
            downloadManager.enqueue(downloadRequest);
        }
    }

    // 动态申请存储权限
    private void checkStoragePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION);
            }
        }
    }

    // 处理权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_STORAGE_PERMISSION) {
            if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "需要存储权限才能完成文件下载", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

第二步:创建对应的布局文件(activity_main.xml)

在res/layout目录下创建布局文件,添加WebView组件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

第三步:在AndroidManifest.xml添加权限

记得在清单文件中声明必要的权限,否则下载和网络访问会失败:

<uses-permission android:name="android.permission.INTERNET" /> <!-- 加载网络网页需要 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- Android 10+需要添加这个属性,兼容旧的存储访问逻辑 -->
<application
    ...
    android:requestLegacyExternalStorage="true">
    ...
</application>

3. 关键逻辑说明

  • WebChromeClient的onCreateWindow方法:这是让window.open()生效的核心,必须在这里创建新的WebView并关联到系统窗口消息,否则JS的新窗口请求会被忽略。
  • DownloadListener:不管是主WebView还是新创建的窗口WebView,都需要设置这个监听器来拦截下载请求,然后交给系统的DownloadManager处理,它比自己实现下载逻辑更稳定,还能自动处理通知栏、断点续传等功能。
  • 权限处理:Android 6.0及以上必须动态申请存储权限,否则无法将文件保存到本地。

这样调整后,你的WebView就能完美支持JS通过window.open()打开多个窗口,每个窗口的文件URL都会被拦截并触发下载,实现多文件下载的功能。

内容的提问来源于stack exchange,提问作者Aviad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:51:03