我该如何编程判断蓝牙设备是否已连接?

95

我知道如何获取已配对设备列表,但如何确定它们是否已连接?

这肯定是可能的,因为我在手机的蓝牙设备列表中看到它们并且状态显示它们是否连接。

7个回答

175

将Bluetooth权限添加到您的AndroidManifest中。

<uses-permission android:name="android.permission.BLUETOOTH" />

然后使用意图过滤器来监听 ACTION_ACL_CONNECTEDACTION_ACL_DISCONNECT_REQUESTEDACTION_ACL_DISCONNECTED 广播:

public void onCreate() {
    ...
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
    filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    this.registerReceiver(mReceiver, filter);
}

//The BroadcastReceiver that listens for bluetooth broadcasts
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           ... //Device found
        }
        else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
           ... //Device is now connected
        }
        else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
           ... //Done searching
        }
        else if (BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
           ... //Device is about to disconnect
        }
        else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
           ... //Device has disconnected
        }
    }
};

几个注意事项:

  • 应用程序启动时无法检索已连接设备的列表。蓝牙API不允许您进行查询,而是允许您监听更改
  • 对于上述问题的一种可行的解决方法是检索所有已知/配对设备的列表...然后尝试连接到每个设备(以确定是否已连接)。
  • 或者,您可以拥有一个后台服务监视蓝牙API,并将设备状态写入磁盘供您的应用程序在以后使用。

2
只要我的应用程序在运行,这很好,但我如何获取当前列表? - dchappelle
2
你打算如何在应用程序“未运行”的情况下执行代码?如果你的意思是需要从除了Activity之外的其他地方访问它...那就去谷歌一下Android服务,构建一个监听广播的服务,并将其持久化到列表中。 - Skylar Sutton
8
我可以监听意图,但是如何获取已连接的蓝牙设备的初始列表呢?如果在我的Activity或Service启动时已经有设备连接,我就不知道了。我想象(希望)这些数据在某个地方是可用的?我不想创建一个服务(只要手机开机就一直运行)来监听这些意图。 - dchappelle
11
在应用程序启动时,无法检索连接设备列表。蓝牙API只能让您监听连接更改。因此,唯一的方法是创建一个长时间运行的服务,并将其添加/移除到公共列表中。这是许多开发人员的一个大问题。根据性能需求,您可以检索配对设备列表并尝试连接每个设备。如果连接失败,则表示该设备不可用。但需要警告的是:.connect()是一个阻塞性操作。 - Skylar Sutton
1
@skylarsutton +1 非常感谢您提供的答案,帮助我开始学习蓝牙技术。 - AgentKnopf
显示剩余10条评论

44

在我的使用场景中,我只想知道蓝牙耳机是否已连接到 VoIP 应用程序。下面的解决方案适用于我。

Kotlin:

fun isBluetoothHeadsetConnected(): Boolean {
    val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
    return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled
        && mBluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED)
}

Java:

public static boolean isBluetoothHeadsetConnected() {
    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
            && mBluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED;
} 

当然你需要获得蓝牙权限:

<uses-permission android:name="android.permission.BLUETOOTH" />


4
这就是我一直在寻找的。只是在启动时检查蓝牙是否已连接。谢谢,它奏效了! - sud007
这个解决方案在我升级Android Studio到2021.1.1版本之前是有效的。现在,getProfileConnectionState(BluetoothHeadset.HEADSET)调用显示错误:“调用需要用户可能拒绝的权限:代码应明确检查权限是否可用(使用checkPermission)或明确处理潜在的SecurityException”。所需的权限是Manifest.permission.BLUETOOTH_CONNECT - Juha
我遇到了一个问题,即使 device.bluetoothClass.hasService(BluetoothClass.Service.AUDIO)=false,三星Galaxy Watch4在这个通话中也显示为耳机。我正在尝试找出如何确定是否有任何支持音频的连接设备,但 isConnected 调用仅限于 SystemApi。目前,我可以看到所有已配对的设备,但无法确定它们是否已连接 - 因此我无法确定是否真正连接了耳机或者只是手表(即我有未连接的耳机)。有人有解决办法吗? - notmystyle

17

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothDevice.java的 BluetoothDevice 系统 API 中有一个isConnected函数。

如果你想知道一个已配对的设备是否当前处于连接状态,下面的函数对我来说很有效:

public static boolean isConnected(BluetoothDevice device) {
    try {
        Method m = device.getClass().getMethod("isConnected", (Class[]) null);
        boolean connected = (boolean) m.invoke(device, (Object[]) null);
        return connected;
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

你是否发现这个与仅检查 bluetoothManager.getConnectionState(device, BluetoothProfile.GATT) == BluetoothProfile.STATE_CONNECTED 是否连接会得到不同的结果? - Gumby The Green
1
据我所知,它们给出的结果是相同的。请注意,对于 Kotlin 用户来说,这前两行只是 val m: Method = device.javaClass.getMethod("isConnected")val connected = m.invoke(device) - Gumby The Green
3
谢谢,正是我需要的!这是用Kotlin编写的等价方法:fun isConnected(device: BluetoothDevice): Boolean { return try { val m: Method = device.javaClass.getMethod( "isConnected" ) m.invoke(device) as Boolean } catch (e: Exception) { throw IllegalStateException(e) } } - Takeya
反射现在被禁止了。 - user924

12

由于某些原因,Android Studio无法解析BluetoothAdapter.ACTION_ACL_CONNECTED。也许它在Android 4.2.2中已经被弃用了?

下面是对Skylarsutton代码的修改(非常感谢Skylarsutton提供的答案)。注册代码相同,接收器代码略有不同。我将其用于一个服务中,该服务更新一个连接的蓝牙标志,其他部分的应用引用该标志。

    public void onCreate() {
        //...
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        this.registerReceiver(BTReceiver, filter);
    }

    //The BroadcastReceiver that listens for bluetooth broadcasts
    private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
            //Do something if connected
            Toast.makeText(getApplicationContext(), "BT Connected", Toast.LENGTH_SHORT).show();
        }
        else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
            //Do something if disconnected
            Toast.makeText(getApplicationContext(), "BT Disconnected", Toast.LENGTH_SHORT).show();
        }
        //else if...
    }
};

1
你的代码是正确的;它在4.2.2中没有被弃用。BluetoothAdapter类不包含ACTION_ACL_CONNECTED。该字符串在BluetoothDevice类中。 - RobLabs

1

BluetoothAdapter.getDefaultAdapter().isEnabled -> 当蓝牙开启时返回 true。

val audioManager = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager

audioManager.isBluetoothScoOn -> 当设备连接时返回 true。


1
这段代码是用于耳机配置文件的,可能也适用于其他配置文件。
首先,您需要提供一个配置文件监听器(Kotlin代码):
private val mProfileListener = object : BluetoothProfile.ServiceListener {
    override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
        if (profile == BluetoothProfile.HEADSET)
            mBluetoothHeadset = proxy as BluetoothHeadset
    }

    override fun onServiceDisconnected(profile: Int) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null
        }
    }
}

在检查蓝牙时:
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET)
if (!mBluetoothAdapter.isEnabled) {
    return Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
}

onServiceConnected 方法被调用需要一些时间。之后,您可以从以下位置获取已连接的耳机设备列表:

mBluetoothHeadset!!.connectedDevices

我用这个来实现A2DP,但是当没有设备连接且仅蓝牙处于活动状态时,它也会被调用;而当你连接设备时,如果没有禁用蓝牙系统设置,则不会触发新的事件。 - Peterdk

0

我真的在寻找一种获取设备连接状态的方法,而不是监听连接事件。以下是对我有效的方法:

BluetoothManager bm = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
List<BluetoothDevice> devices = bm.getConnectedDevices(BluetoothProfile.GATT);
int status = -1;

for (BluetoothDevice device : devices) {
  status = bm.getConnectionState(device, BLuetoothGatt.GATT);
  // compare status to:
  //   BluetoothProfile.STATE_CONNECTED
  //   BluetoothProfile.STATE_CONNECTING
  //   BluetoothProfile.STATE_DISCONNECTED
  //   BluetoothProfile.STATE_DISCONNECTING
}

这返回了一个空设备列表,尽管我有一个已连接的设备。 - K Pradeep Kumar Reddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接