【问题标题】:Recieving incorrect results after BLE scan in android在android中进行BLE扫描后收到不正确的结果
【发布时间】:2021-02-02 17:30:59
【问题描述】:

我正在使用以下代码扫描附近的所有 BLE 设备

这里是代码。

    package com.btexample;

    import android.Manifest;
    import android.app.Activity;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothManager;
    import android.bluetooth.le.BluetoothLeScanner;
    import android.bluetooth.le.ScanCallback;
    import android.bluetooth.le.ScanFilter;
    import android.bluetooth.le.ScanResult;
    import android.bluetooth.le.ScanSettings;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.os.Handler;
    import androidx.annotation.NonNull;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;
    import com.facebook.react.bridge.ActivityEventListener;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import com.facebook.react.bridge.Callback;
    import com.facebook.react.bridge.WritableMap;
    import com.facebook.react.bridge.WritableNativeMap;
    import java.util.ArrayList;
    import java.util.List;
    
    
    public class MyBluetooth extends ReactContextBaseJavaModule implements ActivityEventListener {


    private ReactApplicationContext context;
    private static final int PERMISSION_REQUEST_CODE = 200;
    private static final int REQUEST_ENABLE_BT = 10112;
    private String[] permissions = new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN};
    private BluetoothManager bluetoothManager;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;
    private Callback scanSuccessCallBack,scanFailedCallBack;

    public MyBluetooth(ReactApplicationContext reactContext) {
        super(reactContext);
        context = reactContext;
        bluetoothManager =
                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
    }

    @NonNull
    @Override
    public String getName() {
        return "MyBluetooth";
    }




    @ReactMethod
    public void turnOnBluetooth() {
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            getCurrentActivity().startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }
    }

    private WritableMap convertJsonToMap(String name) {
        WritableMap map = new WritableNativeMap();
        map.putString("device_name", name);
        return map;
    }

    @ReactMethod
    private void requestGPSPermissions() {
        boolean permissionCheck =
                ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
                        != PackageManager.PERMISSION_GRANTED
                        &&
                        ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
                                != PackageManager.PERMISSION_GRANTED;


        if (permissionCheck) {

            ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{
                            Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION
                    },
                    PERMISSION_REQUEST_CODE);

        }
    }


    private boolean mScanning = false,isInvoked;
    private Handler handler;

    @ReactMethod
    public void scanLeDevice(int scanSeconds,Callback scanSuccessCallBack,Callback scanFailedCallBack) {
        if (!mScanning) {
            // Stops scanning after a pre-defined scan period.
            this.scanSuccessCallBack  = scanSuccessCallBack;
            this.scanFailedCallBack  = scanFailedCallBack;
            isInvoked = false;
            handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if(mScanning) {
                        bluetoothLeScanner.stopScan(leScanCallback);
                        mScanning = false;
                        if(!isInvoked){
                            scanFailedCallBack.invoke("No results found");
                        }
                    }
                }
            }, scanSeconds*1000);

            mScanning = true;
            List<ScanFilter> filters = new ArrayList<ScanFilter>();

            ScanSettings settings = new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                    .setReportDelay(1000)
                    .build();



            bluetoothLeScanner.startScan(filters, settings, leScanCallback);
        } else {
            mScanning = false;
            bluetoothLeScanner.stopScan(leScanCallback);
            scanFailedCallBack.invoke("Already scanning");
        }
    }


    private ScanCallback leScanCallback = new ScanCallback() {


        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            if(results.size() > 0 && mScanning) {
                mScanning = false;
                if(!isInvoked){
                    scanSuccessCallBack.invoke("results found");
                    isInvoked = true;
                    bluetoothLeScanner.stopScan(leScanCallback);
                    handler.removeCallbacksAndMessages(null);
                }
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            scanFailedCallBack.invoke("Scan failed");
        }
    };


    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {

    }

    @Override
    public void onNewIntent(Intent intent) {

    }
}

现在,

  1. 到目前为止,我没有任何外围设备,所以我将应用程序用作模拟器 链接:https://play.google.com/store/apps/details?id=com.ble.peripheral.sim&hl=en_IN&gl=US

  2. 现在在另一台设备上使用另一个应用程序https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=en_IN&gl=US 进行扫描,我收到了正确的结果

  3. 现在用我的应用再次扫描并接收结果。

现在主要的问题是。

  1. 当我关闭模拟器应用程序并卸载它时,我仍然在我的应用程序中得到它,但在那个扫描仪应用程序中没有。

  2. 另外,我的应用正在接收 deviceName=null 的随机设备(很多东西也是 null),这些随机设备不会出现在该扫描仪应用中。

  3. 当我从 starScan() 中删除过滤器和设置时;

    例如

    bluetoothLeScanner.startScan(filters, settings, leScanCallback);

onBatchScanResults() 的回调从未收到。

  1. 从设备中删除模拟器应用程序,在第三台设备上全新安装我的应用程序仍然会得到 1 或 2 个带有 null deviceName 的随机结果

注意:我正在 android studio 的调试器中检查结果

请帮助我提前谢谢

【问题讨论】:

    标签: android react-native bluetooth-lowenergy


    【解决方案1】:

    1-2。一旦您的扫描过滤器为空,您将获得所有扫描结果,因此在此应用程序中可能有不同的扫描过滤器。因此,您可以获得不同的扫描结果。

    1. 我认为您开始在回调方法onScanResult 中接收扫描结果,这在您的代码中没有实现。

    2. 可能是您周围的一些 BLE 设备 - 例如笔记本电脑、鼠标、电视等。

    【讨论】:

    【解决方案2】:

    我不确定什么对我有效,但这些是我改变的。

    1. 在两个清单中添加了一项权限并在运行时询问 ACCESS_BACKGROUND_LOCATION

    2. 停止使用 android.bluetooth.le,现在我正在使用这个库来这样做 https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library 两者的代码都是一样的,我们只需要从这个包中导入类。

    3. 在问题中我忘了提到我没有收到 onScanResult 的回调,即使这样做后我也无法获得扫描结果

    4. 所以我只是从 startScan 中删除了扫描过滤器和扫描设置,然后使用 onScanResult 来获取设备。

      这是最终代码

    import android.Manifest;
    import android.app.Activity;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothManager;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.location.LocationManager;
    import android.os.Handler;
    import android.provider.Settings;
    import android.util.Log;
    
    import androidx.annotation.NonNull;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;
    
    import com.facebook.react.bridge.ActivityEventListener;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import com.facebook.react.bridge.Callback;
    import com.facebook.react.bridge.WritableArray;
    import com.facebook.react.bridge.WritableMap;
    import com.facebook.react.bridge.WritableNativeArray;
    import com.facebook.react.bridge.WritableNativeMap;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
    import no.nordicsemi.android.support.v18.scanner.ScanCallback;
    import no.nordicsemi.android.support.v18.scanner.ScanFilter;
    import no.nordicsemi.android.support.v18.scanner.ScanResult;
    import no.nordicsemi.android.support.v18.scanner.ScanSettings;
    
    
    public class BleModule extends ReactContextBaseJavaModule {
    
    
        private ReactApplicationContext context;
        private static final int PERMISSION_REQUEST_CODE = 200;
        private static final int REQUEST_ENABLE_BT = 10112;
        private BluetoothManager bluetoothManager;
        private BluetoothAdapter bluetoothAdapter;
        private BluetoothLeScannerCompat scanner;
        private Callback  scanFailedCallBack;
        private LocationManager lm;
    
        /**
         * constructor to initialize other important instances with the received context
         *
         * @param reactContext
         */
        public BleModule(ReactApplicationContext reactContext) {
            super(reactContext);
            context = reactContext;
            bluetoothManager =
                    (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
            bluetoothAdapter = bluetoothManager.getAdapter();
            scanner = BluetoothLeScannerCompat.getScanner();
            lm = (LocationManager) context.getSystemService(context.LOCATION_SERVICE);
    
        }
    
        /**
         * overriding the method to return a name of module as string to be used inside react native codes to use this module.
         * this name should be same in both android and iOS
         *
         * @return
         */
        @NonNull
        @Override
        public String getName() {
            return "BleModule";
        }
    
    
        /**
         * this method is to check if the GPS is enabled and
         * invoking the react native callback with true or false
         *
         * @param callback
         */
        @ReactMethod
        public void checkGPS(Callback callback) {
            if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER))
                callback.invoke(false);
    
            else
                callback.invoke(true);
        }
    
        /**
         * this method is to launch GPS settings to enable
         */
        @ReactMethod
        public void enableGPS() {
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            getCurrentActivity().startActivity(intent);
        }
    
    
        /**
         * this method is launch the bluetooth enable dialog.
         */
        @ReactMethod
        public void turnOnBluetooth() {
            if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                getCurrentActivity().startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            }
        }
    
    
        /**
         * this method is to ask the Location permission from the user
         */
        @ReactMethod
        private void requestGPSPermissions() {
            boolean permissionCheck =
                    ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
                            != PackageManager.PERMISSION_GRANTED
                            &&
                            ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
                                    != PackageManager.PERMISSION_GRANTED
                            &&
                            ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                                    != PackageManager.PERMISSION_GRANTED;
    
    
            if (permissionCheck) {
    
                ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{
                                Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION
                        },
                        PERMISSION_REQUEST_CODE);
    
            }
        }
    
    
        private boolean mScanning = false, isInvoked;
        private Handler handler;
        private WritableArray reactResults;
        private ArrayList<String> duplicateChecker = new ArrayList<>();
    
        /**
         * this method is to scan the nearby ble devices
         *
         * @param scanSeconds         to run the scan till.
         * @param scanSuccessCallBack to be invoked with the result of the detected devices.
         * @param scanFailedCallBack  to be invoked when any failure occurs with the reason as a string.
         */
        @ReactMethod
        public void scanLeDevice(int scanSeconds, Callback scanSuccessCallBack, Callback scanFailedCallBack) {
            if (!mScanning) {
                reactResults = new WritableNativeArray();
                duplicateChecker.clear();
                // Stops scanning after a pre-defined scan period.
                this.scanFailedCallBack = scanFailedCallBack;
                isInvoked = false;
                handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (mScanning) {
                            scanner.stopScan(leScanCallback);
                            mScanning = false;
                            if (reactResults.size() > 0 )
                                scanSuccessCallBack.invoke(reactResults);
                            else scanFailedCallBack.invoke("No results found");
                        }
                    }
                }, scanSeconds * 1000);
    
                mScanning = true;
    
                scanner.startScan(leScanCallback);
            } else {
                mScanning = false;
                scanner.stopScan(leScanCallback);
                scanFailedCallBack.invoke("Already scanning");
            }
        }
    
    
        /**
         * this ScanCallback instance overriding two methods
         * <p>
         * this object is passed in startScan method inside the scanLeDevice method.
         */
        private ScanCallback leScanCallback = new ScanCallback() {
    
            @Override
            public void onScanResult(int callbackType, @NonNull ScanResult device) {
                String address = device.getDevice().getAddress();
                if(duplicateChecker.contains(address))
                    return;
                else duplicateChecker.add(address);
                WritableMap map = new WritableNativeMap();
                if (device.getDevice() != null)
                    map.putString("device_name", device.getDevice().getName());
                else map.putString("device_name", device.getScanRecord().getDeviceName());
                map.putString("device_address", address);
    
                reactResults.pushMap(map);
            }
    
    
            /**
             * this will be triggered if the scan failed. then trigger react native's scanFailedCallBack.
             * @param errorCode
             */
            @Override
            public void onScanFailed(int errorCode) {
                scanFailedCallBack.invoke("Scan failed");
            }
        };
    
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-24
      • 1970-01-01
      • 2020-11-25
      • 1970-01-01
      相关资源
      最近更新 更多