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

在Android(Kotlin)中用Chaquopy运行Quart服务时跨语言调用线程错误排查

问题根源
  1. 错误实例化Activity:Python代码中activity = MainActivity()完全不符合Android开发规范——Activity对象必须由系统框架创建和管理,手动new的实例不具备完整生命周期,也无法正常访问UI组件。
  2. 线程违规操作:Android的所有UI操作(比如调用webView.evaluateJavascript)必须在主线程执行,而Quart的after_serving回调运行在Python的后台线程中,直接调用Activity方法会触发线程检查错误。
  3. 冗余Looper调用:在启动Python服务器的后台线程中调用Looper.prepare()和Looper.loop()完全多余,反而会混淆线程模型,引发addObserver必须在主线程调用的错误。
解决方案

步骤1:传递合法的Activity实例给Python

在Kotlin中启动Python时,将当前已由系统创建的MainActivity实例作为参数传给Python模块,而非让Python手动创建。

步骤2:强制UI操作在主线程执行

修改MainActivity中的closeLoader方法,确保所有UI相关逻辑都在主线程运行。

步骤3:移除后台线程的Looper代码

Quart本身是异步框架,启动Python服务器的后台线程不需要Looper支持,直接运行即可。


修改后的代码

Kotlin代码(MainActivity.kt)

package com.example.chatterbot

import android.annotation.SuppressLint
import android.net.http.SslError
import android.os.Bundle
import android.util.Log
import android.webkit.SslErrorHandler
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.chaquo.python.Python
import com.chaquo.python.android.AndroidPlatform

class MainActivity : AppCompatActivity() {

    private val tag: String = "MainActivity"
    private lateinit var webView: WebView
    private lateinit var swipeRefreshLayout: SwipeRefreshLayout

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 在后台线程启动Python服务器,无需Looper
        Thread {
            Log.i(tag, "Starting server")
            if (!Python.isStarted()) {
                Python.start(AndroidPlatform(this))
            }
            try {
                val py = Python.getInstance()
                // 将当前Activity实例传递给Python模块
                py.getModule("app").callAttr("run", this)
            } catch (e: Exception) {
                Log.e(tag, "Error starting Python server: ${e.message}")
            }
        }.start()

        // WebView初始化
        Log.i(tag, "Starting webview")
        webView = findViewById(R.id.webview)
        webView.webViewClient = object : WebViewClient() {
            override fun onReceivedSslError(
                view: WebView?,
                handler: SslErrorHandler?,
                error: SslError?
            ) {
                handler?.proceed() // 仅测试环境使用,生产环境禁止忽略SSL错误
            }
        }
        webView.settings.javaScriptEnabled = true
        webView.loadUrl("file:///android_asset/iframe/iframe.html")
        swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout)
        swipeRefreshLayout.setOnRefreshListener {
            webView.reload()
            swipeRefreshLayout.isRefreshing = false
        }
    }

    // 确保UI操作强制在主线程执行
    fun closeLoader() {
        runOnUiThread {
            Log.i(tag, "Close loader")
            webView.evaluateJavascript("closeLoader();", null)
        }
    }
}

Python代码(app.py)

from quart import Quart, render_template

app = Quart(__name__)
activity = None  # 存储从Kotlin传递来的合法Activity实例

@app.route('/')
async def index():
    return await render_template('index.html')

@app.after_serving
async def after_startup():
    print("Server ready")
    # 调用Activity的closeLoader方法,该方法内部已保证主线程执行
    activity.closeLoader()

def run(activity_instance):
    global activity
    activity = activity_instance
    app.run(host="0.0.0.0", port=8000, debug=True)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:02:04