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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:20:23