Android WebView 与 H5 双向通信实现详解

server/2024/12/27 16:16:27/

Android WebView 与 H5 双向通信实现详解

背景介绍

在 Android 原生应用中,WebView 与 H5 页面的双向通信是一个常见需求。本文将详细介绍如何实现一个完整的通信方案。

核心实现

1. H5 端实现

与之前类似,但需要针对 Android 环境做适配:

window.ntalkCallbacks = {callbacks: {},register: function(type, successCallback, errorCallback) {const callbackId = type + Date.now().toString();// Promise方式if (!successCallback && !errorCallback) {const promise = new Promise((resolve, reject) => {this.callbacks[callbackId] = {success: resolve,error: reject,};});return {promise,callbackId};}// 回调方式this.callbacks[callbackId] = {success: successCallback, error: errorCallback,};return callbackId;},execute: function(callbackId, type, data) {const callback = this.callbacks[callbackId];if (callback) {if (type === 'success') {callback.success && callback.success(data);} else {callback.error && callback.error(data);}delete this.callbacks[callbackId];}}
};// Android桥接对象
window.ntalkNative = {getUserInfo: function(successCallback, errorCallback) {const callbackId = ntalkCallbacks.register('getUserInfo', successCallback, errorCallback);// 调用Android注入的对象window.androidBridge.postMessage(JSON.stringify({type: 'getUserInfo',callbackId,data: null}));},getUserInfoAsync: async function() {const { promise, callbackId } = ntalkCallbacks.register('getUserInfo');window.androidBridge.postMessage(JSON.stringify({type: 'getUserInfo',callbackId,data: null}));return promise;}
};

2. Android 端实现

class WebViewBridge(private val webView: WebView) {// JavaScript接口类@JavascriptInterfaceclass AndroidBridge(private val handler: Handler, private val bridge: WebViewBridge) {@JavascriptInterfacefun postMessage(message: String) {// 切换到主线程处理handler.post {bridge.handleMessage(message)}}}init {setupWebView()}private fun setupWebView() {webView.apply {settings.apply {javaScriptEnabled = truedomStorageEnabled = true}// 注入JavaScript接口addJavascriptInterface(AndroidBridge(Handler(Looper.getMainLooper()), this@WebViewBridge),"androidBridge")}}private fun handleMessage(message: String) {try {val jsonObject = JSONObject(message)val type = jsonObject.getString("type")val callbackId = jsonObject.getString("callbackId")val data = jsonObject.opt("data")when (type) {"getUserInfo" -> handleGetUserInfo(callbackId)"setAppCache" -> handleSetAppCache(callbackId, data)else -> sendCallback(callbackId, "error", "Unknown type")}} catch (e: Exception) {Log.e("WebViewBridge", "Handle message error", e)}}private fun sendCallback(callbackId: String, type: String, data: Any?) {val script = """window.ntalkCallbacks.execute('$callbackId','$type',${Gson().toJson(data)});""".trimIndent()webView.evaluateJavascript(script, null)}private fun handleGetUserInfo(callbackId: String) {try {// 示例:获取用户信息val userInfo = mapOf("userId" to "123","name" to "张三","avatar" to "https://example.com/avatar.png")sendCallback(callbackId, "success", userInfo)} catch (e: Exception) {sendCallback(callbackId, "error", e.message)}}private fun handleSetAppCache(callbackId: String, data: Any?) {try {// 处理缓存逻辑sendCallback(callbackId, "success", true)} catch (e: Exception) {sendCallback(callbackId, "error", e.message)}}
}

3. Activity 中使用

class WebViewActivity : AppCompatActivity() {private lateinit var webView: WebViewprivate lateinit var webViewBridge: WebViewBridgeoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_webview)webView = findViewById(R.id.webView)webViewBridge = WebViewBridge(webView)setupWebView()}private fun setupWebView() {webView.apply {webViewClient = object : WebViewClient() {override fun onPageFinished(view: WebView?, url: String?) {super.onPageFinished(view, url)// 页面加载完成后的处理}}webChromeClient = object : WebChromeClient() {override fun onConsoleMessage(message: ConsoleMessage): Boolean {Log.d("WebView", "${message.message()} -- From line ${message.lineNumber()} of ${message.sourceId()}")return true}}// 加载网页loadUrl("https://example.com")}}override fun onDestroy() {webView.destroy()super.onDestroy()}
}

4. 布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><WebViewandroid:id="@+id/webView"android:layout_width="match_parent"android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>

实现要点

  1. 安全性考虑

    • 使用 @JavascriptInterface 注解标记可供 JS 调用的方法
    • 处理跨线程调用问题
    • 注意 WebView 内存泄漏问题
  2. 性能优化

    • 使用 Handler 确保在主线程处理 UI 操作
    • 避免频繁的 JSON 解析和字符串操作
    • 合理管理 WebView 生命周期
  3. 调试功能

    • 实现 WebChromeClient 以捕获 console 日志
    • 添加详细的错误日志
    • 支持开发环境调试
  4. 兼容性处理

    • 处理不同 Android 版本的兼容性问题
    • 确保 JavaScript 接口注入的稳定性
    • 处理 WebView 内核版本差异

最佳实践建议

  1. 添加通信超时机制
  2. 实现错误重试策略
  3. 添加网络状态监听
  4. 实现页面加载进度提示
  5. 处理 WebView 内存管理
  6. 添加安全域名白名单
  7. 实现离线缓存策略

注意事项

  1. 在 AndroidManifest.xml 中添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />
  1. 在 proguard-rules.pro 中添加混淆规则:
-keepclassmembers class com.example.webview.WebViewBridge$AndroidBridge {public *;
}
  1. 注意处理 WebView 的生命周期,避免内存泄漏。

这个实现方案提供了一个完整的 Android WebView 与 H5 通信框架,可以根据实际项目需求进行扩展和优化。


http://www.ppmy.cn/server/153660.html

相关文章

ensp 基于EASY IP的公司出口链路配置

Easy IP Easy IP技术是NAPT的一种简化情况。Easy IP无须建立公网IP地址资源池&#xff0c;因为Easy IP只会用到一个公网IP地址&#xff0c;该IP地址就是路由器R连接公网的出口IP地址。Easy IP也会建立并维护一张动态地址及端口映射表&#xff0c;并且Easy IP会将这张表中的公网…

Linux shell脚本用于常见图片png、jpg、jpeg、webp、tiff格式批量转PDF文件

Linux Debian12基于ImageMagick图像处理工具编写shell脚本用于常见图片png、jpg、jpeg、webp、tiff格式批量转PDF文件&#xff0c;”多个图片分开生成多个PDF文件“或者“多个图片合并生成一个PDF文件” BiliBili视频链接&#xff1a; Linux shell脚本对常见图片格式批量转换…

JavaEE 导读与环境配置

JavaEE 介绍 Java EE(Java Platform Enterprise Edition), Java 平台企业版. 是JavaSE的扩展, ⽤于解决企业级的开发需求, 所以也可以称之为是⼀组⽤于企业开发的Java技术标准. 所以, 学习JavaEE主要是学习Java在企业中如何应⽤ 框架学习 Java EE 课程共涉及4个框架的学习: Spr…

DeepSeek V3:新一代开源 AI 模型,多语言编程能力卓越

DeepSeek V3 横空出世&#xff0c;以其强大的多语言编程能力和先进的技术架构&#xff0c;引发了业界的广泛关注。这款最新的 AI 模型不仅在性能上实现了质的飞跃&#xff0c;还采用了开源策略&#xff0c;为广大开发者提供了更广阔的探索空间。本文将深入解析 DeepSeek V3 的技…

视频监控平台:Liveweb视频汇聚融合平台智慧安防视频监控应用方案

Liveweb是一款功能强大、灵活部署的安防视频监控平台&#xff0c;支持多种主流标准协议&#xff0c;包括GB28181、RTSP/Onvif、RTMP等&#xff0c;同时兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统安防监控功能&#xff0c;还支持接入AI智能分析&#…

PVE虚拟化平台之开启温度显示方法

PVE虚拟化平台之开启温度显示方法 一、PVE平台介绍1.1 PVE简介1.2 PVE特点1.3 PVE主要使用场景二、本次实践介绍2.1 环境介绍2.2 本次实践简介2.3 检查PVE状态三、pvetools介绍3.1 pvetool简介3.2 功能概览四、使用pvetools工具4.1 下载pvetools项目4.2 执行脚本五、一键安装脚…

在WSL的系统中配置免密和GitHub传输数据(SSH)

在 WSL&#xff08;Windows Subsystem for Linux&#xff09;系统中配置免密与 GitHub 传输数据&#xff0c;主要包括设置 SSH 密钥对、将公钥添加到 GitHub 账户以及确保可以通过 WSL 正常使用这些密钥。以下是详细的步骤&#xff1a; 1. 检查现有 SSH 密钥 首先&#xff0c…

网页数据的解析提取之xpath

上一篇博客我们实现了一个最基本的爬虫&#xff0c;但提取页面信息时使用的是正则表达式&#xff0c;过程比较烦琐&#xff0c;而且万一有地方写错了&#xff0c;可能会导致匹配失败&#xff0c;所以使用正则表达式提取页面信息多少还是有些不方便。 对于网页的节点来说&#…