kotlin开发 webview如何在收到JS调用后,native返回数据给到JS

简介: 这段内容描述了在Hybrid App开发中,使用Kotlin的Compose构建的Web视图(WebView)如何通过JsBridge实现JavaScript与原生代码的交互

 

JS与native进行交互的例子


JS调用native的函数

注册JavascriptInterface




@SuppressLint("SetJavaScriptEnabled")
@Composable
fun ComposableWebView(
    url: String,
    modifier: Modifier = Modifier,
    onWebViewReady: (WebView) -> Unit = {}
) {
    val context = LocalContext.current



    Box(modifier) {


        val webView = remember { WebView(context) }
        webView.settings.javaScriptEnabled = true
        webView.settings.setCacheMode(LOAD_DEFAULT)

        val deviceId = getAndroidId(context) ?: ""
        val checkSum = getTodayCheckSum(deviceId)
        setCookie(
            context, url, mutableMapOf(
                "deviceId4Zhibo" to deviceId,
                "checkSum4Zhibo" to checkSum
            )
        )

        Log.i("xudong debug", "开始注入接口 Android")

        // 添加 JavaScript 接口
        webView.addJavascriptInterface(
            WebAppInterface(context, webView),
            "Android"
        )
        Log.i("xudong debug", "完成注入接口 Android")

        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)

                // WebView 加载完成后的回调
                onWebViewReady(webView)
            }
        }

        // 加载URL
        Log.i("xudong debug", "加载url: $url")
        webView.loadUrl(url)
        // 将 WebView 嵌入到 Compose 中
        AndroidView({ webView })
    }
}




具体接口的执行逻辑



// JavaScript 接口类
class WebAppInterface(private val context: Context, private val webView: WebView) {

    init {
        //注册订阅者
        //避免重复注册,重复注册会导致崩溃
        if (!EventBus.getDefault().isRegistered(this)) { //这里的取反别忘记了
            EventBus.getDefault().register(this)
        } else {
            Log.i("xudong debug", "请勿重复注册事件, from Composable WebView")
        }
    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onMessageEvent(event: MessageEvent) {
        // 处理事件
        Log.i("xudong debug", "监听者 webview interface, received message: ${event}")
    }

    @JavascriptInterface
    fun playAudio(message: String) {
        Toast.makeText(context, "playAudio: $message", Toast.LENGTH_SHORT).show()
        EventBus.getDefault().post(MessageEvent("playAudio", message))


        // 使用Handler的示例
        val handler = Handler(Looper.getMainLooper()) // 获取主线程的Handler
        handler.post {

            // 执行 JS 函数 alert
            webView.evaluateJavascript("javascript:window.location.href") { result ->
                // 这里处理 JavaScript 返回值
                if (result != null) {
                    Log.i("xudong debug", "执行 js return: $result")
                } else {
                    Log.i("xudong debug", "执行JS 没有返回值")
                }
            }
        }

    }

    @JavascriptInterface
    fun stopAudio() {
        Toast.makeText(context, "stopAudio", Toast.LENGTH_SHORT).show()
        EventBus.getDefault().post(MessageEvent("stopAudio"))

    }
}




重点注意:

上述代码,反向调用JS函数 window.location.href 时, 如果不使用Handler强行在主线程执行, 会遇到报错。 (在 JavaScriptInterface中的代码,是lopper线程中运行的)

这里需要特别注意。




遇到报错如何解决?

以下是不使用Handler调用webView.evaluateJavascript时遇到的报错,这里仅记录一下, 方便回顾


java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread.


询问文心一言


在Android开发中,如果你需要在非主线程(例如后台线程)上执行某些操作,并且随后需要在主线程上调用WebViewevaluateJavascript方法,你可以使用Handler或者runOnUiThread。以下是使用runOnUiThread在Kotlin中从其他线程调用WebViewevaluateJavascript方法的示例:

// 假设这是你的WebView实例  
private lateinit var webView: WebView  
  
// 在某个后台线程或异步任务中  
// ...  
  
// 需要从后台线程调用evaluateJavascript时  
runOnUiThread {  
    webView.evaluateJavascript("document.title;", { result ->  
        // 处理JavaScript返回值,这里也在主线程上  
        Log.d("MainActivity", "Page title: $result")  
    })  
}  
  
// ...  
  
// runOnUiThread是Activity的一个方法,用于在主线程上运行Runnable  
// 如果你的代码不在Activity中,你可以使用Handler来实现相同的效果  
  
// 使用Handler的示例  
val handler = Handler(Looper.getMainLooper()) // 获取主线程的Handler  
handler.post {  
    webView.evaluateJavascript("document.title;", { result ->  
        // 处理JavaScript返回值,这里也在主线程上  
        Log.d("MainActivity", "Page title: $result")  
    })  
}



请注意,runOnUiThreadActivity类的一个方法,因此你只能在Activity的上下文中调用它。如果你需要在FragmentService或其他没有runOnUiThread方法的上下文中执行此操作,你应该使用Handler

此外,请确保在调用evaluateJavascript之前WebView已经被初始化并且是可用的。如果WebView在调用evaluateJavascript时还没有被附加到界面上或者还没有完成加载,可能会导致不可预测的行为。



相关文章
|
2月前
|
Web App开发 JavaScript 前端开发
Node.js开发
Node.js开发
78 13
|
3月前
|
存储 JavaScript 前端开发
深入浅出Node.js后端开发
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将以Node.js为例,深入探讨其背后的哲学思想、核心特性以及在实际项目中的应用,旨在为读者揭示Node.js如何优雅地处理高并发请求,并通过实践案例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和思考。
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记(18) - 数据类
Kotlin教程笔记(18) - 数据类
|
3月前
|
Web App开发 开发框架 JavaScript
深入浅出Node.js后端开发
本文将带你领略Node.js的魅力,从基础概念到实践应用,一步步深入理解并掌握Node.js在后端开发中的运用。我们将通过实例学习如何搭建一个基本的Web服务,探讨Node.js的事件驱动和非阻塞I/O模型,以及如何利用其强大的生态系统进行高效的后端开发。无论你是前端开发者还是后端新手,这篇文章都会为你打开一扇通往全栈开发的大门。
|
3月前
|
Web App开发 开发框架 JavaScript
深入浅出Node.js后端开发
在这篇文章中,我们将一起探索Node.js的奇妙世界。无论你是刚接触后端开发的新手,还是希望深化理解的老手,这篇文章都适合你。我们将从基础概念开始,逐步深入到实际应用,最后通过一个代码示例来巩固所学知识。让我们一起开启这段旅程吧!
|
2月前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
本文将带领读者从零基础开始,一步步深入到Node.js后端开发的精髓。我们将通过通俗易懂的语言和实际代码示例,探索Node.js的强大功能及其在现代Web开发中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的见解和技巧,让你的后端开发技能更上一层楼。
|
3月前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
3月前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
本文将带你走进Node.js的世界,从基础到进阶,逐步解析Node.js在后端开发中的应用。我们将通过实例来理解Node.js的异步特性、事件驱动模型以及如何利用它处理高并发请求。此外,文章还会介绍如何搭建一个基本的Node.js服务器,并探讨如何利用现代前端框架与Node.js进行交互,实现全栈式开发。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
90 4
|
JavaScript 前端开发 Java
WebView与Javascript交互及JS的注入
本篇目录 前言 本篇讲解Java代码和Javascript代码的相互调用以及JS代码在Java中是如何动态的注入WebView中。一些通用配置在第一部分中说明。
1171 0
|
3月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
50 1
JavaScript中的原型 保姆级文章一文搞懂