You need to enable JavaScript to run this app. 安卓Kivy应用蓝牙发送报错及InputStream多线程读取问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

安卓Kivy应用蓝牙发送报错及InputStream多线程读取问题咨询

蓝牙功能报错与实现修复

发送数据报错的根源

OutputStream.write()仅支持接收Java byte[]类型参数,原实现中已完成转换的Python字节对象,被通过字符串格式化包装成了普通字符串传入,直接触发类型不兼容报错。

修正要点

  • 字符串转字节数组的逻辑本身无错误,不要对已转换的字节对象再做字符串包装
  • Python bytes对象可被jnius自动映射为Java可识别的byte数组,可直接传入write方法
  • 统一发送逻辑,不要混用pyserial与Java层socket的流对象,避免跨平台适配混乱
    修正后的坐标发送代码:
def SendCoordinates(self, XCoordinate, YCoordinate):
    # 直接在原始字符串层拼接换行符,不要在字节层做字符串格式化
    StrPositionData = f"{XCoordinate}, {YCoordinate}\n"
    ByteString = bytes(StrPositionData, 'utf-8')
    # 直接传入转换完成的字节数组,不额外做字符串包装
    self.SendSerialData.write(ByteString)
    self.SendSerialData.flush()

同步修正触摸抬起时的停止指令发送逻辑:安卓端不存在pyserial实例的bluetooth_device对象,统一使用已初始化的输出流发送指令:

def on_touch_up(self,touch):
    if self.BluetoothInit == 1:
        stop_cmd = bytes("STOP\n", 'utf-8')
        self.SendSerialData.write(stop_cmd)
        self.SendSerialData.flush()

后台线程读取蓝牙返回数据的实现

Java InputStream.read()为阻塞式调用,必须放在非UI线程运行,否则会完全卡死Kivy界面。Python原生threading模块即可实现该需求,无需额外依赖。

实现流程

  • 提前在类属性中定义线程运行标志、线程实例,避免连接断开后线程无意义空转
  • 编写独立的后台读取循环,分配固定大小的缓冲区作为read()方法的入参
  • 子线程无法直接修改Kivy UI组件属性,读到数据后通过Clock.schedule_once将数据投递回主线程完成界面更新
    核心实现代码:
import threading

class HomeScreen(Screen):
    # 新增线程相关类属性
    receive_thread = None
    is_running = False

    # 后台线程运行的读取循环,严禁直接操作UI组件
    def _bluetooth_read_loop(self):
        # 分配1024字节缓冲区,匹配Java read()的入参要求
        buffer = bytearray(1024)
        while self.is_running:
            try:
                # 调用InputStream.read(),返回实际读取到的有效字节长度
                bytes_read = self.ReceiveSerialData.read(buffer)
                if bytes_read > 0:
                    # 截取有效长度的字节转成可读字符串
                    received_str = buffer[:bytes_read].decode('utf-8', errors='ignore')
                    # 将数据投递回主线程做界面刷新
                    Clock.schedule_once(
                        lambda dt, data=received_str: self._update_voltage_display(data),
                        0
                    )
            except Exception:
                # 连接异常时自动退出读取循环
                self.is_running = False
                break

    # 主线程执行的UI更新方法
    def _update_voltage_display(self, received_data):
        # 将读取到的内容更新到电压输入框
        self.mvoltage.text = received_data.strip()

在蓝牙连接流程中,等socket建立、输入输出流初始化完成后,启动读取线程:

def ConnectBluetooth(self):
    # Windows端pyserial分支逻辑保持不变
    try:
        # ...原有安卓端配对设备、匹配HC-05、初始化socket的逻辑保持不变
        socket.connect()
        # 启动后台读取线程
        self.is_running = True
        self.receive_thread = threading.Thread(
            target=self._bluetooth_read_loop,
            daemon=True # 设为*守护线程*,应用退出时自动销毁
        )
        self.receive_thread.start()
        self.BluetoothInit = 1
    except Exception:
        raise Exception('无法完成蓝牙连接')

原代码存在的其他逻辑/语法问题

  • 属性与方法重名覆盖:原实现将ReceiveSerialData同时作为InputStream实例属性、读取方法名使用,实例赋值后会直接覆盖类方法,后续无法正常调用读取逻辑,必须将属性名与方法名严格区分
  • 缩进错误:原代码中if self.BluetoothInit == 1:下的读取语句未做缩进,直接触发Python语法报错
  • UI线程阻塞风险:蓝牙socket.connect()本身也是阻塞调用,建议将连接流程也放到后台线程执行,避免点击连接按钮时界面卡顿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.05 02:36:07