Jetpack Compose中如何在WebView创建逻辑外调用evaluateJavascript?
解决方案
核心思路采用单向数据流+事件驱动的实现方式,全程不在Composable以外持有WebView引用,通过ViewModel暴露可观察的JS调用事件,Compose侧监听事件触发WebView的API调用,完全符合官方开发规范。
具体实现步骤
- 步骤1:改造ViewModel,移除WebView引用,替换为JS调用事件流
class SomeViewModel(application: Application): AndroidViewModel(application) { // 定义JS调用事件流,replay=0表示为一次性事件,不会重复消费 private val _jsCommandFlow = MutableSharedFlow<String>(extraBufferCapacity = 1) val jsCommandFlow: SharedFlow<String> = _jsCommandFlow fun callJs(script: String = "updateSomething(123)") { // 向事件流发送JS调用请求 viewModelScope.launch { _jsCommandFlow.emit(script) } } fun createWebAppInterface() : WebAppInterface { // 直接用applicationContext避免持有Activity上下文 return WebAppInterface(getApplication(), ::callJs) } // 移除inner修饰符,避免隐式持有ViewModel引用 class WebAppInterface( private val context: Context, private val onCallJs: () -> Unit ) { @JavascriptInterface fun webViewCallJs() { // Web端触发JS调用,直接走事件流 onCallJs() } } }
- 步骤2:改造Composable,监听事件流触发WebView操作
@SuppressLint("SetJavaScriptEnabled") @Composable fun WebPageScreen(urlToRender: String, someViewModel: SomeViewModel) { AndroidView( factory = { WebView(it).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) webViewClient = WebViewClient() settings.javaScriptEnabled = true addJavascriptInterface(someViewModel.createWebAppInterface(), "Android") loadUrl(urlToRender) } }, update = { webView -> // 监听JS调用事件流,有新事件时执行WebView API LaunchedEffect(Unit) { someViewModel.jsCommandFlow.collect { script -> webView.evaluateJavascript(script, null) } } webView.loadUrl(urlToRender) } ) }
实现说明
- 全程没有在Composable以外存储WebView引用,完全符合Jetpack Compose的官方规范,不会出现上下文泄漏问题
- 双向调用需求完全覆盖:不管是ViewModel内部业务逻辑调用
callJs(),还是Web端点击按钮触发webViewCallJs(),都会通过事件流通知Compose侧执行JS调用,逻辑统一 - 配置变更(比如屏幕旋转)时,ViewModel不会销毁,事件流会正常工作,新的WebView实例创建后会自动监听事件流,不会丢失调用请求
内容的提问来源于stack exchange,提问作者duckduck
相关产品推荐
相关产品推荐

