现在大部分手机都有蓝牙功用,能够与其他有蓝牙功用的设备衔接、传输数据等。本文介绍怎么运用蓝牙框架API扫描和配对邻近的设备。

官方文档

恳求权限

假如要在App中运用蓝牙功用,需求恳求多个权限,详细如下:

targetSdk 31(Android 12)及以上

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!--答应App衔接到配对的蓝牙设备(兼容低版别)-->
    <uses-permission
        android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <!--答应App发现和配对蓝牙设备(兼容低版别)-->
    <uses-permission
        android:name="android.permission.BLUETOOTH_ADMIN"
        android:maxSdkVersion="30" />
    <!--若App需求扫描蓝牙设备,恳求此权限-->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <!--若App使当时设备可供其他蓝牙设备发现,恳求此权限-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <!--若App与蓝牙设备配对、通讯,恳求此权限-->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <!--若App经过蓝牙扫描成果获取物理方位,恳求此权限-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

承认不会运用蓝牙扫描成果获取物理方位时,设置BLUETOOTH_SCAN权限的usesPermissionFlags,就无需再恳求ACCESS_FINE_LOCATION权限。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!--若App需求扫描蓝牙设备,恳求此权限-->
    <!--当usesPermissionFlags设置为neverForLocation时,无需再恳求ACCESS_FINE_LOCATION权限-->
    <uses-permission
        android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation" />
</manifest>

需求注意的是,BLUETOOTH_SCANBLUETOOTH_ADVERTISEBLUETOOTH_CONNECT均为运行时权限,需求恳求并取得用户赞同后,才能扫描邻近蓝牙设备、被其他蓝牙设备发现或与其他蓝牙设备配对和通信,代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    private val requestMultiplePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions: Map<String, Boolean> ->
        val noGrantedPermissions = ArrayList<String>()
        permissions.entries.forEach {
            if (!it.value) {
                noGrantedPermissions.add(it.key)
            }
        }
        if (noGrantedPermissions.isEmpty()) {
            // 一切恳求权限经过,能够履行后续操作
        } else {
            //未赞同授权
            noGrantedPermissions.forEach {
                if (!shouldShowRequestPermissionRationale(it)) {
                    //用户回绝权限而且体系不再弹出恳求权限的弹窗
                    //这时需求咱们自己处理,比方自定义弹窗告知用户为何必须要恳求这个权限
                }
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val requestPermissionNames = arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT)
        if (requestPermissionNames.find { ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED } != null) {
            requestMultiplePermissionLauncher.launch(requestPermissionNames)
        } else {
            // 一切恳求权限经过,能够履行后续操作
        }
    }
}

targetSdk 30(Android 11)及以下

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!--答应App恳求衔接、接受衔接或传输数据-->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <!--答应App发现和配对蓝牙设备-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!--在Android 11(30)及以下版别中,方位权限是必须的-->
    <!--在Android 9(28)及以下版别中,能够运用ACCESS_COARSE_LOCATION权限代替ACCESS_FINE_LOCATION权限-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

需求注意的是,方位权限为运行时权限,需求恳求并取得用户赞同后,才能扫描邻近蓝牙设备,代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    private val requestPermissionName = Manifest.permission.ACCESS_FINE_LOCATION
    private val requestSinglePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted: Boolean ->
        if (granted) {
            // 恳求权限经过,能够履行后续操作
        } else {
            if (!shouldShowRequestPermissionRationale(requestPermissionName)) {
                //用户回绝权限而且体系不再弹出恳求权限的弹窗
                //这时需求咱们自己处理,比方自定义弹窗告知用户为何必须要恳求这个权限
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (ActivityCompat.checkSelfPermission(this, requestPermissionName) == PackageManager.PERMISSION_GRANTED) {
            // 恳求权限经过,能够履行后续操作
        } else {
            requestSinglePermissionLauncher.launch(requestPermissionName)
        }
    }
}

检查与敞开蓝牙

检查是否支撑蓝牙功用

要运用蓝牙功用,首要需求承认当时设备是否支撑蓝牙功用,代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
        if (bluetoothAdapter != null) {
            // 当时设备支撑蓝牙功用    
        }
    }
}

敞开蓝牙

承认当时设备支撑蓝牙功用后,检测蓝牙是否敞开,未敞开的话能够调用体系办法敞开蓝牙,代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    private val intentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
        if (activityResult.resultCode == Activity.RESULT_OK) {
            // 成功敞开蓝牙
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
        if (bluetoothAdapter != null) {
            if (bluetoothAdapter.isEnabled != true) {
                // 蓝牙未敞开,经过体系启用蓝牙
                intentLauncher.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))
            }
        }
    }
}

扫描和配对邻近的设备

权限已恳求结束、蓝牙功用敞开后,能够扫描邻近的设备,进行配对。

获取已配对过的设备

当时设备可能现已与某些蓝牙设备配对过,获取已配对过的设备的代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
        // 已绑定的设备
        val bondedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
    }
}

扫描邻近设备并进行配对

注册广播接收者,监听扫描成果,运用startDiscovery扫描邻近其他蓝牙设备。能够运用BluetoothDevicecreateBond办法进行配对,代码如下:

class BluetoothExampleActivity : AppCompatActivity() {
    private val scanResultReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            when (intent?.action) {
                BluetoothDevice.ACTION_FOUND -> {
                    // 发现的蓝牙设备
                    val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                    // 假如现已找到想要的设备,能够停止扫描
                    bluetoothAdapter?.cancelDiscovery()
                    // 与发现的蓝牙设备配对
                    device?.createBond()
                }
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 注册蓝牙设备扫描成果监听
        registerReceiver(scanResultReceiver, IntentFilter().apply {
            addAction(BluetoothDevice.ACTION_FOUND)
        })
        val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
        bluetoothAdapter?.startDiscovery()
    }
    override fun onDestroy() {
        super.onDestroy()
        // 撤销扫描成果监听
        unregisterReceiver(scanResultReceiver)
    }
}

示例

效果如图:

Android Bluetooth(一)— 扫描和配对附近的设备

完好演示代码已在示例Demo中增加。

ExampleDemo github

ExampleDemo gitee