Thomas Morris
Thomas Morris

Reputation: 822

Wait statement that are sequential

I have an issue leading to the requirement of needing to wait sequentially from going from one thing into the next. I presently do this by setting 3 runnables with different delays to allow for a sequential flow of data to appear on my bluetooth connection. However, whilst this does work I feel there must be a better / cleaner implementation of this. My present code is below.

My code works like this:

Please could you give me some suggestions as to how I can perform my write functions one after another in a better manner.

Handler h =new Handler() ;
h.postDelayed(new Runnable() {
    public void run() {
        Log.d(TAG, "Write 1");
        mBluetoothLeService.writeCharacteristic(10);
    }
}, 1000);

Handler h1 =new Handler() ;
final int Command_to_run = value;
h1.postDelayed(new Runnable() {
    public void run() {
        Log.d(TAG, "Write 2");
        mBluetoothLeService.writeCharacteristic(Command_to_run);
    }
}, 2000);

Handler h2 =new Handler() ;
h2.postDelayed(new Runnable() {
    public void run() {
        Log.d(TAG, "Write 3");
        mBluetoothLeService.writeCharacteristic(20);
    }
}, 3000);

Write code

 public void writeCharacteristic(int Data) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }

        byte[] value = intToByteArray(Data);

        BluetoothGattService mCustomService = 
        mBluetoothGatt.getService(UUID.fromString("f3641400-00b0-4240-ba50- 
        05ca45bf8abc"));
        if(mCustomService == null){
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
        /*get the read characteristic from the service*/
        BluetoothGattCharacteristic characteristic = 
        mCustomService.getCharacteristic(UUID.fromString("f3641401-00b0-4240- 
        ba50-05ca45bf8abc"));
        characteristic.setValue(value);
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

Upvotes: 0

Views: 78

Answers (3)

Furkan Yurdakul
Furkan Yurdakul

Reputation: 3167

I think mBluetoothLeService.writeCharacteristic(10); calls like these already blocks the thread so using them in order without the need of handlers can be your solution. I don't think that function is asynchronous, so if it returns true, you can write the next one. They are boolean functions so if it returns true you can switch to next one.

I've examined the source code and if it throws an exception inside it, it returns false. Otherwise, it returns if it was successful or not.

Side note: This behavior might differ on different API versions, the source code I've looked into was for API 29. Though, I believe the behavior would be the same, except you might need to wrap the mBluetoothLeService.writeCharacteristic(10); calls to a try-catch block.

I have to edit this since the answer is wrong, the boolean return value is not enough to determine whether the operation was successful. The operation is indeed asynchronous but there is a callback that you can use (this callback) to see if the write operation was successful, and then move on to the next one.

Check this answer for more information, and, if possible, remove the tick from this one please.

Upvotes: 1

Emil
Emil

Reputation: 18482

Android's BLE API is fully asynchronous and there are no blocking methods. The methods will return true if the operation was initiated successfully and false otherwise. In particular, false will be returned if there already is an operation ongoing.

When you call requestMtu, writeCharacteristic, readCharacteristic and so on, the corresponding callback onMtuChanged, onCharacteristicWrite, onCharacteristicRead will be called when the operation is complete. Note that this usually means a roundtrip to the remote device, which might take different amount of time to complete depending on how noisy the environment is and which connection parameters you have, so there is never a good idea to sleep or delay some fixed amount of time and assume the operation has then completed.

To make the code structure a bit better and avoiding the "callback hell", you could for example implement a (thread-safe) GATT queue which is later used by your application. This way you can just push what you want in the queue and let your GATT queue library handle the dirty stuff. Or you can use some Android BLE library that already does this.

See https://medium.com/@martijn.van.welie/making-android-ble-work-part-3-117d3a8aee23 for a more thorough discussion.

Upvotes: 1

If the work you want to perform sequentially can be done asynchronously, you can consider the new WorkManager included in Android Jetpack. With WorkManager you can organize all your work very smartly, as per the documentation you can do it as follows:

WorkManager.getInstance(myContext)
   // Candidates to run in parallel
   .beginWith(listOf(filter1, filter2, filter3))
   // Dependent work (only runs after all previous work in chain)
   .then(compress)
   .then(upload)
   // Don't forget to enqueue()
   .enqueue()

The library takes good care of the order of the execution for you. You can find more information on this matter here: https://developer.android.com/topic/libraries/architecture/workmanager/how-to/chain-work

Upvotes: 0

Related Questions