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

Unity开发Android蓝牙插件首次连接正常后续失效问题求助

解决Unity Android蓝牙插件首次连接正常、后续失效的问题

从你的代码和描述来看,问题主要出在静态资源未正确重置、连接逻辑未清理旧状态,加上一些蓝牙操作的不规范导致的。下面是具体的问题分析和修复方案:

核心问题分析

  1. 静态变量的资源泄漏
    你的Uplugin类里sock、remoteDevice、remoteUUID这些都是静态变量,首次连接后,即使断开(或者设备重启蓝牙),这些变量仍然持有旧的引用。下次调用ConnectBluetooth时,会直接复用旧的socket对象,而这个socket可能已经处于无效状态,但isConnected()因为缓存的状态返回true,实际并没有建立新连接。

  2. 蓝牙开启的死循环阻塞
    代码里用while(true)等待蓝牙开启,这会阻塞当前线程(可能是Unity的主线程),导致后续操作异常,甚至引发ANR(应用无响应),同时也会影响蓝牙状态的正确检测。

  3. UUID获取的不可靠性
    直接取list[0]作为连接的UUID,可能不是蓝牙模块提供服务的正确UUID。大多数蓝牙串口模块使用标准的SPP UUID:00001101-0000-1000-8000-00805F9B34FB,硬编码这个UUID会比动态获取更可靠。

  4. 连接前未清理旧连接
    每次调用连接前,没有检查是否已有活跃的socket连接,也没有关闭旧的socket,导致新连接无法建立。

修复后的代码示例

package com.jiho.myplugin;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.Toast;
import com.unity3d.player.UnityPlayer;
import java.io.IOException;
import java.util.Set;
import java.util.UUID;

public class Uplugin {
    private static Uplugin m_instance;
    private Context context;
    private BluetoothAdapter myBluetoothAdapter;
    private UUID remoteUUID;
    private BluetoothSocket sock;
    private BluetoothDevice remoteDevice;
    private String remoteAddress;
    // 蓝牙开启的广播接收器
    private BroadcastReceiver bluetoothStateReceiver;

    public static Uplugin instance() {
        if (m_instance == null) {
            m_instance = new Uplugin();
        }
        return m_instance;
    }

    // 初始化上下文,建议在Unity启动时调用
    public void init(Context ct) {
        this.context = ct;
        myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        // 注册蓝牙状态广播
        registerBluetoothReceiver();
    }

    // 注册蓝牙状态变化的广播监听
    private void registerBluetoothReceiver() {
        bluetoothStateReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                    if (state == BluetoothAdapter.STATE_ON) {
                        // 蓝牙开启后自动查找配对设备
                        findPairedDevice();
                    }
                }
            }
        };
        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(bluetoothStateReceiver, filter);
    }

    // Unity调用的开启蓝牙方法(注意方法名要和Unity调用一致)
    public void StartBluetooth() {
        if (myBluetoothAdapter == null) {
            Toast.makeText(context, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
            return;
        }
        if (!myBluetoothAdapter.isEnabled()) {
            // 发送开启蓝牙的请求,而不是直接enable()(部分设备需要用户授权)
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            UnityPlayer.currentActivity.startActivityForResult(enableBtIntent, 1);
        } else {
            findPairedDevice();
        }
    }

    // 查找配对的TANDA设备
    private void findPairedDevice() {
        Set<BluetoothDevice> pairedDevices = myBluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                if (device.getName().contains("TANDA")) {
                    remoteDevice = device;
                    remoteAddress = device.getAddress();
                    // 使用标准SPP UUID,比动态获取更可靠
                    remoteUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
                    Toast.makeText(context, "找到配对设备: " + device.getName(), Toast.LENGTH_SHORT).show();
                    return;
                }
            }
            Toast.makeText(context, "未找到配对的TANDA设备", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "无已配对设备", Toast.LENGTH_SHORT).show();
        }
    }

    // Unity调用的连接方法
    public void ConnectBluetooth() {
        // 先关闭旧连接
        disconnect();

        if (remoteDevice == null) {
            Toast.makeText(context, "请先找到配对设备", Toast.LENGTH_SHORT).show();
            return;
        }

        try {
            // 创建新的socket
            sock = remoteDevice.createInsecureRfcommSocketToServiceRecord(remoteUUID);
            myBluetoothAdapter.cancelDiscovery();
            sock.connect();
            Toast.makeText(context, "连接成功", Toast.LENGTH_SHORT).show();
            // 这里可以向Unity发送连接成功的回调
        } catch (IOException e) {
            Toast.makeText(context, "连接失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            // 连接失败后清理socket
            disconnect();
        }
    }

    // 断开连接并清理资源
    public void disconnect() {
        if (sock != null) {
            try {
                sock.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sock = null;
        }
    }

    // 检查当前连接状态
    public boolean isConnected() {
        return sock != null && sock.isConnected();
    }

    // 销毁时注销广播,避免内存泄漏
    public void onDestroy() {
        if (bluetoothStateReceiver != null) {
            context.unregisterReceiver(bluetoothStateReceiver);
        }
        disconnect();
    }
}

关键修复点说明

  • 移除静态变量:把sock、remoteDevice等改为实例变量,避免跨调用的资源泄漏,每次创建Uplugin实例都会重置状态。
  • 广播监听蓝牙状态:替换死循环等待,用广播接收器监听蓝牙开启事件,避免线程阻塞。
  • 硬编码标准UUID:大多数蓝牙串口模块使用00001101-0000-1000-8000-00805F9B34FB作为SPP服务UUID,比动态获取第一个UUID更可靠。
  • 连接前清理旧连接:每次调用ConnectBluetooth前先调用disconnect()关闭旧的socket,确保新连接能正常建立。
  • 规范蓝牙开启流程:使用ACTION_REQUEST_ENABLE请求用户开启蓝牙,而不是直接调用enable(),符合Android权限规范。

Unity端调用建议

  1. 先调用init方法初始化上下文:
    AndroidJavaClass pluginClass = new AndroidJavaClass("com.jiho.myplugin.Uplugin");
    AndroidJavaObject pluginInstance = pluginClass.CallStatic<AndroidJavaObject>("instance");
    AndroidJavaObject unityContext = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
    pluginInstance.Call("init", unityContext);
    
  2. 调用StartBluetooth()开启蓝牙并查找设备,等待广播回调后再调用ConnectBluetooth()。
  3. 在Unity退出场景或应用时,调用onDestroy()清理资源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:54:50