Search code examples
kotlinbluetoothencapsulationkotlin-coroutines

Moving functionalty with background tasks from main activity into it's own separate class


In my kotlin app I want to move all the bluetooth related functionality into it's own class. While it is clear to me how to move a method into a separate class, I do not clearly see how to move a method into a class that updates data in the background.

How do I trigger the main activity after the bluetooth class receives some data in the background (currently a coroutine running a read loop) Here is for example the function that reads incoming bluetooth data. It works fine but updates the UI directly:

private suspend fun readBT() {
    val buffer = ByteArray(1024)
    var receivedSoFar = ""

    /* Keep listening to the InputStream until an exception occurs or cancel is set. */
    while (state == "connected") {
        try {
            /* nrOfBytes will hold the number of bytes in the buffer */
            val nrOfBytes = inStream?.read(buffer)
            /*  add buffer content to current line */
            receivedSoFar += String(buffer, 0, nrOfBytes!!)
            /* until (incl) newline character */
            if (receivedSoFar.contains("\n")) {
                withContext(Dispatchers.Main) { txt_state.text = (receivedSoFar.dropLast(1)) }
                receivedSoFar = ""
            }
        } catch (e: Exception) {
            if (state == "connected") {
                withContext(Dispatchers.Main) { txt_state.text = "error while reading bt data from Wallie" }
                state = "error_during_read"
            }
        }
    }
}

Solution

  • Your MainActivity and the new BluetoothClass should communicate through a callback. The callback can be a parameter of BluetoothClass' constructor:

    class BluetoothClass(
      private val updateText: (String) -> Unit,
      ...
    )
    

    and MainActivity will pass in an implementation of that lambda when instantiating BluetoothClass:

    bluetoothClass = BluetoothClass(updateText = { text -> txt_state.text = text })
    

    Lastly, BluetoothClass should invoke the lambda whenever it needs to update the text:

    if (receivedSoFar.contains("\n")) {
      withContext(Dispatchers.Main) { updateText(receivedSoFar.dropLast(1)) }
      receivedSoFar = ""
    }