Mendeteksi perubahan nama perangkat BLE di Android

Dec 01 2020

Kami memiliki perangkat BLE yang sedang kami kerjakan yang mengeluarkan data melalui nama perangkat. Perangkat berfungsi dengan baik dan dapat dilihat mengubah nama dengan benar menggunakan aplikasi seperti nRF Connect. Namun, kami kesulitan melakukan hal yang sama di aplikasi Android kami sendiri. Kami dapat mendeteksi perangkat dengan baik, tetapi mereka hampir tidak akan pernah melewati nama asli yang mereka berikan.

Kode yang saya mulai memiliki loop yang dimulai di onResume () yang memindai menggunakan BluetoothLeScanner dan fungsi startScan ().

public void BLEScan(final boolean enable){

    final int SCAN_PERIOD = 12000;
    final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

    if (enable) {
        Log.d(TAG, "Starting Scan");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(false);
                invalidateOptionsMenu();
            }
        }, SCAN_PERIOD);
        
        bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
        bluetoothLeScanner.startScan(mLeScanCallback);
    } else {
        Log.d(TAG, "Stopping Scan");
        bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
        bluetoothLeScanner.stopScan(mLeScanCallback);
        
        mHandler.removeCallbacksAndMessages(null);

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(true);
            }
        }, SCAN_PERIOD);
    }
}

Dan itu terdeteksi di mLeScanCallback yang memfilter berdasarkan perangkat. Pada dasarnya, jika belum pernah melihat perangkat sebelumnya, ia menambahkannya ke daftar Beacon. Tapi, jika telah melihatnya sebelumnya, ia memperbarui nilai dengan fungsi seen () Beacon dengan nama Beacon seperti yang terlihat dari situlah informasi akan berasal. Dalam kedua kasus, itu akan memperbarui adaptornya untuk mengisi informasi baru.

private ScanCallback mLeScanCallback =
    new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            final ScanResult res2 = result;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    BluetoothDevice device = res2.getDevice();
                    String address = device.getAddress();
                    
                    if (device.getName() != null){
                        if (device.getName().contains("ABT:")){
                            if (!mLeBeacons.containsKey(address)){
                                BleBeacon beacon = new BleBeacon(device.getName(), address);
                                bleList.add(beacon);
                                adapter.notifyDataSetChanged();

                                mLeBeacons.put(device.getAddress(), beacon);
                            } else {
                                for (int x = 0; x < bleList.size(); x++){
                                    if (device.getAddress().equals(bleList.get(x).getMAC())){
                                        bleList.get(x).seen(device.getName());
                                        adapter.notifyDataSetChanged();
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
    };

Namun, bahkan setelah memperbarui nama, mLeScanCallback hanya akan mengembalikan nama aslinya.

Setelah beberapa pencarian melalui sini, hal yang terus saya temukan adalah menggunakan fungsi fetchUuidsWithSdp () dan maksud seperti ACTION_FOUND, ACTION_UUID, ACTION_DISCOVERY_FINISHED, dan ACTION_NAME_CHANGED untuk melihat nama berubah dengan benar. Jadi, saya menambahkan fetchUuidsWithSdp () ke mLeScanCallback. Namun, meskipun ini akan memicu Intent, namanya masih belum diperbarui. Saya mencoba memanggil fetchUuidsWithSdp () dalam maksud sebenarnya, tetapi itu juga tidak membantu. Cukup aneh, ACTION_NAME_CHANGED terkadang aktif jika saya mematikan layar ponsel saya atau berjalan cukup jauh dari perangkat BLE. Namun, yang dilakukan onPause () hanyalah panggilan super.onPause()dan BLEScan(false). Dan, karena ini adalah hal-hal yang sudah saya lakukan dalam loop saya, saya tidak yakin bagaimana memasukkan ini ke dalam kode saya saat ia aktif.

Setelah mencari lebih lanjut, saya menemukan bahwa, untuk menggunakan fungsi fetchUuidsWithSdq (), Anda perlu menggunakan fungsi startDiscovery () BluetoothAdapter Anda. Jadi, saya mengubah BLEScan untuk menggunakannya, memotong mLeScanCallback sepenuhnya.

public void BLEScan(final boolean enable){
    final int SCAN_PERIOD = 12000;
    if (enable) {
        Log.d(TAG, "Starting Scan");
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(false);
                invalidateOptionsMenu();
            }
        }, SCAN_PERIOD);

        if (mBluetoothAdapter.isDiscovering()){
            mBluetoothAdapter.cancelDiscovery();
        }
        mBluetoothAdapter.startDiscovery();
    } else {
        Log.d(TAG, "Stopping Scan");
        mBluetoothAdapter.cancelDiscovery();

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                BLEScan(true);
            }
        }, SCAN_PERIOD);
    }
}

Namun, sementara Intent diaktifkan sebelumnya, sekarang tidak, meskipun mereka ada di filter maksud saya. Setelah melakukan beberapa pencarian lagi, saya menemukan beberapa orang mengatakan bahwa startDiscovery () tidak berfungsi dengan perangkat Le. Jadi, saya mencari apa yang seharusnya Anda gunakan ... yang membawa saya kembali ke startScan dengan BluetoothLeScanner. Pada saat itu, saya menyadari bahwa saya telah berputar-putar dan membutuhkan bantuan.

Saya membahas seluruh proses saya karena, di suatu tempat, saya melewatkan sesuatu. Aku hanya tidak tahu dimana itu. Haruskah saya menggunakan startScan ()? Apakah saya tidak menggunakan startDiscovery () dengan benar? Atau apakah ada hal lain yang harus saya gunakan seluruhnya? Fakta bahwa ACTION_NAME_CHANGED kadang-kadang disulut membuat saya ingin kembali ke situ, tapi bagaimana cara membuatnya bekerja setiap saat saat perangkat aktif?

Jawaban

M.Kotzjan Dec 02 2020 at 21:16

Saya pikir Anda mungkin memiliki masalah dengan caching di android. Lihat jawaban ini di sini untuk kemungkinan solusi:https://stackoverflow.com/a/50745997/7473793

Seth Dec 03 2020 at 23:48

Meskipun saya tidak menemukan kesalahan yang saya lakukan dalam kode asli saya (meskipun sepertinya Android mungkin saja rusak dalam hal ini), saya menemukan solusinya. NFC Toolbox nRF memiliki kode sumbernya tersedia dan memindai dalam batch dengan filter tambahan serta pemindai Le sendiri dari perpustakaan Nordik. Inilah tampilan fungsi pindai saya sekarang:

    public void BLEScan(final boolean enable){

        final BluetoothLeScannerCompat bluetoothLeScanner = BluetoothLeScannerCompat.getScanner();
        final ScanSettings settings = new ScanSettings.Builder()
                .setLegacy(false)
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setReportDelay(1000).setUseHardwareBatchingIfSupported(false).build();
        final List<ScanFilter> filters = new ArrayList<>();
        ParcelUuid Uuid = new ParcelUuid(UUID.fromString(uuidString));
        filters.add(new ScanFilter.Builder().setServiceUuid(Uuid).build());

        if (enable) {
            Log.d(TAG, "Starting Scan");
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    BLEScan(false);
                    invalidateOptionsMenu();
                }
            }, SCAN_PERIOD);

            bluetoothLeScanner.startScan(filters, settings, mLeScanCallback);
        } else {
            BLEService.refreshGatt();
            Log.d(TAG, "Stopping Scan");
            bluetoothLeScanner.stopScan(mLeScanCallback);

            mHandler.removeCallbacksAndMessages(null);

            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    BLEScan(true);
                }
            }, SCAN_PERIOD);
        }
    }

Dan panggilan balik saya terlihat seperti ini:

    private no.nordicsemi.android.support.v18.scanner.ScanCallback mLeScanCallback =
            new no.nordicsemi.android.support.v18.scanner.ScanCallback() {
                @Override
                public void onBatchScanResults(@NonNull final List<no.nordicsemi.android.support.v18.scanner.ScanResult> results) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            for (final no.nordicsemi.android.support.v18.scanner.ScanResult result : results){
                                BluetoothDevice device = result.getDevice();

                                if (device != null){
                                    String address = device.getAddress();
                                    String name = result.getScanRecord() != null ? result.getScanRecord().getDeviceName() : null;

                                    if (!mLeBeacons.containsKey(address)) {
                                        BleBeacon beacon = new BleBeacon(device.getName(), address);
                                        bleList.add(beacon);
                                        adapter.notifyDataSetChanged();

                                        mLeBeacons.put(device.getAddress(), beacon);
                                    } else {
                                        for (int x = 0; x < bleList.size(); x++){
                                            if (device.getAddress().equals(bleList.get(x).getMAC())){
                                                bleList.get(x).seen(device.getName());
                                                adapter.notifyDataSetChanged();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });
                }

Sepertinya ini akan berhasil. Anda perlu menambahkan berikut ini ke dependensi Anda.

implementation 'no.nordicsemi.android.support.v18:scanner:1.4.2'

Setelah itu, maksud nama saya ACTION_NAME_CHANGE terpicu dengan benar dan data diperbarui.

Saya tidak yakin apakah ini cara yang berbeda untuk mengambil nama dari hasil atau apakah itu pemindaian batch. Tetapi, bahkan jika Nordic tidak menggunakan pustaka Android BLE standar, saya rasa ini adalah cara terbaik untuk melakukannya.