0

0

Kivy UrlRequest 回调阻塞 GUI 的原因与正确解决方案

心靈之曲

心靈之曲

发布时间:2026-01-20 14:32:31

|

436人浏览过

|

来源于php中文网

原创

Kivy UrlRequest 回调阻塞 GUI 的原因与正确解决方案

kivy 的 `urlrequest` 回调(如 `on_success`)默认在主线程中执行,即使请求本身异步,回调中的耗时操作仍会阻塞 ui;解决方法是将回调逻辑移出主线程,或确保其轻量、非阻塞,并通过 `@mainthread` 显式控制 ui 更新时机。

在 Kivy 应用中,UrlRequest 常被误认为“完全异步”——实际上,它底层基于 urllib 或 requests(取决于配置),网络 I/O 是异步的,但所有回调函数(on_success、on_error、on_failure)默认在主线程(即 GUI 线程)中同步执行。这意味着:哪怕你在后台线程中调用了 UrlRequest(...),只要回调里包含 time.sleep(5)、JSON 解析、数据库写入或复杂计算等耗时操作,GUI 就会卡死,MDSpinner 停止旋转、界面无响应。

你原代码中的关键问题在于:

  • mycallback 被直接用作 on_success,且内部含 time.sleep(5);
  • 尽管 sync_thread 在新线程运行,但 UrlRequest 的回调不继承该线程上下文,始终回归主线程;
  • @mainthread 装饰器只对 主动从子线程调用 UI 方法 有效(如 kill_spinner),无法“拯救”已在主线程中执行的阻塞回调。

✅ 正确做法是:将耗时处理逻辑移出回调,在子线程中完成,仅用回调触发轻量任务(如调度、标记状态),再通过 Clock.schedule_once 或 @mainthread 安全更新 UI

以下是推荐重构方案:

Simplified
Simplified

AI写作、平面设计、编辑视频和发布内容。专为团队打造。

下载
from kivy.network.urlrequest import UrlRequest
from functools import partial
from kivy.clock import Clock, mainthread
from threading import Thread
from kivymd.uix.screen import MDScreen
import json
import time

class MyScreen(MDScreen):
    def on_button(self):
        self.ids.spinner.active = True
        # 启动后台下载流程(不阻塞)
        t = Thread(target=self.sync_thread, daemon=True)
        t.start()

    def sync_thread(self):
        # ✅ 每个 download 应独立处理,避免串行阻塞
        urls = ["https://httpbin.org/json", "https://httpbin.org/delay/1"]
        for url in urls:
            # 注意:此处 download 是普通函数,非绑定方法(需传 self)
            self.download(url)

    def download(self, url):
        # 回调只做最小调度:把数据和上下文传给后台处理
        def on_success(req, result):
            # ? 错误:在此处解析/处理会阻塞主线程
            # data = json.loads(result.decode())  # ❌ 危险!

            # ✅ 正确:启动子线程处理,回调仅触发调度
            worker = Thread(
                target=self.process_response,
                args=(url, result),
                daemon=True
            )
            worker.start()

        def on_failure(req, result):
            print(f"Download failed for {url}")
            self._finish_download_if_all_done()

        def on_error(req, error):
            print(f"Network error for {url}: {error}")
            self._finish_download_if_all_done()

        UrlRequest(
            url=url,
            on_success=on_success,
            on_failure=on_failure,
            on_error=on_error,
            timeout=10,
            req_headers={"User-Agent": "KivyApp/1.0"}
        )

    def process_response(self, url, raw_data):
        """在子线程中执行所有耗时操作"""
        try:
            # 模拟耗时解析(可替换为 pandas 处理、SQL 写入等)
            time.sleep(3)  # ⚠️ 此 sleep 不影响 GUI
            data = json.loads(raw_data.decode())

            # ✅ 处理完成后,安全更新 UI(必须用 @mainthread)
            Clock.schedule_once(
                lambda dt: self.on_response_processed(url, data),
                0
            )
        except Exception as e:
            print(f"Processing error for {url}: {e}")

    @mainthread
    def on_response_processed(self, url, data):
        """仅在此处更新 UI 元素(安全)"""
        print(f"✅ Processed {url}, got {len(data)} keys")
        # 例如:更新列表、保存到本地、刷新表格...

    def _finish_download_if_all_done(self):
        # 可配合计数器/事件标志判断是否全部完成
        pass

    @mainthread
    def kill_spinner(self):
        self.ids.spinner.active = False

? 关键要点总结

  • UrlRequest 回调 ≠ 子线程执行,它是主线程回调,务必保持轻量;
  • 所有 CPU 密集型、I/O 密集型或 time.sleep() 操作,必须放入 Thread 或 asyncio.to_thread()(Kivy 2.3+)中;
  • UI 更新(如 self.ids.spinner.active = False)只能在主线程进行,使用 @mainthread 或 Clock.schedule_once 是唯一安全方式;
  • 避免在回调中直接调用 self.kill_spinner() —— 它应在所有下载逻辑真正收尾后触发(例如用 threading.Event 或 concurrent.futures.as_completed 统一管理完成信号);
  • 若需链式处理(下载 → 解析 → 存储 → 刷新),建议改用 aiohttp + asyncio(需 Kivy 异步支持补丁)或 kivy.clock.CyClockBase 配合 ThreadPoolExecutor 提升可维护性。

遵循以上模式,你的 Spinner 将全程流畅旋转,用户交互零卡顿。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

413

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

533

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

310

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

75

2025.09.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

Java 并发编程高级实践
Java 并发编程高级实践

本专题深入讲解 Java 在高并发开发中的核心技术,涵盖线程模型、Thread 与 Runnable、Lock 与 synchronized、原子类、并发容器、线程池(Executor 框架)、阻塞队列、并发工具类(CountDownLatch、Semaphore)、以及高并发系统设计中的关键策略。通过实战案例帮助学习者全面掌握构建高性能并发应用的工程能力。

61

2025.12.01

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

352

2023.06.29

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.4万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号