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

Android WebView离线缓存异常:无网切换Fragment无法加载缓存

解决WebView离线缓存与重复加载问题

首先,咱们来梳理下你遇到的核心问题:Fragment每次切换时onCreateView都会重新执行,导致WebView反复调用loadUrl();同时设置缓存模式后,无网络时依然无法加载缓存内容,报错net::ERR_INTERNET_DISCONNECTED或net::ERR_ADDRESS_UNREACHABLE。下面分步骤帮你解决:

1. 修复Fragment重复加载的问题

Fragment的onCreateView在每次Fragment可见/重建时都会执行,这会导致WebView被反复创建和加载。我们可以通过两种方式优化:

方式一:复用WebView实例

将WebView作为Fragment的成员变量,在onCreateView中判断是否已经初始化过,避免重复创建:

private WebView webView;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    if (webView == null) {
        // 第一次创建时初始化WebView
        webView = new WebView(getContext());
        // 这里进行WebView的配置
        setupWebViewSettings();
    }
    // 如果WebView已有父容器,先移除避免重复添加
    if (webView.getParent() != null) {
        ((ViewGroup) webView.getParent()).removeView(webView);
    }
    return webView;
}

方式二:设置ViewPager预加载(如果用ViewPager管理Fragment)

如果你的Fragment是用ViewPager承载的,设置setOffscreenPageLimit来保留Fragment实例,避免频繁重建:

viewPager.setOffscreenPageLimit(2); // 根据你的Fragment数量调整数值

2. 动态配置缓存模式(核心修复)

固定设置LOAD_CACHE_ELSE_NETWORK或LOAD_DEFAULT无法满足"有网加载在线,无网用缓存"的需求,我们需要根据网络状态动态切换缓存模式:

第一步:添加网络权限并实现状态判断

确保已经在AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

然后在Fragment中添加网络判断方法:

private boolean isNetworkAvailable() {
    ConnectivityManager cm = (ConnectivityManager) requireContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    if (cm == null) return false;
    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    return activeNetwork != null && activeNetwork.isConnected();
}

第二步:动态设置缓存策略

在setupWebViewSettings()方法中,根据网络状态调整缓存模式:

private void setupWebViewSettings() {
    WebSettings settings = webView.getSettings();
    settings.setAppCacheEnabled(true);
    settings.setDomStorageEnabled(true);
    settings.setAllowFileAccess(true);
    settings.setJavaScriptEnabled(true);
    settings.setAppCachePath(requireContext().getCacheDir().getPath());
    settings.setAppCacheMaxSize(1024 * 1024 * 50); // 设置50MB缓存上限
    settings.setLoadWithOverviewMode(true);
    settings.setUseWideViewPort(true);

    // 动态切换缓存模式
    if (isNetworkAvailable()) {
        // 有网络时:优先加载在线内容,同时缓存新内容
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
    } else {
        // 无网络时:只加载缓存内容,没有缓存才报错
        settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
    }

    webView.setWebViewClient(new WebViewClient() {
        // 可选:如果目标页面缓存控制严格,可在这里自定义缓存拦截逻辑
        @Nullable
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            return super.shouldInterceptRequest(view, request);
        }
    });
}

第三步:优化加载逻辑

避免每次onCreateView都调用loadUrl(),可以判断WebView是否已经加载过目标URL:

private static final String TARGET_URL = "https://calendar.google.com/calendar/htmlembed?src=wlmacci%40gmail.com&amp;ctz=America%2FToronto";

// 在onCreateView中
if (webView.getUrl() == null || !webView.getUrl().equals(TARGET_URL)) {
    webView.loadUrl(TARGET_URL);
}

3. 排查缓存无效的潜在原因

如果上述配置后依然无法加载缓存,可能是以下原因:

  • 目标页面的缓存控制头限制:比如Google Calendar这类页面可能设置了Cache-Control: no-cache或no-store,导致WebView无法缓存内容。建议先测试一个允许缓存的简单页面(比如你注释里的https://sites.google.com/view/wlmac/textfile?)验证缓存是否生效。
  • 缓存路径权限问题:确保getCacheDir()的路径是可读写的,WebView有足够权限写入缓存。

修改后的完整代码

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class CalendarFragment extends Fragment {
    private WebView webView;
    private static final String TARGET_URL = "https://calendar.google.com/calendar/htmlembed?src=wlmacci%40gmail.com&amp;ctz=America%2FToronto";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (webView == null) {
            webView = new WebView(requireContext());
            setupWebViewSettings();
        }
        // 移除父容器,避免重复添加
        if (webView.getParent() != null) {
            ((ViewGroup) webView.getParent()).removeView(webView);
        }
        // 仅在未加载过目标URL时执行加载
        if (webView.getUrl() == null || !webView.getUrl().equals(TARGET_URL)) {
            webView.loadUrl(TARGET_URL);
        }
        return webView;
    }

    private void setupWebViewSettings() {
        WebSettings settings = webView.getSettings();
        settings.setAppCacheEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setAllowFileAccess(true);
        settings.setJavaScriptEnabled(true);
        settings.setAppCachePath(requireContext().getCacheDir().getPath());
        settings.setAppCacheMaxSize(1024 * 1024 * 50); // 50MB缓存
        settings.setLoadWithOverviewMode(true);
        settings.setUseWideViewPort(true);

        // 动态切换缓存模式
        if (isNetworkAvailable()) {
            settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        } else {
            settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }

        webView.setWebViewClient(new WebViewClient() {
            @Nullable
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                // 可选:如果目标页面缓存控制严格,可在这里自定义缓存逻辑
                return super.shouldInterceptRequest(view, request);
            }
        });
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager cm = (ConnectivityManager) requireContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return false;
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnected();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:31:00