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

在线程中打开Socket并返回完整Socket?安卓蓝牙消息发送技术求助

在子线程中获取蓝牙Socket对象的实现方案

没问题,我来帮你搞定这个需求~你现在的代码是在主线程里直接执行蓝牙连接,虽然能跑,但connect()是阻塞操作,很容易触发ANR(应用无响应),把这部分逻辑放到子线程里是非常必要的。下面给你两种常用的实现方式,适配Java和Kotlin项目:

一、Java项目:线程+回调接口实现

因为Java里没有协程的原生支持,用线程配合回调是最直接的方式:

1. 定义回调接口

先写一个回调接口,用来传递连接成功的Socket或者错误信息:

public interface BluetoothSocketCallback {
    void onSocketConnected(BluetoothSocket socket);
    void onConnectFailed(Exception e);
}

2. 子线程中执行连接逻辑

封装一个方法,把蓝牙连接的逻辑放到子线程里:

public void connectBluetoothInThread(BluetoothDevice device, UUID uuid, BluetoothSocketCallback callback) {
    new Thread(() -> {
        BluetoothSocket mmSocket = null;
        try {
            // 创建Socket实例
            mmSocket = device.createRfcommSocketToServiceRecord(uuid);
            
            // 先关闭已连接的Socket(如果有的话)
            if (mmSocket.isConnected()) {
                mmSocket.close();
            }
            
            // 执行阻塞连接(这一步必须放子线程)
            mmSocket.connect();
            
            // 连接成功后切回主线程回调,避免后续操作阻塞子线程或触发UI线程异常
            new Handler(Looper.getMainLooper()).post(() -> {
                callback.onSocketConnected(mmSocket);
            });
            
        } catch (IOException e) {
            e.printStackTrace();
            // 连接失败,同样切回主线程通知上层
            new Handler(Looper.getMainLooper()).post(() -> {
                callback.onConnectFailed(e);
            });
            
            // 清理资源:关闭未成功连接的Socket
            if (mmSocket != null) {
                try {
                    mmSocket.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }).start();
}

3. 调用示例

在需要触发连接的地方调用这个方法,拿到Socket后再执行写操作:

// 假设mmDevice是你选中的蓝牙设备,yourUUID是目标服务的UUID
connectBluetoothInThread(mmDevice, yourUUID, new BluetoothSocketCallback() {
    @Override
    public void onSocketConnected(BluetoothSocket socket) {
        // 这里拿到了已连接的Socket,执行你的写逻辑
        try {
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(fullMsg.getBytes());
            outputStream.close();
            // 用完记得关闭Socket,或者根据业务需求保留连接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onConnectFailed(Exception e) {
        // 处理连接失败的情况,比如给用户弹提示
        Toast.makeText(getApplicationContext(), "蓝牙连接失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }
});

二、Kotlin项目:协程实现(更简洁)

如果你的项目用Kotlin,推荐用协程来处理异步操作,代码更简洁,不需要回调:

1. 封装挂起函数

写一个挂起函数,把连接逻辑放到IO线程:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket

suspend fun connectBluetooth(device: BluetoothDevice, uuid: UUID): BluetoothSocket? {
    return withContext(Dispatchers.IO) {
        var socket: BluetoothSocket? = null
        try {
            socket = device.createRfcommSocketToServiceRecord(uuid)
            if (socket.isConnected) {
                socket.close()
            }
            socket.connect()
            socket
        } catch (e: IOException) {
            e.printStackTrace()
            socket?.close()
            null
        }
    }
}

2. 调用示例

在协程作用域里调用这个挂起函数(比如Activity/Fragment的lifecycleScope):

import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import android.widget.Toast

// 在合适的地方触发连接
lifecycleScope.launch {
    val socket = connectBluetooth(mmDevice, yourUUID)
    socket?.let {
        try {
            it.outputStream.write(fullMsg.toByteArray())
            it.outputStream.close()
            it.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    } ?: run {
        Toast.makeText(applicationContext, "蓝牙连接失败", Toast.LENGTH_SHORT).show()
    }
}

注意事项

  • 权限检查:确保已经申请了蓝牙相关权限,Android 12及以上需要BLUETOOTH_CONNECT权限,低版本需要BLUETOOTH和BLUETOOTH_ADMIN权限,并且要动态申请危险权限。
  • 资源清理:无论连接成功还是失败,都要记得关闭Socket和流,避免资源泄漏。
  • 读写操作:如果后续有大量数据读写,也建议放到子线程/IO线程里执行,不要阻塞主线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:40:57