JS与native进行交互的例子
JS调用native的函数
注册JavascriptInterface
"SetJavaScriptEnabled") ( 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") } } threadMode = ThreadMode.MAIN) ( fun onMessageEvent(event: MessageEvent) { // 处理事件 Log.i("xudong debug", "监听者 webview interface, received message: ${event}") } 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 没有返回值") } } } } 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开发中,如果你需要在非主线程(例如后台线程)上执行某些操作,并且随后需要在主线程上调用WebView
的evaluateJavascript
方法,你可以使用Handler
或者runOnUiThread
。以下是使用runOnUiThread
在Kotlin中从其他线程调用WebView
的evaluateJavascript
方法的示例:
// 假设这是你的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") }) }
请注意,runOnUiThread
是Activity
类的一个方法,因此你只能在Activity
的上下文中调用它。如果你需要在Fragment
、Service
或其他没有runOnUiThread
方法的上下文中执行此操作,你应该使用Handler
。
此外,请确保在调用evaluateJavascript
之前WebView
已经被初始化并且是可用的。如果WebView
在调用evaluateJavascript
时还没有被附加到界面上或者还没有完成加载,可能会导致不可预测的行为。