Android蓝牙Ble基本操作-(扫描1)

前言:
Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作
Android BLE 使用的蓝牙协议是 GATT 协议,有关该协议的详细内容可以参见蓝牙官方文档:点击此处

1、扫描工具类BleScanUtils:

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.os.Build
import android.text.TextUtils
import android.util.Log
import androidx.annotation.RequiresApi
import java.util.*

/**
 * 类说明:扫描工具类
 */
class BleScanUtils private constructor() {

    private val TAG = "BleScanUtils"

    //本地蓝牙设备的适配类,所有的蓝牙操作都要通过该类完成
    private var bluetoothAdapter: BluetoothAdapter? = null

    //扫描回调
    private var leScanner: BluetoothLeScanner? = null

    //蓝牙列表
    private var scanResultSet: MutableSet<BluetoothDevice>? = null

    //结果回调
    private lateinit var listener: (MutableSet<BluetoothDevice>) -> Unit

    fun setOnListener(bluetoothDevice: (MutableSet<BluetoothDevice>) -> Unit) {
        listener = bluetoothDevice
    }

    companion object {
        val getInstance: BleScanUtils by lazy {
            BleScanUtils()
        }
    }

    /**
     * 开始搜索蓝牙设备
     * Android官方提供的蓝牙扫描方式有三种,分别是:
     * 1、BluetoothAdapter.startDiscovery()//可以扫描经典蓝牙和ble蓝牙两种
     * 2、BluetoothAdapter.startLeScan()//扫描低功耗蓝牙,在api21已经弃用,不过还是可以使用
     * 3、BluetoothLeScanner.startScan()//新的ble扫描方法
     */
    fun startScanBle(context: Context) {
        if (null == scanResultSet) {
            scanResultSet = HashSet()
        } else {
            scanResultSet!!.clear()
        }
        val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        bluetoothAdapter = bluetoothManager.adapter
        leScanner = bluetoothAdapter!!.bluetoothLeScanner
        if (bluetoothAdapter != null && bluetoothAdapter!!.isEnabled) {
            //安卓版本是否大于6.0
            val settings: ScanSettings = if (Build.VERSION.SDK_INT >= 23) {
                ScanSettings.Builder() //设置扫描模式。可选择模式主要三种( 从上到下越来越耗电,扫描间隔越来越短,即扫描速度会越来越快。
                        //1、SCAN_MODE_LOW_POWER:低功耗模式(默认扫描模式,如果扫描应用程序不在前台,则强制使用此模式。)
                        //2、SCAN_MODE_BALANCED: 平衡模式
                        .setScanMode(ScanSettings.SCAN_MODE_BALANCED) //设置回调类型。可选择模式主要三种:
                        //1、CALLBACK_TYPE_ALL_MATCHES:1 寻找符合过滤条件的蓝牙广播,如果没有设置过滤条件,则返回全部广播包
                        //2、CALLBACK_TYPE_FIRST_MATCH:2 与筛选条件匹配的第一个广播包触发结果回调
                        //3、CALLBACK_TYPE_MATCH_LOST:4 有过滤条件时过滤,返回符合过滤条件的蓝牙广播。无过滤条件时,返回全部蓝牙广播
                        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) //设置蓝牙扫描滤波器硬件匹配的匹配模式
                        //MATCH_MODE_STICKY:2 粘性模式,在通过硬件报告之前,需要更高的信号强度和目击阈值
                        //MATCH_MODE_AGGRESSIVE:1 激进模式,即使信号强度微弱且持续时间内瞄准/匹配的次数很少,hw也会更快地确定匹配。
                        //.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
                        //设置蓝牙LE扫描的报告延迟的时间(以毫秒为单位)。
                        //该参数默认为 0,如果不修改它的值,则默认只会在onScanResult(int,ScanResult)中返回扫描到的蓝牙设备,不会触发onBatchScanResults(List)方法。(onScanResult(int,ScanResult) 和 onBatchScanResults(List) 是互斥的。 )
                        //设置为0以立即通知结果,不开启批处理扫描模式。即ScanCallback蓝牙回调中,不会触发onBatchScanResults(List)方法,但会触发onScanResult(int,ScanResult)方法,返回扫描到的蓝牙设备。
                        //当设置的时间大于0L时,则会开启批处理扫描模式。即触发onBatchScanResults(List)方法,返回扫描到的蓝牙设备列表。但不会触发onScanResult(int,ScanResult)方法。
                        //.setReportDelay(0)
                        .build()
            } else {
                ScanSettings.Builder()
                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                        .build()
            }
            //第一个参数(ScanFilter):筛选条件,可以通过设置过滤器的mDeviceName、mDeviceAddress、mServiceUuid等作为过滤条件进行过滤。
            //第二个参数(ScanSettings):设置,可以设置扫描的mScanMode 、mCallbackType 、mScanResultType 等。
            //List<ScanFilter> scanFilters = new ArrayList<>();
            //scanFilters.add(new ScanFilter.Builder().setDeviceName("deviceName").build());
            leScanner!!.startScan(null, settings, leScanCallback)
        }
    }

    /**
     * 回调函数中尽量不要做耗时操作
     */
    private val leScanCallback: ScanCallback = object : ScanCallback() {
        @RequiresApi(api = Build.VERSION_CODES.O)
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            super.onScanResult(callbackType, result)
            //信号强度【在dBm中返回接收到的信号强度。有效范围为[-127,126]。】
            val rssi = result.rssi.toString()
            //ScanRecord record =result.getScanRecord();//搜索记录相关
            val device = result.device
            //设备名称
            val deviceName = device.name
            //设备MAC地址
            val deviceAddress = device.address
            //BOND_NONE:数值 10 表示远程设备未绑定,没有共享链接密钥,因此通信(如果允许的话)将是未经身份验证和未加密的。【默认10未绑定】
            //BOND_BONDING:数值 11 表示正在与远程设备进行绑定;
            //BOND_BONDED:数值 12 表示远程设备已绑定,远程设备本地存储共享连接的密钥,因此可以对通信进行身份验证和加密
            val bondState = device.bondState
            //设备的蓝牙设备类型
            //DEVICE_TYPE_CLASSIC 传统蓝牙 常量值:1,
            //DEVICE_TYPE_LE  低功耗蓝牙 常量值:2【一般为2,表示为BLE设备】
            //DEVICE_TYPE_DUAL 双模蓝牙 常量值:3.
            //DEVICE_TYPE_UNKNOWN:未知 常量值:0)
            val type = device.type

            //过滤掉无设备名【注意:无设备名不代表无mac地址】和被绑定的
            if (!TextUtils.isEmpty(deviceName) && !scanResultSet!!.contains(device)) {
                //SET添加不重复元素【当set为BluetoothDevice或基本数据类型不会重复添加 当set为ScanResult会重复添加】
                scanResultSet!!.add(device)
                listener.invoke(scanResultSet!!)
            }
        }

        override fun onBatchScanResults(results: List<ScanResult>) {
            Log.e(TAG, "onBatchScanResults: " + results.size)
        }

        override fun onScanFailed(errorCode: Int) {
            //手机在startScan扫描的过程中还没来得及stopScan ,就被系统强制杀掉了, 导致mClientIf未被正常释放,
            //实例和相关蓝牙对象已被残留到系统蓝牙服务中,  打开app后又重新初始化ScanCallback多次被注册,
            //导致每次的扫描mClientIf的值都在递增, 于是mClientIf的值在增加到一定程度时
            //(最大mClientIf数量视国产系统而定不做深究), onScanFailed 返回了 errorCode =2  。
            Log.e(TAG, "scan error: $errorCode")
        }
    }

    /**
     * 关闭扫描
     */
    fun stopScanBle() {
        if (leScanner != null && leScanCallback != null) {
            if (bluetoothAdapter != null && bluetoothAdapter!!.isEnabled) {
                leScanner!!.stopScan(leScanCallback)
            }
        }
    }
}

2、使用类MainActivity:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.helloword.testdemo.BleScanUtils

/**
 * 测试机型:小米10	Anroid版本:11
 * 附近设备:1
 * 57:4C:42:00:91:7F
 * IMT60 00917F
 */
class MainActivity : AppCompatActivity() {

    private val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //开始扫描
        BleScanUtils.getInstance.startScanBle(this)
        //扫描回调
        BleScanUtils.getInstance.setOnListener {
            for (i in it) {
                Log.e(TAG, "onCreate: " + i.address)
                Log.e(TAG, "onCreate: " + i.name)
            }
            Log.e(TAG, "onCreate: 附近设备:" + it.size)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        //关闭扫描
        BleScanUtils.getInstance.stopScanBle()
    }
}

3、AndroidManifest.xml:

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

注意:动态权限不做说明,若扫描不到,请到设置-应用管理-手动打开定位、蓝牙权限!

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值