Android 11+如何通过存储访问框架(SAF)在WebView中加载HTML文件?
Android 11+ 用SAF在WebView加载带本地链接的HTML文件
问题背景
之前用file://路径加载本地HTML,Android 11及以上必须改用存储访问框架(SAF)。已经拿到文件URI并成功加载了index.html,但里面的本地链接在shouldOverrideUrlLoading回调里返回的URL格式错误:
- 错误URL:
content://com.android.externalstorage.documents/tree/9C33-6BBD%3AGames/document/www.abcgames.sk/wsdindex.html@p=navody_zobraz&id=4004.html - 真实文件路径:
/storage/9C33-6BBD/Games/Walkthroughs/doom/www.abcgames.sk/wsdindex.html@p=navody_zobraz&id=4004.html
解决方案
1. 解析index.html的SAF URI,获取所在目录真实路径
加载index.html时,先通过其SAF URI解析出父目录的真实路径,后续用来拼接子链接:
private String getIndexParentPath(Context context, Uri indexUri) { String realPath = null; if (DocumentsContract.isDocumentUri(context, indexUri)) { String docId = DocumentsContract.getDocumentId(indexUri); if ("com.android.externalstorage.documents".equals(indexUri.getAuthority())) { String[] split = docId.split(":"); String volumeId = split[0]; if ("primary".equalsIgnoreCase(volumeId)) { realPath = Environment.getExternalStorageDirectory() + "/" + split[1]; } else { // 处理SD卡存储卷 StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); StorageVolume volume = storageManager.getStorageVolume(Uri.parse("content://com.android.externalstorage.documents/tree/" + volumeId)); if (volume != null) { File volumeDir = volume.getDirectory(); realPath = volumeDir.getAbsolutePath() + "/" + split[1]; } } // 截断文件名,保留父目录路径 if (realPath != null) { realPath = realPath.substring(0, realPath.lastIndexOf("/")); } } } return realPath; }
调用时机:拿到index.html的URI后,调用该方法获取父目录路径,存入全局变量(比如private String indexParentPath;)。
2. 修正shouldOverrideUrlLoading的URL处理逻辑
在回调里识别错误格式的URL,提取相对路径后拼接成真实路径,再转成正确的SAF URI加载:
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 判断是不是WebView拼接错误的SAF链接 if (url.startsWith("content://com.android.externalstorage.documents/tree/") && url.contains("/document/www.")) { // 提取链接里的相对路径部分 String relativePath = url.substring(url.indexOf("/document/") + 10); // 拼接真实文件路径 String realFilePath = indexParentPath + "/" + relativePath; // 转成正确的SAF URI Uri correctUri = buildSafUriFromPath(realFilePath); if (correctUri != null) { view.loadUrl(correctUri.toString()); return true; } } // 其他正常链接直接加载 view.loadUrl(url); return true; } // 从真实路径构建SAF URI private Uri buildSafUriFromPath(String realPath) { File targetFile = new File(realPath); if (!targetFile.exists()) return null; // 提取存储卷ID(比如9C33-6BBD) String volumeId = realPath.split("/")[2]; // 提取卷内相对路径 String volumeRelativePath = realPath.substring(("/storage/" + volumeId).length() + 1); // 构建标准SAF URI return Uri.parse( "content://com.android.externalstorage.documents/tree/" + volumeId + "/document/" + volumeId + "%2F" + Uri.encode(volumeRelativePath) ); }
3. 配置WebView允许本地文件访问
初始化WebView时,开启必要设置确保能加载本地文件:
WebSettings webSettings = myWebView.getSettings(); webSettings.setAllowFileAccess(true); webSettings.setAllowFileAccessFromFileURLs(true); webSettings.setAllowUniversalAccessFromFileURLs(true);
核心原因
WebView加载SAF URI时,会用SAF的URI结构拼接相对路径,但SAF URI和真实文件系统路径结构不匹配,导致拼接出错误链接。必须手动解析真实目录,重新构建正确的访问路径/URI。
内容的提问来源于stack exchange,提问作者user1063364
相关产品推荐
相关产品推荐

