Webview详解(上)

news2025/3/30 0:22:08

第一阶段:基础入门

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 WebViewiOS WKWebView
底层引擎ChromiumWebKit
性能较好,低端设备可能卡顿更优,特别在 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>
总结
功能AndroidiOS
原生调用 JavaScriptwebView.evaluateJavascript(jsCode, null)webView.evaluateJavaScript(jsCode, nil)
JavaScript 调用原生addJavascriptInterface + @JavascriptInterfaceWKUserContentController + 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");
总结
功能AndroidiOS
参数传递通过 @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)
    }
}
总结
功能AndroidiOS
监听页面加载开始onPageStartedwebView(_:didStartProvisionalNavigation:)
监听页面加载完成onPageFinishedwebView(_:didFinish:)
监听页面加载失败onReceivedErrorwebView(_:didFail:withError:)
处理 HTTPS 错误onReceivedSslErrorwebView(_:didFailProvisionalNavigation:withError:)
监控网络异常ConnectivityManagerNWPathMonitor

内存泄漏预防(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 的生命周期,容易泄漏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker镜像相关命令(Day2)

文章目录 前言一、问题描述二、相关命令1.查看镜像2.搜索镜像3.拉取镜像4.删除镜像5.镜像的详细信息6.标记镜像 三、验证与总结 前言 Docker 是一个开源的容器化平台&#xff0c;它让开发者能够将应用及其依赖打包到一个标准化的单元&#xff08;容器&#xff09;中运行。在 D…

LangChain4J开源开发框架简介

目录 1.1、前言1.2、集成方式简单1.3、核心功能与优势1.4、两种调用方式1.5、链式调用示例代码1.6、AI服务调用示例代码1.7、典型使用场景1.8、总结 1.1、前言 LangChain4J 是一个专为 Java 开发者设计的开源框架&#xff0c;旨在简化大型语言模型&#xff08;LLMs&#xff09;…

SpringBoot集成Elasticsearch 7.x spring-boot-starter-data-elasticsearch 方式

SpringBoot集成Elasticsearch 7.x | spring-boot-starter-data-elasticsearch 方式 前言添加maven依赖配置application.properties测试实体类 方式一&#xff1a;继承 ElasticsearchRepository&#xff08;适合简单查询&#xff09; 直接使用想自定义自己的Repository接口 方式…

STM32蜂鸣器播放音乐

STM32蜂鸣器播放音乐 STM32蜂鸣器播放音乐 Do, Re, Mi, Fa, 1. 功能概述 本系统基于STM32F7系列微控制器&#xff0c;实现了以下功能&#xff1a; 通过7个按键控制蜂鸣器发声&#xff0c;按键对应不同的音符。每个按键对应一个音符&#xff08;Do, Re, Mi, Fa, Sol, La, Si&a…

解码未来:DeepSeek开源FlashMLA,推理加速核心技术,引领AI变革

前言&#xff1a; DeepSeek 兑现了自己的诺言&#xff0c;开源了一款用于 Hopper GPU 的高效型 MLA 解码核&#xff1a;FlashMLA。 项目地址&#xff1a;https://github.com/deepseek-ai/FlashMLA 1:FlashMLA 是什么呀&#xff1f; MLA是DeepSeek大模型的重要技术创新点&…

leetcode:136. 只出现一次的数字(python3解法)

难度&#xff1a;简单 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 示例 1 &#xf…

Isaac Sim与Isaac Lab初使用

目录 基于Omiverse下载Isaacsim安装Isaac Lab配置isaacsim环境测试克隆仓库配置python环境强化学习训练的测试 IsaacLab模板配置vscode环境ros接口安装 作为nvidia出品的仿真软件&#xff0c;很多机器人、机器狗【具身智能】都可以有很不错的效果&#xff0c;所以会使用isaac s…

Spring AI Alibaba 工具(Function Calling)使用

一、工具(Function Calling)简介 Spring AI Alibaba工具(Function Calling)&#xff1a;https://java2ai.com/docs/1.0.0-M6.1/tutorials/function-calling/ 1、工具(Function Calling) “工具&#xff08;Tool&#xff09;”或“功能调用&#xff08;Function Calling&#xf…

Touch Diver:Weart为XR和机器人遥操作专属设计的触觉反馈动捕手套

在虚拟现实&#xff08;VR&#xff09;和扩展现实&#xff08;XR&#xff09;领域&#xff0c;触觉反馈技术正逐渐成为提升沉浸感和交互体验的重要因素。Weart作为这一领域的创新者&#xff0c;凭借其TouchDIVER Pro和TouchDIVER G1触觉手套&#xff0c;为用户带来了高度逼真的…

[深度学习]图片分类任务

图片分类任务 文章目录 图片分类任务分类任务回归和分类如何做分类的输出 图片分类卷积神经网络保持特征图大小不变更大的卷积核和更多的卷积核层数特征图怎么变小卷积神经网络中特征图改变卷积到全连接分类任务的LOSS一个基本的分类神经网络 经典神经网络AlexNetVggNetResNet …

Nodejs 项目打包部署方式

方式一&#xff1a;PM2 一、准备工作 确保服务器上已安装 Node.js 环境建议使用 PM2 进行进程管理&#xff08;需要额外安装&#xff09; 二、部署步骤 1.首先在服务器上安装 PM2&#xff08;推荐&#xff09;&#xff1a; npm install -g pm22.将项目代码上传到服务器&…

C++类与对象的的第三个简单的实战练习-3.25笔记

哔哩哔哩C面向对象高级语言程序设计教程&#xff08;118集全&#xff09; 简单实战三 创建项目 打开VS&#xff0c;点击创建一个新项目 创建一个空项目 点击下一步 点击工程名称&#xff0c;选择添加 选择新建项 选择C类 取名 点击确定&#xff0c;这时候还需要一个main.cpp …

HarmonyOS-ArkUI Grip组件

我们在学习List的时候&#xff0c;已经捎带引入了Grid。讲解如下图所示&#xff1a; 也就是&#xff0c;如果一个表&#xff0c;长宽基本都是一致的&#xff0c;那么此时可以完全不用Grid也可以实现&#xff0c;并且&#xff0c;优先考虑的就是List。 如果List实现不了的情况下…

2025清华大学:DeepSeek教程全集(PDF+视频精讲,共10份).zip

一、资料列表 第一课&#xff1a;Deepseek基础入门 第二课&#xff1a;DeepSeek赋能职场 第三课&#xff1a;普通人如何抓住DeepSeek红利 第四课&#xff1a;让科研像聊天一样简单 第五课&#xff1a;DeepSeek与AI幻觉 第六课&#xff1a;基于DeepSeek的AI音乐词曲的创造法 第…

mac vim命令快捷键

目录 移动光标插入模式复制/粘贴删除搜索/替换退出 移动光标 快捷键说明0 / ^跳到行首&#xff0c;移动到光标所在行的"行首"$跳到行末&#xff0c;移动到光标所在行的"行尾"gg跳到文件第一行G移动到文章的最后[n]G跳到第n行w光标跳到下个字的开头e光标跳…

低代码配置式Web组态解析

低代码配置式Web组态技术通过可视化操作和预置组件库&#xff0c;大幅降低开发门槛&#xff0c;适用于工业控制、物联网监控、数据可视化等场景。以下是综合行业实践和产品特性的分析&#xff1a; ‌一、核心功能与优势‌ ‌可视化编辑与拖拽布局‌ 提供图形化编辑器&#xff0…

基于web的家政服务网站

内容摘要 由于互联网的使用&#xff0c;人们在管理、应用、服务等领域使用数据更加简洁、方便&#xff0c;大大提高了工作效率。互联网正逐渐融入我们的生活&#xff0c;影响和改变我们的生活。 家政服务管理系统是典型的信息管理系统&#xff08;MIS&#xff09;。其开发主要…

聚水潭数据集成到MySQL的最佳实践分享

聚水潭数据集成到MySQL的技术案例分享 在本次技术案例中&#xff0c;我们将探讨如何通过轻易云数据集成平台&#xff0c;将聚水潭的数据高效、可靠地集成到MySQL数据库中。具体的集成方案为“聚水潭-商品信息查询-->BI初本-商品信息表_copy”。该方案旨在实现从聚水潭获取商…

线性代数核心概念与NumPy科学计算实战全解析

前言 学习方法&#xff1a; 思维导图&#xff0c;梳理 多记忆&#xff0c;函数名和功能&#xff0c;参数 学会应用&#xff0c;不要钻牛角尖 一、浅解线性代数 1.1标量 标量是一个只有大小没有方向的量。在数学上&#xff0c;标量通常表示为一个普通的数字&#xff0c;如‌质量…

C#自定义曲线便器功能实现(简化版)

目录 一、曲线编辑器实现功能 二、实现方法说明 三、关键代码说明 1、绘制背景板和曲线 2、绘制坐标系面板 3、绘制曲线 四、工程下载连接 一、曲线编辑器实现功能 添加或者删除控制点&#xff0c;通过移动控制点来修改曲线形状 二、实现方法说明 1、坐标系系统&#x…