![](/img/trans.png)
[英]Android BLE Scan Callback is not called after scanning for BLE devices
[英]Unable to scan or discover BT and BLE devices in android
我是 Java 和 Android 的完全新手。我正在嘗試創建一個測試應用程序來監聽附近的 BLE 和 BT 設備。 我有另一個設備,我在其中編寫了一些邏輯來廣播其 BLE 信標。 我使用 Playstore 應用驗證了它。 現在我正在嘗試在 Android 上編寫自己的應用程序。我一直在閱讀 Android 開發人員頁面以獲取指導。 我確實遵循了以下頁面的每一步
https://developer.android.com/guide/topics/connectivity/bluetooth/setup
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
另外,請注意,我使用了 Android 開發者頁面中的 BARE MINIMUM CODE所以這就是我所做的。
1. 首先我在 AndroidManifest 下添加了我的權限
注意 1:我正在將此應用程序部署到運行 Android 11 的手機
注 2:所有這些代碼都寫在 MainActivity 中。 我還沒有創建任何其他活動 class
<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.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2. 接下來我檢查我的 BT 是否啟用。
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
我收到了 BT 當然已啟用的成功消息
3.接下來我檢查我的設備是否支持BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
blefinder.append("\nBLE NOT SUPPORTED ON THIS DEVICE : ");
finish();
}
else{
blefinder.append("\nBLE IS SUPPORTED ON THIS DEVICE : ");
}
我收到支持 BLE 的消息
4.接下來我列出我已經配對/綁定的設備
為此,我調用ListPairedAndBondedDevices();
在上述步驟之后的 onCreate() 本身。 Function 定義如下。
private void ListPairedAndBondedDevices(){
@SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
blefinder.append("\nPAIRED/BONDED DEVICES");
for (BluetoothDevice device : pairedDevices) {
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
}
這也很有效,可以打印出我配對的設備。 接下來的兩部分是我面臨的問題。
5. 問題步驟 | 第1部分:
在這里,我注冊了一個廣播接收器來發現附近的所有 BT 設備。 我已經取消綁定我的 BT 耳機並將其保持在配對模式以驗證這一點。
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
廣播接收器實現
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
@SuppressLint("MissingPermission")
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
};
所以這部分沒有工作:(
如果您在上面看到,我在列出已經配對的設備(通過調用ListPairedAndBondedDevices()
)后立即在onCreate
中注冊BTReceiver
。
當我運行調試器時,這個廣播接收器永遠不會被調用。
6.問題步驟| 第2部分:
在此之后,我也嘗試通過scanLeDevice()
來掃描 BLE 設備
ListPairedAndBondedDevices(); // From previous snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // From previous snippet
registerReceiver(BTReceiver, filter);// From previous snippet
scanLeDevice(); // ---------------->>> CALLING THIS FUNCTION TO SCAN FOR BLE DEVICES
scanLeDevice() 的實現
private void scanLeDevice() {
if (!scanning) {
// Stops scanning after a predefined scan period.
handler.postDelayed(new Runnable() {
@Override
public void run() {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN... TIMEOUT REACHED");
}
}, SCAN_PERIOD);
scanning = true;
bluetoothLeScanner.startScan(leScanCallback);
} else {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN");
}
}
不幸的是,這也失敗了。 調試器告訴我這部分代碼正在被調用。 在SCAN_PERIOD
(我設置的超時)30 秒后,我收到掃描已停止的消息( STOPPING BLE SCAN
)
現在我也實現了leScanCallback
(即設備掃描回調)
private ScanCallback leScanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
blefinder.append("SOMETHING GOT SCANNED?");
blefinder.append("\n"+result.getDevice().toString());
// leDeviceListAdapter.addDevice(result.getDevice());
// leDeviceListAdapter.notifyDataSetChanged();
}
};
請注意,我沒有使用ListAdapter
,因為我不知道這個概念。 因此,對於初學者來說,我只是想將結果轉儲到由 blefinder 表示的blefinder
中。 這個blefinder
打印了所有其他文本,因此 TextView 變量沒有任何問題。 當我使用調試器運行時,它根本沒有進入leScanCallback
代碼定義,即使在 30 秒后,在執行scanLeDevice()
function 之后也是如此。
我在這里有點迷路了。 有什么我可能遺漏或做錯的事情嗎? 它應該很簡單,列出我附近的 ble/bt 設備。
如果我錯過了,我很樂意分享任何進一步的信息。 請在評論中告訴我。
首先,您應該檢查您的應用程序是否在設置應用程序 > 應用程序 <your_app> > 權限中獲得了位置權限。 一些權限(如 ACCESS_*_LOCATION 和 BLUETOOTH_ADMIN)需要在運行時請求並由用戶通過彈出窗口授予。 通常,在嘗試執行需要您的應用程序沒有的權限的代碼時,您應該會收到 SecurityException 或 logcat 警告,但 android 跳過錯誤處理的情況並不少見。
考慮使用此方法開始掃描,以便檢查其結果代碼以獲取有關正在(未)發生的事情的潛在附加信息。
您還可以通過記錄 BTReceiver.onReceive() 中收到的所有操作來獲得一些線索,而不僅僅是找到的操作。
最后檢查您設備上的位置設置是否已打開藍牙掃描(設置應用>位置> wifi和藍牙掃描)
假設您已完成我在評論中提到的權限,我們可以實現一個干凈的藍牙 LE 掃描器 object,然后在 UI 中使用它。
首先我們實現一個結果消費者接口,以便將結果傳遞給調用BleScanner.scan()
方法的消費者。
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
現在我們需要實現管理掃描事件的掃描儀 object:
public class BleScanner {
private static final String TAG = BleScanner.class.getSimpleName();
private BluetoothLeScanner leScanner = null;
private BluetoothAdapter bleAdapter = null;
private Handler uiHandler = new Handler(Looper.getMainLooper);
private ScanResultConsumer scanResultConsumer;
private boolean scanning = false;
private final ArrayList<BluetoothDevice> foundDeviceList = new ArrayList<>();
public BleScanner(Context context) {
final BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bluetoothManager.getAdapter();
if(bleAdapter == null) {
Log.d(TAG, "No bluetooth hardware.");
}
else if(!bleAdapter.isEnabled()){
Log.d(TAG, "Blutooth is off.");
}
}
public void scan(ScanResultConsumer scanResultConsumer, long scanTime){
foundDeviceList.clear();
if (scanning){
Log.d(TAG, "Already scanning.");
return;
}
Log.d(TAG, "Scanning...");
if(leScanner == null){
leScanner = bleAdapter.getBluetoothLeScanner();
}
if(scanTimeMs > 0) {
uiHandler.postDelayed(()-> {
if (scanning) {
Log.d(TAG, "Scanning is stopping.");
if(leScanner != null)
leScanner.stopScan(scanCallBack);
else
Log.d(TAG,"Scanner null");
setScanning(false);
}
}, scanTimeMs);
}
this.scanResultConsumer = scanResultConsumer;
leScanner.startScan(scanCallBack);
setScanning(true);
}
private final ScanCallback scanCallBack = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning){
return;
}
if(foundDeviceList.contains(result.getDevice())) {
// This device has already been found
return;
}
// New device found, add it to the list in order to prevent duplications
foundDeviceList.add(result.getDevice());
if(scanResultConsumer != null) {
uiHandler.post(() -> {
scanResultConsumer.onDeviceFound(result.getDevice(),
result.getScanRecord().getBytes(), result.getRssi());
});
}
}
};
public boolean isScanning(){
return scanning;
}
void setScanning(boolean scanning){
this.scanning = scanning;
uiHandler.post(() -> {
if(scanResultConsumer == null) return;
if(!scanning){
scanResultConsumer.onScanningStopped();
// Nullify the consumer in order to prevent UI crashes
scanResultConsumer = null;
} else{
scanResultConsumer.onScanningStarted();
}
});
}
}
最后,我們可以在任何需要的地方使用這個干凈的實現。 但請注意,必須提供上下文才能創建BleScanner
object。
public class MainActivity extends AppCompatActivity {
private BleScanner bleScanner;
private Button buttonScan
// Other codes...
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other codes...
bleScanner = new BleScanner(getApplicationContext());
// Other codes...
// For example if you want to start scanning on a button press
// Let's say you have a button called buttonScan and initiated it
buttonScan = findViewById(R.id.scan_button);
buttonScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bleScanner.scan(new ScanResultConsumer {
@Override
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi) {
// TODO Here you have a newly found device, do something.
}
@Override
q public void onScanningStarted() {
// TODO Scanning has just started, you may want to make some UI changes.
}
@Override
public void onScanningStopped() {
// TODO Scanning has just stopped, you may want to make some UI changes.
}
});
}
});
}
}
注意:我在普通編輯器中編寫了這段代碼,而不是在 Android Studio 中。 所以可能會有一些錯誤,如果有的話請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.