Webview详解(上)

第一阶段:基础入门

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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 WebViewWebView webView = findViewById(R.id.webView);// 启用 JavaScriptwebView.getSettings().setJavaScriptEnabled(true);// 加载 URLString 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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 WebViewWebView webView = findViewById(R.id.webView);// 启用 JavaScriptwebView.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 WebKitclass ViewController: UIViewController {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 初始化 WKWebViewwebView = WKWebView(frame: self.view.frame)self.view.addSubview(webView)// 加载 URLif let url = URL(string: "https://www.example.com") {let request = URLRequest(url: url)webView.load(request)}}
}
2. 加载本地 HTML
import UIKit
import WebKitclass ViewController: UIViewController {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 初始化 WKWebViewwebView = 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)// 或者从本地文件加载 HTMLif 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 WebViewwebView = findViewById(R.id.webView);webView.getSettings().setJavaScriptEnabled(true);// 设置 WebViewClient,确保页面加载完成后再执行 JavaScriptwebView.setWebViewClient(new WebViewClient() {@Overridepublic 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 WebKitclass ViewController: UIViewController, WKNavigationDelegate {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 初始化 WKWebViewwebView = WKWebView(frame: self.view.frame)webView.navigationDelegate = selfself.view.addSubview(webView)// 加载网页if let url = URL(string: "https://www.example.com") {let request = URLRequest(url: url)webView.load(request)}}// 页面加载完成后调用 JavaScriptfunc 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 WebViewwebView = findViewById(R.id.webView);webView.getSettings().setJavaScriptEnabled(true);// 将 NativeBridge 对象暴露给 JavaScriptwebView.addJavascriptInterface(new NativeBridge(), "NativeBridge");// 加载网页webView.loadUrl("file:///android_asset/index.html");}// 定义 NativeBridge 类public class NativeBridge {@JavascriptInterfacepublic 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 WebKitclass ViewController: UIViewController, WKScriptMessageHandler {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 配置 WKWebViewlet config = WKWebViewConfiguration()let userContentController = WKUserContentController()// 注册消息处理器userContentController.add(self, name: "nativeBridge")config.userContentController = userContentController// 初始化 WKWebViewwebView = 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)}}// 显示 Toastfunc 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? Stringlet 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 WebViewwebView = findViewById(R.id.webView);webView.getSettings().setJavaScriptEnabled(true);// 设置 WebViewClient 监听加载状态webView.setWebViewClient(new WebViewClient() {@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon) {super.onPageStarted(view, url, favicon);// 页面开始加载showLoadingIndicator();}@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);// 页面加载完成hideLoadingIndicator();}@Overridepublic void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {super.onReceivedError(view, request, error);// 页面加载失败handlePageLoadError(error.getDescription().toString());}@Overridepublic 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 WebKitclass ViewController: UIViewController, WKNavigationDelegate {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 初始化 WKWebViewwebView = WKWebView(frame: self.view.frame)webView.navigationDelegate = selfself.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 Networklet monitor = NWPathMonitor()override func viewDidLoad() {super.viewDidLoad()monitor.pathUpdateHandler = { path inif 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 指定独立进程:

<activityandroid: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;@Overrideprotected 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();// 销毁 WebViewif (webView != null) {webView.stopLoading();webView.destroy();webView = null;}// 销毁进程android.os.Process.killProcess(android.os.Process.myPid());
}
注意事项
  1. 进程间通信

    • 独立进程与主进程之间的通信可以通过 IntentBroadcastReceiverAIDL 实现。
    • 示例:通过 BroadcastReceiver 发送消息:
      // 主进程中注册 BroadcastReceiver
      BroadcastReceiver receiver = new BroadcastReceiver() {@Overridepublic 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.rhkb.cn/news/39815.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

日事清甘特图制作工具:一键生成,精准管理项目周期

在工作中&#xff0c;我们很多岗位都经常需要对项目进度进行追踪&#xff0c; 例如人事经理需要要追踪招聘进度或员工培训计划&#xff0c;项目经理负责监督项目的各个阶段以保证按计划执行&#xff0c;软件研发经理则需确保功能迭代的及时交付&#xff0c;而市场经理负责监控…

vue 加载动态效果,自行封装组件

背景&#xff1a; 在项目开发中&#xff0c;会请求接口&#xff0c;就会遇到加载中、加载成功、加载失败、和加载成功但暂无数据等情况。就自行封装了一个加载组件。采用vue3elementsetup组合式写法。 实现效果&#xff1a; 封装组件&#xff1a; //封装组件 <template>…

SQLark SQL编辑器秘籍,编写高效SQL查询

SQLark 是一款功能强大的数据库开发和管理工具&#xff0c;用于快速查询、创建和管理不同类型的数据库系统&#xff0c;支持达梦、Oracle 和 MySQL 数据库。SQLark内置的 SQL 编辑器&#xff0c;基于语法解析&#xff0c;集成智能提示、实时语法检查及语法高亮等功能&#xff0…

Flutter项目之table页面实现

目录&#xff1a; 1、首页页面index.dart&#xff08;首页table页面&#xff09; 1、首页页面 效果图&#xff1a; index.dart&#xff08;首页table页面&#xff09; import package:flutter/material.dart; import package:flutter_haoke/pages/home/info/index.dart; impo…

【学习笔记】卷积网络简介及原理探析

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 之前的文章参考下面的链接&#xf…

【北京迅为】iTOP-RK3568开发板鸿蒙OpenHarmony系统南向驱动开发实操-HDF驱动配置UART

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

【蓝桥杯每日一题】3.25

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x “OJ超时不是终点&#xff0c;是算法在提醒你该优化时间复杂度了&#xff01;” 目录 3.25 差分数组 一、一维差分 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a;…

穿透Session 0隔离

1、前言 在 Windows XP 和 Windows Server 2003 之前&#xff0c;用户和服务会共享同一个会话&#xff0c;而这个会话是由第一个登录到控制台的用户来启动的&#xff0c;该会话就称为Session 0。 而从Windows Vista 开始&#xff0c;Windows 采取了会话隔离的措施&#xff0c;…

大模型思维链COT:Chain-of-Thought Prompting Elicits Reasoningin Large Language Models

一、TL&#xff1b;DR 探索了COT&#xff08;chain-of-thought prompting&#xff09;通过一系列的中间推理步骤来显著的提升了LLM的复杂推理能力在三个大型语言模型上的实验表明&#xff0c;思维链提示能够提升模型在一系列算术、常识和符号推理任务上的表现解释了一下为什么…

颠覆传统:SaaS 品牌如何通过 SEO 策略引爆市场!

SaaS 商业模式提供了令人难以置信的可扩展性和盈利能力——但前提是与正确的营销增长策略相结合。 SaaS 品牌知道&#xff0c;托管基于云的应用程序的成本会随着用户量的增加而降低&#xff0c;因此必须专注于订阅者的快速增长&#xff0c;以保持竞争力并降低成本。 许多 CMO…

大模型训练 | 智能体知识库 资源收集之心理咨询问答数据集

最近我一直在研究AI大模型相关的内容&#xff0c;想着从现在开始慢慢收集各种各样的资源&#xff0c;万一以后需要训练大模型的时候可以用到&#xff0c;或者自己以后也许会需要。今天我想介绍一组“心理咨询问答数据集”产品&#xff0c;包含9414条心理咨询问答数据&#xff0…

vivado 创建生成时钟

创建生成时钟 生成时钟源自另一个现有时钟 &#xff08;主时钟&#xff09;。通常用来描述由逻辑块在主时钟上执行的波形变换。由于生成时钟的定义 取决于主时钟特性&#xff0c;因此必须首先定义主时钟。要明确定义生成时钟&#xff0c;必须使用 create_generated_clock 命…

数据清洗:基于python抽取jsonl文件数据字段

基于python抽取目录下所有“jsonl”格式文件。遍历文件内某个字段进行抽取并合并。 import os import json import time from tqdm import tqdm # 需要先安装&#xff1a;pip install tqdmdef process_files():# 设置目录路径dir_path r"D:\daku\关键词识别\1623-00000…

《TCP/IP网络编程》学习笔记 | Chapter 22:重叠 I/O 模型

《TCP/IP网络编程》学习笔记 | Chapter 22&#xff1a;重叠 I/O 模型 《TCP/IP网络编程》学习笔记 | Chapter 22&#xff1a;重叠 I/O 模型理解重叠 I/O 模型重叠 I/O本章讨论的重叠 I/O 的重点不在于 I/O 创建重叠 I/O 套接字执行重叠 I/O 的 WSASend 函数进行重叠 I/O 的 WSA…

html css 网页制作成品——HTML+CSS非遗文化扎染网页设计(5页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…

飞书只有阅读权限的文档下载,飞书文件下载

wx搜索公zhong号&#xff1a;"狮心王"回复"飞书文档保存"下载chrome扩展文件 拿到扩展文件之后给chrome添加扩展

避免踩坑!查收查引常见问题解答

Q1&#xff1a;为什么我的论文刚发表&#xff0c;在查引系统中却查不到&#xff1f; 这是由于数据库存在数据延迟。SCI、SSCI等索引平台通常需要1-3个月更新数据&#xff0c;而中文数据库如CNKI也可能延迟1-2周。建议在论文发表后耐心等待&#xff0c;或直接联系数据库客服…

Centos7 Gitlab17部署

确保你的安装源正常 1.安装依赖项 sudo yum install -y curl policycoreutils-python openssh-server openssh-clients postfix 2.安装Gitlab (1)添加仓库 curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash (2) 安装Gitl…

[数据结构]1.时间复杂度和空间复杂度

这里写目录标题 1. 算法复杂度2. 时间复杂度2.1 执行次数2.2 大O渐进表示法2.3 常见时间复杂度计算eg1eg2eg3eg4eg5eg6eg7eg8eg9 3. 空间复杂度eg1eg2eg3eg4 4. 常见复杂度对比5. 复杂度练习eg1 1. 算法复杂度 衡量一个算法的好坏&#xff0c;一般是从时间空间两个维度来衡量&…