Android Studio蓝牙设备连接失败:socket closed问题求助
解决蓝牙连接失败(socket closed)并实现数据接收的方案
Hey there! 作为刚入行5天的Android开发者,碰到蓝牙连接问题太正常了——蓝牙这块确实有不少细节要注意。咱们一点点拆解你的代码问题,帮你搞定连接和数据接收~
先说说你的代码里的核心问题
1. BluetoothConnection类的关键错误
- 连接操作放错了地方:你在构造函数里直接调用
mmSocket.connect(),这是阻塞操作,会导致调用构造函数的线程(大概率是主线程)卡顿甚至ANR。而且连接失败后你直接关闭了socket,后续run()方法再尝试读取流时,自然会报“socket closed”。 - 未正确取消蓝牙发现:你注释掉了
mmAdapter.cancelDiscovery(),而且mmAdapter根本没初始化,蓝牙发现会占用大量资源,严重影响连接成功率。 - 连接失败后仍尝试获取流:连接失败关闭socket后,你还继续执行
tmpIn = mmSocket.getInputStream(),这时候socket已经关闭,必然抛出异常,导致mmInStream为null,后续读数据完全无法进行。
2. DeviceList类的缺失逻辑
- BluetoothAdapter未初始化:
BA变量在onCreate里没赋值,直接调用BA.getBondedDevices()会空指针。 - PopupMenu未完善:你创建了PopupMenu,但没添加任何菜单选项,也没调用
popup.show(),用户根本没法选择要连接的设备。 - 未触发连接逻辑:选中设备后没有实例化
BluetoothConnection并启动线程,连接流程完全没启动。
修复后的代码示例
首先修复BluetoothConnection类
这里把连接逻辑移到run()方法里,同时添加回调接口把接收的数据传给UI层:
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; public class BluetoothConnection extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; private final BluetoothDevice mmDevice; private static final UUID MY_UUID = UUID.fromString("eb58f747-a241-4ccb-935d-04ac6039895d"); private OnDataReceivedListener mListener; // 定义回调接口,把数据传给UI层 public interface OnDataReceivedListener { void onDataReceived(byte[] data, int length); void onConnectionFailed(); void onConnected(); } public BluetoothConnection(BluetoothDevice device, OnDataReceivedListener listener) { mmDevice = device; mListener = listener; BluetoothSocket tmpSocket = null; // 获取BluetoothSocket try { tmpSocket = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e("BluetoothConn", "创建Socket失败", e); } mmSocket = tmpSocket; // 提前获取流(仅当socket不为null时) InputStream tmpIn = null; OutputStream tmpOut = null; if (mmSocket != null) { try { tmpIn = mmSocket.getInputStream(); tmpOut = mmSocket.getOutputStream(); } catch (IOException e) { Log.e("BluetoothConn", "获取流失败", e); } } mmInStream = tmpIn; mmOutStream = tmpOut; } @Override public void run() { // 取消蓝牙发现(必须做,否则影响连接) mmDevice.getBluetoothAdapter().cancelDiscovery(); try { // 在这里执行连接(线程内执行,不会阻塞主线程) if (mmSocket != null) { mmSocket.connect(); Log.d("BluetoothConn", "连接成功!"); if (mListener != null) { mListener.onConnected(); } } } catch (IOException connectException) { // 连接失败,关闭socket try { if (mmSocket != null) { mmSocket.close(); } Log.e("BluetoothConn", "连接失败:" + connectException.getMessage()); if (mListener != null) { mListener.onConnectionFailed(); } } catch (IOException closeException) { Log.e("BluetoothConn", "关闭Socket失败", closeException); } return; } // 连接成功后,持续读取数据 byte[] buffer = new byte[1024]; int bytes; while (true) { try { // 读取输入流的数据 if (mmInStream != null) { bytes = mmInStream.read(buffer); if (bytes > 0 && mListener != null) { // 把数据传给UI层 mListener.onDataReceived(buffer, bytes); } } } catch (IOException e) { Log.e("BluetoothConn", "读取数据失败,连接断开", e); break; } } } // 发送数据方法 public void write(byte[] buffer) { try { if (mmOutStream != null) { mmOutStream.write(buffer); } } catch (IOException e) { Log.e("BluetoothConn", "发送数据失败", e); } } // 关闭连接 public void cancel() { try { if (mmSocket != null) { mmSocket.close(); } } catch (IOException e) { Log.e("BluetoothConn", "关闭连接失败", e); } } }
然后修复DeviceList类
完善蓝牙初始化、设备选择和连接触发逻辑:
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.PopupMenu; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.util.Set; public class DeviceList extends AppCompatActivity implements BluetoothConnection.OnDataReceivedListener { private Button deviceSearch; private BluetoothAdapter BA; private BluetoothConnection connector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_device_list); deviceSearch = findViewById(R.id.device_search); // 初始化蓝牙适配器 BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class); BA = bluetoothManager.getAdapter(); // 检查蓝牙是否开启 if (BA == null || !BA.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, 1); } deviceSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showPairedDevicesMenu(); } }); } // 显示配对设备的弹出菜单 private void showPairedDevicesMenu() { PopupMenu popup = new PopupMenu(DeviceList.this, deviceSearch); Set<BluetoothDevice> pairedDevices = BA.getBondedDevices(); if (pairedDevices.isEmpty()) { Toast.makeText(this, "没有已配对的蓝牙设备", Toast.LENGTH_SHORT).show(); return; } // 把配对设备添加到菜单 int index = 0; for (BluetoothDevice device : pairedDevices) { popup.getMenu().add(0, index, index, device.getName() + "\n" + device.getAddress()); index++; } // 菜单点击事件:选择设备后启动连接 popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int position = item.getItemId(); BluetoothDevice selectedDevice = (BluetoothDevice) pairedDevices.toArray()[position]; startBluetoothConnection(selectedDevice); return true; } }); popup.show(); } // 启动蓝牙连接 private void startBluetoothConnection(BluetoothDevice device) { if (connector != null && connector.isAlive()) { connector.cancel(); } // 实例化连接线程并启动 connector = new BluetoothConnection(device, this); connector.start(); Toast.makeText(this, "正在连接:" + device.getName(), Toast.LENGTH_SHORT).show(); } // 实现回调接口方法 @Override public void onDataReceived(byte[] data, int length) { // 这里处理接收到的数据,注意要切换到主线程更新UI runOnUiThread(() -> { String receivedData = new String(data, 0, length); Toast.makeText(this, "收到数据:" + receivedData, Toast.LENGTH_SHORT).show(); // 可以把数据显示到TextView或者其他UI控件 }); } @Override public void onConnectionFailed() { runOnUiThread(() -> Toast.makeText(this, "连接失败,请检查设备", Toast.LENGTH_SHORT).show()); } @Override public void onConnected() { runOnUiThread(() -> Toast.makeText(this, "连接成功!", Toast.LENGTH_SHORT).show()); } @Override protected void onDestroy() { super.onDestroy(); // 关闭连接,释放资源 if (connector != null) { connector.cancel(); } } }
额外注意事项
- 权限配置:在
AndroidManifest.xml里添加必要的蓝牙权限(根据你的Android版本调整):<!-- 蓝牙基础权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- Android 6.0+ 需要位置权限才能扫描设备 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Android 12+ 额外权限 --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> - UUID一致性:你代码里的
MY_UUID必须和蓝牙设备端使用的UUID完全一致,否则无法建立连接。 - 设备状态:确保蓝牙设备处于可连接状态(不是仅配对,而是处于等待连接的服务状态)。
慢慢来,蓝牙这块多试几次就能摸透啦!
内容的提问来源于stack exchange,提问作者Collin Urie
相关产品推荐
相关产品推荐

