WebView处理网页返回的错误码信息

在 Android 开发中,WebView 用于加载网页或 HTML 内容时,可能会遇到各种错误(如网络错误、HTTP 错误码、SSL 错误等)。正确处理这些错误码信息对于提升用户体验和调试问题至关重要。以下是关于 WebView 处理网页返回的错误码信息 的详细中文讲解,涵盖错误类型、处理方法、代码示例、Android 4.4+(基于 Chromium 引擎)的注意事项,以及实践建议。


一、WebView 错误码概述

  • 错误类型
  1. 网络错误:如无网络连接、DNS 解析失败、超时等。
  2. HTTP 错误码:如 404(未找到)、403(禁止访问)、500(服务器错误)等。
  3. SSL 错误:如证书无效、过期或不受信任。
  4. 资源加载错误:如图片、CSS 或 JavaScript 文件加载失败。
  • 错误处理目标
  • 捕获错误码和描述。
  • 向用户显示友好的错误提示(如 Toast 或自定义错误页面)。
  • 根据错误类型采取适当措施(如重试、切换缓存模式)。
  • 关键 API
  • WebViewClient.onReceivedError:处理页面或资源加载错误。
  • WebViewClient.onReceivedHttpError:处理 HTTP 错误码(API 23+)。
  • WebViewClient.onReceivedSslError:处理 SSL 错误。
  • Android 4.4+ 特性
  • 基于 Chromium 引擎,提供更详细的错误信息。
  • API 23+(Android 6.0)细化了 onReceivedErroronReceivedHttpError

二、准备工作

  1. 权限声明
    AndroidManifest.xml 中添加网络权限:
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  1. 布局文件
    res/layout/activity_main.xml 中添加 WebView:
   <WebView
       android:id="@+id/webView"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
  1. 错误页面(可选):
    assets 目录下创建 error.html 用于显示错误:
   <!-- assets/error.html -->
   <!DOCTYPE html>
   <html>
   <head>
       <title>Error</title>
   </head>
   <body>
       <h1>加载失败</h1>
       <p id="errorMessage">页面加载出错,请检查网络或稍后重试。</p>
   </body>
   </html>

三、WebView 错误码处理方法

以下是处理 WebView 中常见的错误码信息的详细方法,结合 WebViewClient 的相关回调。

1. 处理网络错误和通用错误

使用 onReceivedError 捕获页面加载错误(如网络断开、DNS 错误)。

  • 代码示例
  import android.os.Bundle;
  import android.webkit.WebResourceError;
  import android.webkit.WebResourceRequest;
  import android.webkit.WebView;
  import android.webkit.WebViewClient;
  import android.widget.Toast;
  import androidx.appcompat.app.AppCompatActivity;

  public class MainActivity extends AppCompatActivity {
      private WebView webView;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          webView = findViewById(R.id.webView);
          webView.getSettings().setJavaScriptEnabled(true);

          // 设置 WebViewClient 处理错误
          webView.setWebViewClient(new WebViewClient() {
              @Override
              public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                  // API 23+,处理详细错误
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                      String errorMsg = "错误码: " + error.getErrorCode() + ", 描述: " + error.getDescription();
                      Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                      Log.e("WebView", errorMsg);
                      // 加载自定义错误页面
                      view.loadUrl("file:///android_asset/error.html");
                  }
              }

              @Override
              public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                  // API 22 及以下,旧版方法
                  String errorMsg = "错误码: " + errorCode + ", 描述: " + description + ", URL: " + failingUrl;
                  Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                  Log.e("WebView", errorMsg);
                  view.loadUrl("file:///android_asset/error.html");
              }
          });

          // 加载网页
          webView.loadUrl("https://example.com");
      }
  }
  • 常见错误码WebResourceError.getErrorCode):
  • ERROR_HOST_LOOKUP:DNS 解析失败。
  • ERROR_CONNECT:无法连接到服务器。
  • ERROR_TIMEOUT:请求超时。
  • ERROR_IO:IO 异常(如无网络)。
  • 完整列表见:https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_HOST_LOOKUP
  • 说明
  • API 23+:使用 onReceivedError(WebView, WebResourceRequest, WebResourceError),提供详细错误信息(如 getErrorCode()getDescription())。
  • API 22 及以下:使用旧版 onReceivedError(WebView, int, String, String)
  • 加载自定义错误页面(如 error.html)或显示 Toast 提示用户。

2. 处理 HTTP 错误码

使用 onReceivedHttpError(API 23+)捕获 HTTP 状态码(如 404、500)。

  • 代码示例
  webView.setWebViewClient(new WebViewClient() {
      @Override
      public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
              int statusCode = errorResponse.getStatusCode();
              String reason = errorResponse.getReasonPhrase();
              String errorMsg = "HTTP 错误: " + statusCode + " " + reason;
              Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
              Log.e("WebView", errorMsg);
              if (statusCode == 404) {
                  view.loadUrl("file:///android_asset/error.html");
              }
          }
      }
  });
  • 常见 HTTP 错误码
  • 404 Not Found:页面或资源不存在。
  • 403 Forbidden:无权限访问。
  • 500 Internal Server Error:服务器内部错误。
  • 503 Service Unavailable:服务器不可用。
  • 说明
  • onReceivedHttpError 专门处理 HTTP 响应错误,适用于页面或资源请求。
  • 可根据状态码(如 404)显示特定错误页面或提示。

3. 处理 SSL 错误

使用 onReceivedSslError 处理 SSL 相关错误(如证书无效)。

  • 代码示例
  import android.net.http.SslError;
  import android.webkit.SslErrorHandler;

  webView.setWebViewClient(new WebViewClient() {
      @Override
      public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
          String errorMsg = "SSL 错误: " + error.getPrimaryError();
          Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
          Log.e("WebView", errorMsg);
          // 提示用户是否继续
          new AlertDialog.Builder(MainActivity.this)
                  .setTitle("SSL 错误")
                  .setMessage("证书错误,是否继续?")
                  .setPositiveButton("继续", (dialog, which) -> handler.proceed())
                  .setNegativeButton("取消", (dialog, which) -> {
                      handler.cancel();
                      view.loadUrl("file:///android_asset/error.html");
                  })
                  .show();
      }
  });
  • 常见 SSL 错误SslError.getPrimaryError):
  • SSL_UNTRUSTED:证书不受信任。
  • SSL_DATE_INVALID:证书过期或未生效。
  • SSL_IDMISMATCH:主机名不匹配。
  • 说明
  • 谨慎调用 handler.proceed(),可能导致安全风险。
  • 建议提示用户并默认调用 handler.cancel()

4. 处理资源加载错误

资源(如图片、CSS)加载失败时,也会触发 onReceivedError

  • 代码示例
  webView.setWebViewClient(new WebViewClient() {
      @Override
      public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
              if (request.isForMainFrame()) {
                  // 主页面错误
                  String errorMsg = "主页面错误: " + error.getDescription();
                  Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                  view.loadUrl("file:///android_asset/error.html");
              } else {
                  // 资源错误(如图片、CSS)
                  Log.w("WebView", "资源错误: " + request.getUrl() + ", " + error.getDescription());
              }
          }
      }
  });
  • 说明
  • 使用 request.isForMainFrame() 区分主页面和子资源错误。
  • 资源错误通常不影响主页面加载,可记录日志而无需提示用户。

5. 检查网络状态

在加载网页前检查网络状态,避免不必要的错误。

  • 代码示例
  import android.net.ConnectivityManager;
  import android.net.NetworkInfo;

  ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo networkInfo = cm.getActiveNetworkInfo();
  if (networkInfo == null || !networkInfo.isConnected()) {
      Toast.makeText(this, "无网络连接", Toast.LENGTH_SHORT).show();
      webView.loadUrl("file:///android_asset/error.html");
  } else {
      webView.loadUrl("https://example.com");
  }

四、完整代码示例

以下是一个完整的 WebView 示例,处理网络错误、HTTP 错误和 SSL 错误:

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.webkit.SslError;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true); // 谨慎启用

        // 设置 WebViewClient 处理错误
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    String errorMsg = "错误码: " + error.getErrorCode() + ", 描述: " + error.getDescription();
                    Log.e("WebView", errorMsg);
                    if (request.isForMainFrame()) {
                        Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                        view.loadUrl("file:///android_asset/error.html");
                    } else {
                        Log.w("WebView", "资源错误: " + request.getUrl());
                    }
                }
            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                // API 22 及以下
                String errorMsg = "错误码: " + errorCode + ", 描述: " + description;
                Log.e("WebView", errorMsg);
                Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                view.loadUrl("file:///android_asset/error.html");
            }

            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    int statusCode = errorResponse.getStatusCode();
                    String reason = errorResponse.getReasonPhrase();
                    String errorMsg = "HTTP 错误: " + statusCode + " " + reason;
                    Log.e("WebView", errorMsg);
                    Toast.makeText(MainActivity.this, errorMsg, Toast.LENGTH_LONG).show();
                    if (statusCode == 404) {
                        view.loadUrl("file:///android_asset/error.html");
                    }
                }
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                String errorMsg = "SSL 错误: " + error.getPrimaryError();
                Log.e("WebView", errorMsg);
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("SSL 错误")
                        .setMessage("证书错误,是否继续?")
                        .setPositiveButton("继续", (dialog, which) -> handler.proceed())
                        .setNegativeButton("取消", (dialog, which) -> {
                            handler.cancel();
                            view.loadUrl("file:///android_asset/error.html");
                        })
                        .show();
            }
        });

        // 检查网络状态
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        if (networkInfo == null || !networkInfo.isConnected()) {
            Toast.makeText(this, "无网络连接", Toast.LENGTH_SHORT).show();
            webView.loadUrl("file:///android_asset/error.html");
        } else {
            webView.loadUrl("https://example.com");
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        webView.onPause();
        webView.pauseTimers();
    }

    @Override
    protected void onResume() {
        super.onResume();
        webView.onResume();
        webView.resumeTimers();
    }

    @Override
    protected void onDestroy() {
        if (webView != null) {
            webView.stopLoading();
            webView.clearCache(true);
            webView.loadUrl("about:blank");
            webView.removeAllViews();
            webView.destroy();
            webView = null;
        }
        super.onDestroy();
    }
}
  • 布局文件res/layout/activity_main.xml):
  <WebView
      android:id="@+id/webView"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
  • 错误页面assets/error.html):
  <!DOCTYPE html>
  <html>
  <head>
      <title>错误</title>
  </head>
  <body>
      <h1>加载失败</h1>
      <p>页面加载出错,请检查网络或稍后重试。</p>
  </body>
  </html>
  • 说明
  • 处理网络错误、HTTP 错误和 SSL 错误。
  • 根据错误类型显示 Toast 或加载错误页面。
  • 检查网络状态,优化用户体验。
  • 包含生命周期管理。

五、注意事项

  1. Android 4.4+ 特性
  • Chromium 引擎:提供更详细的错误信息(如 WebResourceError)。
  • API 23+onReceivedErroronReceivedHttpError 分离,分别处理网络和 HTTP 错误。
  • WebView 更新:Android 5.0+ WebView 可通过 Google Play 更新,需测试版本兼容性:
    java String webViewVersion = WebView.getCurrentWebViewPackage().versionName; Log.d("WebView", "WebView Version: " + webViewVersion);
  1. 安全问题
  • HTTPS:Android 9+ 默认禁用 HTTP,优先使用 HTTPS:
    xml <application android:usesCleartextTraffic="false">
  • SSL 错误:避免直接调用 handler.proceed(),提示用户确认。
  • JavaScript 安全:启用 setJavaScriptEnabled(true) 时,仅加载受信任内容。
  1. 性能优化
  • 启用缓存以减少网络请求:
    java webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); webView.getSettings().setAppCacheEnabled(true);
  • 错误后尝试缓存模式:
    java webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
  1. 生命周期管理
  • 暂停/恢复 WebView: @Override protected void onPause() { super.onPause(); webView.onPause(); webView.pauseTimers(); } @Override protected void onResume() { super.onResume(); webView.onResume(); webView.resumeTimers(); }
  • 销毁 WebView:
    java @Override protected void onDestroy() { if (webView != null) { webView.stopLoading(); webView.clearCache(true); webView.loadUrl("about:blank"); webView.removeAllViews(); webView.destroy(); webView = null; } super.onDestroy(); }
  1. 用户体验
  • 显示友好的错误提示(如 Toast 或错误页面)。
  • 提供重试按钮:
    xml <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/retryButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="重试" /> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
    java findViewById(R.id.retryButton).setOnClickListener(v -> webView.reload());

六、学习建议与实践

  1. 学习路径
  • 掌握 WebViewClient 的错误处理方法(onReceivedErroronReceivedHttpErroronReceivedSslError)。
  • 了解常见错误码(网络、HTTP、SSL)。
  • 实现自定义错误页面和用户提示。
  • 测试不同网络环境(如无网络、弱网)。
  • 调试 WebView 错误。
  1. 实践项目
  • 简单项目:加载网页,捕获网络错误并显示 Toast。
  • 进阶项目:处理 HTTP 404/500 错误,加载自定义错误页面。
  • 高级项目:开发 Hybrid App,结合 REST API 和错误处理。
  1. 调试工具
  • Chrome DevTools:通过 chrome://inspect 调试 WebView,查看网络请求和错误。
  • Charles/Fiddler:抓包分析 HTTP 错误码。
  • Logcat:记录错误日志。
  • Android Studio Network Profiler:监控网络请求。
  1. 推荐资源
  • Android 官方文档:https://developer.android.com/reference/android/webkit/WebViewClient
  • WebView 错误码:https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_HOST_LOOKUP
  • SSL 错误:https://developer.android.com/reference/android/net/http/SslError

七、总结

  • 错误类型
  • 网络错误:DNS 失败、超时等,使用 onReceivedError
  • HTTP 错误:404、500 等,使用 onReceivedHttpError(API 23+)。
  • SSL 错误:证书问题,使用 onReceivedSslError
  • 处理方法
  • 捕获错误码和描述,记录日志。
  • 显示友好的提示或错误页面。
  • 检查网络状态,尝试缓存或重试。
  • Android 4.4+ 注意
  • Chromium 引擎提供更详细错误信息。
  • API 23+ 细化错误处理。
  • 测试 WebView 版本兼容性。
  • 推荐
  • 优先使用 HTTPS,谨慎处理 SSL 错误。
  • 结合错误页面和用户提示提升体验。
  • 使用 Chrome DevTools 调试。

如果需要更详细的代码示例(如复杂错误处理、自定义错误 UI)或特定场景的讲解,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注