Jetpack Compose中用evaluateJavascript()获取WebView元素失败
解决WebView中evaluateJavascript无法获取元素的问题
核心问题梳理
你的代码存在几个关键问题导致无法获取fname元素值:
- 获取元素的逻辑从未执行:
webViewCallJs方法没有被任何代码触发,evaluateJavascript根本没运行 - JS接口方法参数不合法:
@JavascriptInterface标注的方法不能接收WebView类型参数,JS无法传递该对象 - 页面未加载完成就执行JS:即使调用获取方法,页面还没加载完时元素不存在,会返回null
- ViewModel使用不规范:直接创建ViewModel实例会导致生命周期管理异常
修复方案及代码示例
1. 确保页面加载完成后执行JS
通过WebViewClient的onPageFinished方法监听页面加载完成事件,此时再调用evaluateJavascript获取元素。
2. 修正JS接口参数问题
移除WebAppInterface方法中的WebView参数,改为在页面加载完成时直接触发元素获取逻辑。
3. 规范ViewModel使用
在Compose中使用viewModel()方法配合工厂类获取ViewModel实例,确保生命周期正确管理。
修改后的完整代码
WebPageScreen及ViewModel代码
package com.close.practice.webview import android.annotation.SuppressLint import android.app.Application import android.view.ViewGroup import android.webkit.WebView import android.webkit.WebViewClient import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewmodel.compose.viewModel @SuppressLint("SetJavaScriptEnabled") @Composable fun WebPageScreen(urlToRender: String) { val elementValue = remember { mutableStateOf("MainActivity Before") } val viewModel: SomeViewModel = viewModel(factory = SomeViewModelFactory(application(), elementValue)) Column { Text(text = "First Show MAIN Before: ${elementValue.value}") AndroidView( modifier = Modifier.height(200.dp), factory = { context -> WebView(context).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) // 页面加载完成后调用获取元素方法 viewModel.getElementById(this@apply, "fname") } } settings.javaScriptEnabled = true loadUrl(urlToRender) } }, update = { it.loadUrl(urlToRender) } ) Text(text = "First Show MAIN After: ${elementValue.value}") } } class SomeViewModel(application: Application, private val elementValue: MutableState<String>) : AndroidViewModel(application) { fun getElementById(webViewObj: WebView, elementId: String) { webViewObj.evaluateJavascript( "(function() { return document.getElementById('$elementId')?.value || '元素未找到'; })();" ) { value -> // 移除JS返回值自带的双引号 elementValue.value = value.replace("\"", "") } } } // ViewModel工厂类,用于传递参数 class SomeViewModelFactory( private val application: Application, private val elementValue: MutableState<String> ) : androidx.lifecycle.ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : androidx.lifecycle.ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(SomeViewModel::class.java)) { return SomeViewModel(application, elementValue) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
MainActivity代码
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PracticeTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { WebPageScreen("https://www.w3schools.com/html/html_forms.asp") } } } } }
关键修复点说明
- 页面加载完成触发JS:通过
onPageFinished确保页面完全渲染后再执行元素获取,避免元素未存在导致的null问题。 - 处理JS返回值:
evaluateJavascript返回的结果会带有双引号,需要替换后才能得到真实值;同时增加空安全判断,避免元素不存在时抛出错误。 - 规范ViewModel实例化:通过工厂类传递参数,使用
viewModel()方法获取实例,确保ViewModel生命周期与页面一致。 - 简化无用逻辑:移除了未被调用的JS接口,直接在Android端触发元素获取,简化代码结构。
内容的提问来源于stack exchange,提问作者Bassel Habib
相关产品推荐
相关产品推荐

