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&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&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
相关产品推荐
相关产品推荐

