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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:59:52