1

I'm new to reactive programming and this library and I'm trying to write an android app that will be able to send messages back and forth with other phones having the same app.

I've written a BleAdvertiser class which basically broadcasts the service UUID, I then use RxAndroidBle to identify other devices and attempt a connection. It would appear that I'm not actually making a connection, however, no errors are being thrown. What am I doing wrong?

On create I look for other phones with the same app. The Log statement that prints device name and MAC address is working as expected, I can see my test device. So I'm good there.

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    Intent intent = new Intent(this, BluetoothLeService.class);
    startService(intent);

    BleAdvertiser bleAdvertiser = new BleAdvertiser(new AdvertisingPacket(new Date(System.currentTimeMillis())));

    Thread t = new Thread(bleAdvertiser);
    t.start();

    SERVICE = UUID.fromString("e12b9e62-5c03-4ca2-88a5-1034e494f4dc");
    CHARACTERISTIC = UUID.fromString("201274dd-04dc-4ce6-943c-a830df466b33");
    PUUID = ParcelUuid.fromString(SERVICE.toString());

    rxBleClient = RxBleClient.create(this);

    scanSubscription = rxBleClient.scanBleDevices(
            new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(),
            new com.polidea.rxandroidble.scan.ScanFilter[]{new ScanFilter.Builder().setServiceUuid(PUUID).build()}
    )
        .subscribe(
            scanResult -> {
                //process new ble device.
                Log.d("Scan Result: ", scanResult.getBleDevice().getMacAddress() + " : " + scanResult.getBleDevice().getName() );
                ConnectToDevice(scanResult.getBleDevice());
            },
            throwable -> {
                Toast.makeText(this,throwable.getMessage(),Toast.LENGTH_LONG ).show();
            }
        );

    ...
}

So we try to connect:

private void ConnectToDevice(RxBleDevice device) {
    device.establishConnection(true)
        .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(CHARACTERISTIC)
            .doOnNext(bytes-> {
                //process read data (convert to message? )
                try {

                    Toast.makeText(this,"stuff happened in what we think is read bytes.",Toast.LENGTH_SHORT).show();
                    ArrayList<TinCanMessage> receivedMessages = (ArrayList<TinCanMessage>) Serializer.deserialize(bytes);

                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            })
            .flatMap(bytes -> {
                Toast.makeText(this,"stuff happened in what we think is write bytes.",Toast.LENGTH_SHORT).show();
                try {
                    return rxBleConnection.createNewLongWriteBuilder()
                        .setCharacteristicUuid(CHARACTERISTIC)
                        .setBytes(Serializer.serialize(new TinCanMessage("New messages will go here.", "kyle")))
                        .build();
                } catch (IOException e) {
                     e.printStackTrace();
                }
                return null;
            })
        )
        .subscribe(
            bytes -> {
                //Written data
                //assign to fragment and update adapter?
                Toast.makeText(this, "stuff happened in the written data section", Toast.LENGTH_SHORT).show();
            },
            throwable -> {
                Toast.makeText(this, throwable.getMessage(), Toast.LENGTH_LONG);
            }
        );
}

All help is appreciated.

Update

The devices actually are connecting. I've verified this by printing out to the debugger. I thought that I had an issue because I was trying to verify the connection with toast messages which are not displaying. This was discovered via comments from Darkusz Seweryn, so I've accepted his answer. My question was built on faulty assumptions :P

Community
  • 1
  • 1
Kyle
  • 366
  • 3
  • 17

1 Answers1

2

The above code looks good in terms that it does not seem to have any flaws.

Two things you should try:

  1. You should stop (unsubscribe from) the scan once you have scanned your device peripheral. You could do this automatically by adding .take(1) just before .subscribe() to .scanBleDevices().
  2. You have used RxBleDevice.establishConnection(true). The autoConnect=true changes the behaviour of connection. With autoConnect=true the connection may be established even many minutes after the call. You could connecting with autoConnect=false which would connect (or fail) in just 30 seconds.
Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thank you, I'll try these suggestions out and report back. I neglected to mention that BluetoothLeService is the GATT server for the application. I'm not sure if that changes anything. – Kyle Jan 05 '18 at 14:01
  • After altering those settings I'm getting an error: MainActivity has leaked IntentReceiver com.polidea.rxandroidble.RxBleAdapterStateObservable$1$1@6ebdcb3 that was originally registered here. Are you missing a call to unregisterReceiver()? Do you know why that could happen? – Kyle Jan 05 '18 at 14:11
  • 1
    It is a warning and a totally unrelated problem. You just forgot to unsubscribe from the `Subscription`s. Every call to `.subscribe()` returns a `Subscription` which is a handle that needs to be unsubscribed once it is not used anymore. If the Activity is being destroyed while a `BroadcastReceiver` is registered then the above warning is displayed. – Dariusz Seweryn Jan 05 '18 at 14:20
  • Ok. I've added the `take(1)` to the subscription that scans for devices. The only other subscription that I have is for actually connecting to the devices. But I'd like to leave that one open so that I can read and write data, is that not a good way to handle a line of communication between the devices? Also, the app crashes and that's the only red text I'm seeing. That's why I posted this specific stack trace message. – Kyle Jan 05 '18 at 14:25
  • 1
    The above is only a warning. There must be another reason for the crash. On Android if you need an object that needs to outlive the `Activity` it should not be bound to the `Activity` context. There are many architectural possibilities—Services, objects bound to `Application` lifecycle, etc. – Dariusz Seweryn Jan 05 '18 at 14:40
  • Ok thanks, I'll try moving this code into a service so that it's removed from the activities lifecycle. Do you mind if I ask for you to venture a guess as to why none of the Toasts pop up? – Kyle Jan 05 '18 at 14:52
  • 1
    They are not called from the main thread. All `RxAndroidBle` emissions happen on a single background thread and the UI (this include `Toast`s) must be called from the main thread. – Dariusz Seweryn Jan 05 '18 at 15:09