Android WebView点击DataTables导出按钮崩溃问题求助
解决Android WebView中DataTables导出按钮崩溃的问题
你遇到的崩溃原因很明确:DataTables导出功能生成的是blob:协议的URL,而Android系统默认没有应用能处理这种URI的ACTION_VIEW请求,所以直接抛出了ActivityNotFoundException。我们可以通过拦截Blob请求并手动处理文件下载来解决这个问题,具体步骤如下:
核心思路
Blob URL是WebView内部的内存资源,系统应用无法直接访问。我们需要:
- 拦截WebView中的Blob请求
- 通过JavaScript将Blob内容转换为Base64格式传递给Android端
- 在Android端将Base64数据保存为本地文件
- 使用FileProvider提供文件URI,让系统应用可以安全打开
具体实现步骤
1. 添加JavaScript接口类
这个类负责接收从Web端传递过来的Base64数据,并处理文件保存和打开:
public class WebAppInterface { private final Context mContext; public WebAppInterface(Context context) { mContext = context; } @JavascriptInterface public void downloadBlob(String fileName, String base64Data) { try { // 解码Base64数据 byte[] fileData = Base64.decode(base64Data, Base64.DEFAULT); // 创建存储目录(使用应用私有外部存储,无需动态权限) File downloadDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); if (!downloadDir.exists()) { downloadDir.mkdirs(); } // 创建本地文件 File outputFile = new File(downloadDir, fileName); FileOutputStream fos = new FileOutputStream(outputFile); fos.write(fileData); fos.flush(); fos.close(); // 打开文件(使用FileProvider避免权限问题) Uri fileUri = FileProvider.getUriForFile( mContext, mContext.getPackageName() + ".fileprovider", outputFile ); Intent openIntent = new Intent(Intent.ACTION_VIEW); openIntent.setDataAndType(fileUri, getMimeType(outputFile.getAbsolutePath())); openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); mContext.startActivity(openIntent); } catch (Exception e) { e.printStackTrace(); Toast.makeText(mContext, "导出失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } } // 获取文件对应的MIME类型 private String getMimeType(String filePath) { String fileExtension = MimeTypeMap.getFileExtensionFromUrl(filePath); return MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension); } }
2. 配置WebView并拦截Blob请求
修改你的WebView初始化代码,启用JavaScript并添加接口,同时拦截Blob URL:
// 启用JavaScript支持 myWebview.getSettings().setJavaScriptEnabled(true); // 添加自定义JavaScript接口 myWebview.addJavascriptInterface(new WebAppInterface(this), "AndroidDownloader"); // 设置WebViewClient拦截Blob请求 myWebview.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { String url = request.getUrl().toString(); // 检测是否为Blob URL if (url.startsWith("blob:")) { // 执行JavaScript获取Blob内容并传递给Android String jsCode = String.format( "(function() {" + " var xhr = new XMLHttpRequest();" + " xhr.open('GET', '%s', true);" + " xhr.responseType = 'blob';" + " xhr.onload = function() {" + " if (this.status === 200) {" + " var blob = this.response;" + " var reader = new FileReader();" + " reader.onloadend = function() {" + " var base64Data = reader.result.split(',')[1];" + " // 从响应头提取文件名" + " var disposition = '%s';" + " var fileNameMatch = disposition.match(/filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/);" + " var fileName = fileNameMatch ? fileNameMatch[1].replace(/['\"]/g, '') : 'exported_file';" + " AndroidDownloader.downloadBlob(fileName, base64Data);" + " };" + " reader.readAsDataURL(blob);" + " }" + " };" + " xhr.send();" + "})()", url, request.getResponseHeaders().getOrDefault("Content-Disposition", "") ); view.evaluateJavascript(jsCode, null); return true; // 标记请求已处理 } return super.shouldOverrideUrlLoading(view, request); } });
3. 配置FileProvider(Android 7.0+必需)
在AndroidManifest.xml中添加FileProvider配置:
<application> <!-- 其他已有配置 --> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
然后在res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="downloads" path="Downloads/" /> </paths>
4. 添加必要权限
在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.INTERNET" /> <!-- Android 10+使用应用私有存储,无需WRITE_EXTERNAL_STORAGE权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
为什么这个方案有效?
- 绕过了系统无法处理Blob URI的问题,直接从WebView内部获取了文件内容
- 使用FileProvider确保了文件访问的安全性,符合Android 7.0+的文件权限规范
- 自动从响应头提取文件名,保持了DataTables导出的原有体验
内容的提问来源于stack exchange,提问作者DEEP SECRETS
相关产品推荐
相关产品推荐

