使用Pending Intent的BLE Scanner无法获取ScanResults问题
BLE扫描PendingIntent广播无数据问题解决
问题描述
使用PendingIntent调用startScan进行BLE扫描时,广播接收器能被触发,但收到的Intent中没有任何额外数据(无扫描结果),该代码在旧版Android系统中可正常运行。
现有代码
扫描器代码
fun ComponentActivity.startScanner() { if (!checkForPermission( if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S){ Manifest.permission.ACCESS_COARSE_LOCATION } else { Manifest.permission.BLUETOOTH_SCAN } )){ return } with(logViewModel) { if (!haveBtPermissions) { haveBtPermissions = true clear() } } // bluetoothAdapter.bluetoothLeScanner.startScan(filters, settings, scanCallback) bluetoothAdapter.bluetoothLeScanner.startScan(filters, settings, pendingIntent) Log.i(TAG, "Scanner started") } private val filters get() = mutableListOf<ScanFilter>().apply { val filterShortServiceUUID = ScanFilter.Builder() .setServiceUuid(ParcelUuid.fromString ("0000${"180D"}-0000-1000-8000-00805F9B34FB")) .build() add(filterShortServiceUUID) } private val settings get() = ScanSettings.Builder().setScanMode( ScanSettings.SCAN_MODE_BALANCED ).build() private val ComponentActivity.bluetoothAdapter get() = (applicationContext .getSystemService(ComponentActivity.BLUETOOTH_SERVICE) as BluetoothManager).adapter private val ComponentActivity.pendingIntent get() = applicationContext.let {appContext -> val intent = Intent(appContext, DeviceBroadcastReceiver::class.java) // .setAction("com.solvek.bletrigger.ACTION_FOUND") PendingIntent.getBroadcast( appContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE )!! } private fun ComponentActivity.checkForPermission(permission: String): Boolean { if (ActivityCompat.checkSelfPermission( this, permission ) == PackageManager.PERMISSION_GRANTED ) { return true } with(logViewModel) { append("Please grant permissions and restart app!") haveBtPermissions = false } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){ registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){ startScanner() }.launch(arrayOf( Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT )) } return false }
接收器代码
class DeviceBroadcastReceiver: BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Log.i(TAG, "===== A new message received") if (context == null){ Log.e(TAG, "Context is null") return } if (intent == null){ Log.e(TAG, "Intent is null") return } val bleCallbackType: Int = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1) if (bleCallbackType == -1){ Log.w(TAG, "Callback type not specified") return } Log.i(TAG, "Received callback type $bleCallbackType") } }
Manifest配置
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" /> <!-- Request Bluetooth permissions for devices on API 31,32. --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:targetApi="s" /> <!-- Needed only if your app communicates with already-paired Bluetooth devices. --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Since Bluetooth is a basic requirement for our app --> <uses-feature android:name="android.hardware.bluetooth" android:required="true" /> <application ... <receiver android:name=".DeviceBroadcastReceiver" android:exported="false"> <!-- <intent-filter>--> <!-- <action android:name="com.solvek.bletrigger.ACTION_FOUND" />--> <!-- </intent-filter>--> </receiver>
解决要点及修改方案
1. 修正PendingIntent的Mutable标记
Android 12+(API 31+)中,FLAG_IMMUTABLE会阻止系统向Intent添加扫描结果数据,需改用FLAG_MUTABLE:
private val ComponentActivity.pendingIntent get() = applicationContext.let { appContext -> val intent = Intent(appContext, DeviceBroadcastReceiver::class.java) .setAction("com.solvek.bletrigger.ACTION_FOUND") // 添加唯一Action PendingIntent.getBroadcast( appContext, 0, intent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT } )!! }
2. 为ScanSettings指定回调类型
必须明确设置回调类型,否则系统不会传递扫描结果:
private val settings get() = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_BALANCED) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // 匹配所有结果 .build()
3. 完善Manifest配置
给广播接收器添加IntentFilter匹配指定Action:
<receiver android:name=".DeviceBroadcastReceiver" android:exported="false"> <intent-filter> <action android:name="com.solvek.bletrigger.ACTION_FOUND" /> </intent-filter> </receiver>
如果不需要位置权限辅助扫描,给BLUETOOTH_SCAN权限添加标记:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" tools:targetApi="s" />
4. 在接收器中获取扫描结果
补充代码提取扫描结果:
override fun onReceive(context: Context?, intent: Intent?) { Log.i(TAG, "===== 收到新消息") if (context == null || intent == null) { Log.e(TAG, "Context或Intent为空") return } val bleCallbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1) if (bleCallbackType == -1) { Log.w(TAG, "未指定回调类型") return } Log.i(TAG, "收到回调类型 $bleCallbackType") // 获取扫描结果列表 val scanResults = intent.getParcelableArrayListExtra<ScanResult>(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT) scanResults?.forEach { result -> Log.i(TAG, "发现设备: 名称=${result.device.name ?: "未知"}, MAC=${result.device.address}, RSSI=${result.rssi}") } }
内容的提问来源于stack exchange,提问作者Solvek
相关产品推荐
相关产品推荐

