You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin:通过JavascriptInterface修改WebView URL报错求助

Android WebView通过JavascriptInterface调用loadUrl报错解决

问题描述

使用Kotlin开发Android WebView应用,尝试通过JavascriptInterface调用loadUrl修改WebView的URL时,出现错误:Uncaught Error: Java exception was raised during method invocation。调用返回字符串的JavascriptInterface方法可以正常工作,但执行webview.loadUrl就触发异常。

相关代码

WebAppInterfaced.kt

package com.example.barangkalearninghub

import android.content.Context
import android.webkit.JavascriptInterface
import android.webkit.WebView

class WebAppInterfaced(val webview: WebView) {
    @JavascriptInterface
    fun PageRedirect(newUri: String) {
        webview.loadUrl(newUri)
    }
}

MainActivity.kt

package com.example.barangkalearninghub

import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

@SuppressLint("SetJavaScriptEnabled")
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        val webView: WebView = findViewById(R.id.webViewMain)
        webView.webChromeClient = WebChromeClient()
        WebView.setWebContentsDebuggingEnabled(true)
        val jsInterface: WebAppInterfaced = WebAppInterfaced(webView);
        webView.addJavascriptInterface(jsInterface, "WebAppInterfaced")

        webView.settings.databaseEnabled = true
        webView.settings.allowFileAccess = true
        webView.settings.javaScriptEnabled = true
        webView.settings.domStorageEnabled = true
        webView.settings.useWideViewPort = true
        webView.loadUrl("file:///android_asset/index.html")
    }
}

assets/index.html

window.addEventListener('load', function(){
    var p = $("#demo");
    p.html("hello jquery android");
    setTimeout(function(){
       WebAppInterfaced.PageRedirect("dashboard.html");
    }, 5000);
});

解决方案

核心原因

JavascriptInterface标注的方法运行在WebView的后台线程(非主线程),而Android中所有UI组件的操作必须在主线程执行,直接调用webview.loadUrl会触发CalledFromWrongThreadException,这就是报错的根源。

修改代码

将loadUrl操作切换到主线程执行,同时确保传入的URL是完整的asset路径(避免相对路径解析问题):

修改后的WebAppInterfaced.kt

package com.example.barangkalearninghub

import android.webkit.JavascriptInterface
import android.webkit.WebView

class WebAppInterfaced(val webview: WebView) {
    @JavascriptInterface
    fun PageRedirect(newUri: String) {
        // 用WebView的post方法将操作切换到主线程
        webview.post {
            // 拼接完整的asset文件路径
            val fullUrl = "file:///android_asset/$newUri"
            webview.loadUrl(fullUrl)
        }
    }
}

另一种实现(通过Activity主线程)

如果需要依赖Activity上下文,也可以通过runOnUiThread实现:

package com.example.barangkalearninghub

import android.webkit.JavascriptInterface
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity

class WebAppInterfaced(val activity: AppCompatActivity, val webview: WebView) {
    @JavascriptInterface
    fun PageRedirect(newUri: String) {
        activity.runOnUiThread {
            val fullUrl = "file:///android_asset/$newUri"
            webview.loadUrl(fullUrl)
        }
    }
}

对应MainActivity中的初始化:

val jsInterface = WebAppInterfaced(this, webView)

验证说明

修改后,Javascript调用PageRedirect时,loadUrl操作会在主线程执行,既符合Android的UI线程规则,又能正确加载assets目录下的目标HTML文件,不会再触发异常。

内容的提问来源于stack exchange,提问作者exejee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:05:20