Unity开发Android蓝牙插件首次连接正常后续失效问题求助
解决Unity Android蓝牙插件首次连接正常、后续失效的问题
从你的代码和描述来看,问题主要出在静态资源未正确重置、连接逻辑未清理旧状态,加上一些蓝牙操作的不规范导致的。下面是具体的问题分析和修复方案:
核心问题分析
静态变量的资源泄漏
你的Uplugin类里sock、remoteDevice、remoteUUID这些都是静态变量,首次连接后,即使断开(或者设备重启蓝牙),这些变量仍然持有旧的引用。下次调用ConnectBluetooth时,会直接复用旧的socket对象,而这个socket可能已经处于无效状态,但isConnected()因为缓存的状态返回true,实际并没有建立新连接。蓝牙开启的死循环阻塞
代码里用while(true)等待蓝牙开启,这会阻塞当前线程(可能是Unity的主线程),导致后续操作异常,甚至引发ANR(应用无响应),同时也会影响蓝牙状态的正确检测。UUID获取的不可靠性
直接取list[0]作为连接的UUID,可能不是蓝牙模块提供服务的正确UUID。大多数蓝牙串口模块使用标准的SPP UUID:00001101-0000-1000-8000-00805F9B34FB,硬编码这个UUID会比动态获取更可靠。连接前未清理旧连接
每次调用连接前,没有检查是否已有活跃的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端调用建议
- 先调用
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); - 调用
StartBluetooth()开启蓝牙并查找设备,等待广播回调后再调用ConnectBluetooth()。 - 在Unity退出场景或应用时,调用
onDestroy()清理资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者jibang
相关产品推荐
相关产品推荐

