Webview详解(上)

发布于:2025-03-27 ⋅ 阅读:(81) ⋅ 点赞:(0)

第一阶段:基础入门

WebView基础概念

什么是Webview?

WebView是一种用于在移动应用程序中展示网页内容的嵌入式浏览器组件。它允许开发者将网页内容直接加载到应用界面中,用户无需离开应用即可浏览网页。WebView 通常用于加载 HTML、CSS、JavaScript 等 Web 技术构建的内容,广泛应用于混合移动应用开发(Hybrid App)或需要展示 Web 内容的场景。

主要特点:
  1. 嵌入网页:可以直接在应用中加载和显示网页。
  2. 支持 Web 技术:支持 HTML、CSS、JavaScript 等标准 Web 技术。
  3. 与原生应用交互:可以通过 JavaScript 与原生代码(如 Java、Kotlin、Swift)进行通信。
  4. 轻量级:相比完整的浏览器,WebView 更轻量,适合嵌入应用。
常见用途:
  • 在应用中展示动态更新的 Web 内容(如新闻、博客)。
  • 实现混合应用开发,结合 Web 和原生功能。
  • 加载在线帮助文档或用户协议页面。
平台支持:
  • Android:通过 WebView 类实现。
  • iOS:通过 WKWebView 组件实现。

简单来说,WebView 是移动应用中的一个窗口,用于展示网页内容,同时保持应用的完整性和用户体验。

WebView的常见应用场景

1. 混合开发(Hybrid Development)

混合开发结合了原生应用(Native App)和网页技术(Web App)的优势,使用 WebView 来加载网页内容,同时通过 JavaScript 与原生代码交互。常见的应用场景包括:

  • 跨平台开发:通过 WebView 加载基于 HTML、CSS 和 JavaScript 开发的页面,可以减少为不同平台(如 iOS 和 Android)单独开发的工作量。
  • 快速迭代:由于网页内容可以远程更新,开发者可以直接修改服务器端的 HTML 文件,而无需发布新的应用版本。
  • 轻量级功能:对于一些不需要复杂原生功能的部分(如帮助文档、活动页面、用户协议等),可以直接用 WebView 加载网页。
  • 第三方集成:集成第三方服务(如支付、地图、广告等)时,可以通过 WebView 加载其提供的网页界面。
2. 动态内容加载(Dynamic Content Loading)

WebView 可以加载远程或本地的 HTML 内容,适合需要动态更新内容的场景。常见的应用场景包括:

  • 实时内容更新:从服务器加载最新的 HTML 内容,例如新闻、公告、活动详情等,用户无需更新应用即可获取最新信息。
  • 富文本展示:加载包含图片、视频、表格等复杂格式的内容,例如文章详情页、产品介绍页等。
  • 离线缓存:通过缓存机制,WebView 可以在离线状态下加载本地存储的网页内容,提升用户体验。
  • 动态表单:加载动态生成的表单页面,例如问卷调查、用户反馈等。
其他常见应用场景

除了上述两种主要场景,WebView 还可以用于以下场景:

  • OAuth 授权:通过 WebView 加载第三方登录页面,例如使用 Google、Facebook 登录。
  • 内嵌广告:在应用中嵌入广告页面,广告内容由广告平台动态提供。
  • 教育类应用:加载在线课程、电子书或交互式学习内容。
  • 企业应用:加载企业内部的管理系统或工作平台。

Android WebView vs iOS WKWebView 核心差异

1. 底层引擎
  • Android WebView

    • 基于 Chromium 内核(从 Android 4.4 开始)。
    • 在 Android 7.0 及以上版本中,WebView 是一个独立的模块,可以通过 Google Play 更新。
    • 支持最新的 Web 标准(如 HTML5、CSS3、JavaScript)。
  • iOS WKWebView

    • 基于 Safari 的 WebKit 引擎。
    • 从 iOS 8 开始引入,取代了旧的 UIWebView
    • 同样支持最新的 Web 标准,性能优于 UIWebView

WKWebView 的性能通常优于 Android WebView,特别是在 JavaScript 执行和渲染效率方面。

2. 性能
  • Android WebView

    • 性能较好,但在低端设备上可能出现卡顿。
    • 内存占用较高,特别是在加载复杂网页时。
  • iOS WKWebView

    • 性能显著优于 Android WebView,特别是在 JavaScript 执行和页面渲染方面。
    • 内存管理更高效,独立于应用进程运行,减少了内存泄漏的风险。
3. 进程模型
  • Android WebView

    • 运行在应用的主进程中,与应用共享内存。
    • 如果 WebView 崩溃,可能会导致整个应用崩溃。
  • iOS WKWebView

    • 运行在独立的进程中,与应用主进程分离。
    • 如果 WKWebView 崩溃,不会影响应用的主进程。
4. API 设计
  • Android WebView

    • 提供了丰富的 API,允许开发者自定义 WebView 的行为。
    • 支持通过 WebViewClientWebChromeClient 处理网页加载、JavaScript 交互等事件。
    • 支持通过 addJavascriptInterface 实现 JavaScript 与原生代码的交互。
  • iOS WKWebView

    • API 设计更加现代化和简洁。
    • 通过 WKNavigationDelegateWKUIDelegate 处理网页加载和用户交互。
    • 支持通过 evaluateJavaScript 执行 JavaScript 代码,并通过 WKScriptMessageHandler 实现 JavaScript 与原生代码的交互。
5. 缓存与存储
  • Android WebView

    • 支持缓存网页内容,但缓存管理功能较弱。
    • 支持通过 WebSettings 配置缓存行为。
  • iOS WKWebView

    • 提供了更强大的缓存管理功能,支持 HTTP 缓存、本地存储等。
    • 支持通过 WKWebsiteDataStore 管理缓存和存储数据。
6. 安全性
  • Android WebView

    • 默认安全性较低,开发者需要手动配置以提高安全性。
    • 支持通过 WebSettings 禁用 JavaScript、限制文件访问等。
  • iOS WKWebView

    • 默认安全性较高,独立进程模型减少了安全风险。
    • 支持通过 WKPreferences 配置安全性选项。
7. 兼容性
  • Android WebView

    • 兼容性较好,但由于 Android 设备碎片化,不同设备上的表现可能存在差异。
    • 需要针对不同 Android 版本进行适配。
  • iOS WKWebView

    • 兼容性较高,所有 iOS 设备上的表现基本一致。
    • 需要 iOS 8 及以上版本支持。
8. 开发体验
  • Android WebView

    • 开发文档详细,但 API 较多,学习曲线较陡。
    • 调试工具依赖于 Chrome DevTools。
  • iOS WKWebView

    • 开发文档清晰,API 设计简洁,学习曲线较平缓。
    • 调试工具依赖于 Safari Web Inspector。
总结
特性 Android WebView iOS WKWebView
底层引擎 Chromium WebKit
性能 较好,低端设备可能卡顿 更优,特别在 JavaScript 执行
进程模型 运行在主进程 独立进程
API 设计 灵活但复杂 现代且简洁
缓存与存储 功能较弱 功能强大
安全性 默认较低,需手动配置 默认较高
兼容性 设备碎片化,需适配 设备一致,兼容性好
开发体验 文档详细,学习曲线陡 文档清晰,学习曲线平缓

环境搭建与基础使用

初始化WebView并加载网页

Android 初始化 WebView 并加载网页
1. 加载 URL
// 在 Activity 或 Fragment 中
import android.os.Bundle;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 WebView
        WebView webView = findViewById(R.id.webView);

        // 启用 JavaScript
        webView.getSettings().setJavaScriptEnabled(true);

        // 加载 URL
        String url = "https://www.example.com";
        webView.loadUrl(url);
    }
}
2. 加载本地 HTML
// 在 Activity 或 Fragment 中
import android.os.Bundle;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 WebView
        WebView webView = findViewById(R.id.webView);

        // 启用 JavaScript
        webView.getSettings().setJavaScriptEnabled(true);

        // 加载本地 HTML 文件
        String htmlContent = "<html><body><h1>Hello, World!</h1></body></html>";
        webView.loadData(htmlContent, "text/html", "UTF-8");

        // 或者从 assets 文件夹加载本地 HTML 文件
        // webView.loadUrl("file:///android_asset/index.html");
    }
}
iOS 初始化 WKWebView 并加载网页
1. 加载 URL
import UIKit
import WebKit

class ViewController: UIViewController {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 初始化 WKWebView
        webView = WKWebView(frame: self.view.frame)
        self.view.addSubview(webView)

        // 加载 URL
        if let url = URL(string: "https://www.example.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }
}
2. 加载本地 HTML
import UIKit
import WebKit

class ViewController: UIViewController {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 初始化 WKWebView
        webView = WKWebView(frame: self.view.frame)
        self.view.addSubview(webView)

        // 加载本地 HTML 字符串
        let htmlContent = "<html><body><h1>Hello, World!</h1></body></html>"
        webView.loadHTMLString(htmlContent, baseURL: nil)

        // 或者从本地文件加载 HTML
        if let filePath = Bundle.main.path(forResource: "index", ofType: "html") {
            let fileURL = URL(fileURLWithPath: filePath)
            webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
        }
    }
}
注意事项
  • Android
    • AndroidManifest.xml 中添加网络权限:<uses-permission android:name="android.permission.INTERNET" />
    • 使用 WebViewClient 处理页面加载事件(如重定向、错误处理)。
  • iOS
    • Info.plist 中允许加载 HTTP 资源(如果需要):添加 NSAppTransportSecurity 配置。
    • 使用 WKNavigationDelegate 处理页面加载事件。

第二阶段:核心交互与功能

WebView与JavaScript交互

原生调用js方法,js调用原生接口

原生调用 JavaScript 方法
1. Android 实现

在 Android 中,可以通过 WebViewevaluateJavascript 方法执行 JavaScript 代码。

// 在 Activity 或 Fragment 中
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
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
        webView = findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);

        // 设置 WebViewClient,确保页面加载完成后再执行 JavaScript
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);

                // 调用 JavaScript 方法
                String jsCode = "alert('Hello from Android!')";
                webView.evaluateJavascript(jsCode, null);
            }
        });

        // 加载网页
        webView.loadUrl("https://www.example.com");
    }
}
2. iOS 实现

在 iOS 中,可以通过 WKWebViewevaluateJavaScript(_:completionHandler:) 方法执行 JavaScript 代码。

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 初始化 WKWebView
        webView = WKWebView(frame: self.view.frame)
        webView.navigationDelegate = self
        self.view.addSubview(webView)

        // 加载网页
        if let url = URL(string: "https://www.example.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }

    // 页面加载完成后调用 JavaScript
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let jsCode = "alert('Hello from iOS!')"
        webView.evaluateJavaScript(jsCode, completionHandler: nil)
    }
}
JavaScript 调用原生接口
1. Android 实现(JSBridge)

在 Android 中,可以通过 addJavascriptInterface 将 Java 对象暴露给 JavaScript。

// 在 Activity 或 Fragment 中
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
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
        webView = findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);

        // 将 NativeBridge 对象暴露给 JavaScript
        webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");

        // 加载网页
        webView.loadUrl("file:///android_asset/index.html");
    }

    // 定义 NativeBridge 类
    public class NativeBridge {
        @JavascriptInterface
        public void showToast(String message) {
            // 在原生代码中处理 JavaScript 调用
            runOnUiThread(() -> {
                Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
            });
        }
    }
}

在 HTML/JavaScript 中调用原生方法:

<!DOCTYPE html>
<html>
<head>
    <title>JSBridge Example</title>
    <script>
        function callNative() {
            // 调用原生方法
            NativeBridge.showToast("Hello from JavaScript!");
        }
    </script>
</head>
<body>
    <button onclick="callNative()">Call Native</button>
</body>
</html>
2. iOS 实现(WKScriptMessageHandler)

在 iOS 中,可以通过 WKUserContentControllerWKScriptMessageHandler 实现 JavaScript 调用原生接口。

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 配置 WKWebView
        let config = WKWebViewConfiguration()
        let userContentController = WKUserContentController()

        // 注册消息处理器
        userContentController.add(self, name: "nativeBridge")
        config.userContentController = userContentController

        // 初始化 WKWebView
        webView = WKWebView(frame: self.view.frame, configuration: config)
        self.view.addSubview(webView)

        // 加载网页
        if let filePath = Bundle.main.path(forResource: "index", ofType: "html") {
            let fileURL = URL(fileURLWithPath: filePath)
            webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
        }
    }

    // 处理 JavaScript 消息
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "nativeBridge", let messageBody = message.body as? String {
            // 处理 JavaScript 调用
            showToast(message: messageBody)
        }
    }

    // 显示 Toast
    func showToast(message: String) {
        let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        self.present(alert, animated: true, completion: nil)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            alert.dismiss(animated: true, completion: nil)
        }
    }
}

在 HTML/JavaScript 中调用原生方法:

<!DOCTYPE html>
<html>
<head>
    <title>WKScriptMessageHandler Example</title>
    <script>
        function callNative() {
            // 调用原生方法
            window.webkit.messageHandlers.nativeBridge.postMessage("Hello from JavaScript!");
        }
    </script>
</head>
<body>
    <button onclick="callNative()">Call Native</button>
</body>
</html>
总结
功能 Android iOS
原生调用 JavaScript webView.evaluateJavascript(jsCode, null) webView.evaluateJavaScript(jsCode, nil)
JavaScript 调用原生 addJavascriptInterface + @JavascriptInterface WKUserContentController + WKScriptMessageHandler

参数传递与异步通信设计

参数传递
1. Android 实现

在 Android 中,可以通过 @JavascriptInterface 注解的方法传递参数。

  • JavaScript 调用原生并传递参数
// 在 NativeBridge 类中
@JavascriptInterface
public void showToast(String message) {
    runOnUiThread(() -> {
        Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
    });
}
  • JavaScript 调用
NativeBridge.showToast("Hello from JavaScript!");
  • 传递复杂参数
@JavascriptInterface
public void handleData(String jsonData) {
    // 解析 JSON 数据
    try {
        JSONObject json = new JSONObject(jsonData);
        String name = json.getString("name");
        int age = json.getInt("age");
        // 处理数据
    } catch (JSONException e) {
        e.printStackTrace();
    }
}
  • JavaScript 调用
let data = { name: "John", age: 30 };
NativeBridge.handleData(JSON.stringify(data));
2. iOS 实现

在 iOS 中,可以通过 WKScriptMessageHandler 接收 JavaScript 传递的参数。

  • JavaScript 调用原生并传递参数
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "nativeBridge", let messageBody = message.body as? [String: Any] {
        // 处理参数
        let name = messageBody["name"] as? String
        let age = messageBody["age"] as? Int
        // 处理数据
    }
}
  • JavaScript 调用
let data = { name: "John", age: 30 };
window.webkit.messageHandlers.nativeBridge.postMessage(data);
异步通信设计
1. Android 实现

在 Android 中,可以通过回调机制实现异步通信。

  • 定义回调接口
public interface JsCallback {
    void onResult(String result);
}
  • 暴露给 JavaScript 的方法
@JavascriptInterface
public void fetchData(String request, final JsCallback callback) {
    // 模拟异步操作
    new Thread(() -> {
        try {
            Thread.sleep(2000); // 模拟网络请求
            final String result = "Response for: " + request;
            runOnUiThread(() -> callback.onResult(result));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}
  • JavaScript 调用
NativeBridge.fetchData("Hello", function(result) {
    console.log(result); // 输出:Response for: Hello
});
2. iOS 实现

在 iOS 中,可以通过 evaluateJavaScript 实现异步通信。

  • JavaScript 调用原生并传递回调
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "nativeBridge", let request = message.body as? String {
        // 模拟异步操作
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 2) // 模拟网络请求
            let result = "Response for: \(request)"
            DispatchQueue.main.async {
                // 调用 JavaScript 回调
                self.webView.evaluateJavaScript("handleResponse('\(result)')", completionHandler: nil)
            }
        }
    }
}
  • JavaScript 调用
function fetchData(request) {
    window.webkit.messageHandlers.nativeBridge.postMessage(request);
}

function handleResponse(result) {
    console.log(result); // 输出:Response for: Hello
}

fetchData("Hello");
总结
功能 Android iOS
参数传递 通过 @JavascriptInterface 方法接收参数 通过 WKScriptMessageHandler 接收参数
复杂参数 使用 JSON 字符串传递 直接传递字典对象
异步通信 使用回调接口实现 使用 evaluateJavaScript 实现

页面生命周期控制

处理页面加载状态,错误处理与网络异常监控

Android 实现

在 Android 中,可以通过 WebViewClient 监听页面加载状态,并通过 onReceivedErroronReceivedHttpError 处理错误。

1. 监听页面加载状态
import android.os.Bundle;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
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
        webView = findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);

        // 设置 WebViewClient 监听加载状态
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                // 页面开始加载
                showLoadingIndicator();
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                // 页面加载完成
                hideLoadingIndicator();
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);
                // 页面加载失败
                handlePageLoadError(error.getDescription().toString());
            }

            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
                super.onReceivedHttpError(view, request, errorResponse);
                // 处理 HTTP 错误
                handleHttpError(errorResponse.getStatusCode());
            }
        });

        // 加载网页
        webView.loadUrl("https://www.example.com");
    }

    private void showLoadingIndicator() {
        // 显示加载指示器(例如 ProgressBar)
    }

    private void hideLoadingIndicator() {
        // 隐藏加载指示器
    }

    private void handlePageLoadError(String errorDescription) {
        // 显示错误页面或错误提示
        String errorHtml = "<html><body><h1>Error: " + errorDescription + "</h1></body></html>";
        webView.loadData(errorHtml, "text/html", "UTF-8");
    }

    private void handleHttpError(int statusCode) {
        // 根据 HTTP 状态码处理错误
        String errorMessage = "HTTP Error: " + statusCode;
        showToast(errorMessage);
    }

    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}
2. 处理 HTTPS 错误

如果需要处理 HTTPS 错误,可以重写 onReceivedSslError 方法:

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
    // 处理 SSL 错误
    handler.cancel(); // 取消加载
    handlePageLoadError("SSL Error: " + error.toString());
}
3. 监控网络异常

可以通过 ConnectivityManager 监控网络状态:

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

private boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}

@Override
protected void onResume() {
    super.onResume();
    if (!isNetworkAvailable()) {
        showToast("No network connection");
        handlePageLoadError("No network connection");
    }
}
iOS 实现

在 iOS 中,可以通过 WKNavigationDelegate 监听页面加载状态,并通过 didFaildidFailProvisionalNavigation 处理错误。

1. 监听页面加载状态
import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 初始化 WKWebView
        webView = WKWebView(frame: self.view.frame)
        webView.navigationDelegate = self
        self.view.addSubview(webView)

        // 加载网页
        if let url = URL(string: "https://www.example.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }

    // 页面开始加载
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        showLoadingIndicator()
    }

    // 页面加载完成
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        hideLoadingIndicator()
    }

    // 页面加载失败
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        handlePageLoadError(error.localizedDescription)
    }

    // 页面加载失败(临时导航)
    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        handlePageLoadError(error.localizedDescription)
    }

    private func showLoadingIndicator() {
        // 显示加载指示器(例如 UIActivityIndicatorView)
    }

    private func hideLoadingIndicator() {
        // 隐藏加载指示器
    }

    private func handlePageLoadError(_ errorDescription: String) {
        // 显示错误页面或错误提示
        let errorHtml = "<html><body><h1>Error: \(errorDescription)</h1></body></html>"
        webView.loadHTMLString(errorHtml, baseURL: nil)
    }
}
2. 处理 HTTPS 错误

如果需要处理 HTTPS 错误,可以在 didFailProvisionalNavigation 方法中检查错误码:

func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
    if (error as NSError).code == NSURLErrorServerCertificateUntrusted {
        handlePageLoadError("SSL Error: \(error.localizedDescription)")
    }
}
3. 监控网络异常

可以通过 Network 框架监控网络状态:

import Network

let monitor = NWPathMonitor()

override func viewDidLoad() {
    super.viewDidLoad()

    monitor.pathUpdateHandler = { path in
        if path.status == .satisfied {
            print("Network is available")
        } else {
            DispatchQueue.main.async {
                self.showToast("No network connection")
                self.handlePageLoadError("No network connection")
            }
        }
    }

    let queue = DispatchQueue(label: "NetworkMonitor")
    monitor.start(queue: queue)
}

private func showToast(_ message: String) {
    let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
    self.present(alert, animated: true, completion: nil)
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        alert.dismiss(animated: true, completion: nil)
    }
}
总结
功能 Android iOS
监听页面加载开始 onPageStarted webView(_:didStartProvisionalNavigation:)
监听页面加载完成 onPageFinished webView(_:didFinish:)
监听页面加载失败 onReceivedError webView(_:didFail:withError:)
处理 HTTPS 错误 onReceivedSslError webView(_:didFailProvisionalNavigation:withError:)
监控网络异常 ConnectivityManager NWPathMonitor

内存泄漏预防(Android的独立进程方案)

独立进程方案的优势
  1. 彻底释放内存
    • WebView 运行在独立进程中,关闭页面后可以直接销毁进程,释放所有相关内存。
  2. 隔离崩溃风险
    • WebView 的崩溃不会影响主进程的稳定性。
  3. 优化性能
    • 独立进程可以充分利用多核 CPU,提升性能。
实现步骤
1. 在 Manifest 中声明独立进程

AndroidManifest.xml 中为需要运行 WebView 的 Activity 指定独立进程:

<activity
    android:name=".WebViewActivity"
    android:process=":webview_process" />

通过 android:process 属性,WebViewActivity 将运行在一个独立的进程中。

2. 启动独立进程的 Activity

从主进程启动独立进程的 Activity:

Intent intent = new Intent(this, WebViewActivity.class);
startActivity(intent);
3. 在独立进程中初始化 WebView

WebViewActivity 中初始化 WebView:

public class WebViewActivity extends AppCompatActivity {
    private WebView webView;

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

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

        // 加载网页
        webView.loadUrl("https://www.example.com");
    }
}
4. 销毁独立进程

在页面关闭时,主动销毁独立进程:

@Override
protected void onDestroy() {
    super.onDestroy();
    // 销毁 WebView
    if (webView != null) {
        webView.stopLoading();
        webView.destroy();
        webView = null;
    }
    // 销毁进程
    android.os.Process.killProcess(android.os.Process.myPid());
}
注意事项
  1. 进程间通信

    • 独立进程与主进程之间的通信可以通过 IntentBroadcastReceiverAIDL 实现。
    • 示例:通过 BroadcastReceiver 发送消息:
      // 主进程中注册 BroadcastReceiver
      BroadcastReceiver receiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
              String message = intent.getStringExtra("message");
              Log.d("WebViewProcess", "Received message: " + message);
          }
      };
      registerReceiver(receiver, new IntentFilter("com.example.WEBVIEW_MESSAGE"));
      
      // 独立进程中发送消息
      Intent intent = new Intent("com.example.WEBVIEW_MESSAGE");
      intent.putExtra("message", "Hello from WebView process!");
      sendBroadcast(intent);
      
  2. 资源释放

    • 确保在 onDestroy 中释放 WebView 资源,并销毁独立进程。
  3. 性能开销

    • 独立进程会增加一定的内存和 CPU 开销,适用于需要频繁加载和销毁 WebView 的场景。
  4. 兼容性问题

    • 在某些低端设备上,独立进程可能会影响应用的启动速度和稳定性。

优化建议

  1. WebView 复用

    • 如果不需要频繁销毁 WebView,可以考虑复用 WebView,而不是每次创建独立进程。
  2. 内存监控

    • 使用工具(如 Android Profiler)监控内存使用情况,确保独立进程的内存被正确释放。
  3. 错误处理

    • 在独立进程中处理 WebView 的崩溃和错误,避免影响主进程。

总结

方案 优点 缺点
独立进程方案 彻底释放内存,隔离崩溃风险,优化性能 增加内存和 CPU 开销,兼容性问题
WebView 复用 减少进程创建开销,适合频繁使用 WebView 的场景 需要手动管理 WebView 的生命周期,容易泄漏

网站公告

今日签到

点亮在社区的每一天
去签到