Kotlin: Read bluetooth data real time - kotlin

So I'm fairly new to Kotlin and Bluetooth, I am making and Android app to read the data from my Arduino sensors in real time so that track of the current readings of each sensor. Right now my read works and I have tried using Handler to keep reading but my problem is that when it keeps reading it also lags the application, it doesn't crash or anything and reads fine, but inputting texts and going to other activities takes a while, so I can confidently say that it must be my Handler that's lagging the application. So my question, is there a better way of making handler not lag my app but still read the sensors? Or is there other ways that I can use other than Handler?
companion object {
var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
var m_bluetoothSocket: BluetoothSocket? = null
lateinit var m_progress: ProgressDialog
lateinit var m_bluetoothAdapater: BluetoothAdapter
var m_isConnected: Boolean = false
lateinit var m_address: String
private val mmBuffer: ByteArray = ByteArray(1024)
private const val TAG = "MY_APP_DEBUG_TAG"
var buffer = ByteArray (1024)
var bytes: Int = 0
lateinit var arrVal: Array<String>
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.control_layout)
m_address = intent.getStringExtra(BluetoothActivity.EXTRA_ADDRESS)!!
ConnectToDevice(this).execute()
val handler = Handler()
val runnable = object : Runnable{
override fun run() {
readSensors()
handler.postDelayed(this, 2000)
}
}
Handler().postDelayed({
Log.e("Thread", runnable.run().toString())
val intent = Intent(this, MainActivity::class.java)
finish()
startActivity(intent)
}, 5000)
}
Here is oncreate() where I have placed the Handler runnable and companion object arrVal so I can just use the variable in other activities to display the data.
private fun readSensors() {
val bluetoothSocketInputStream = m_bluetoothSocket!!.inputStream
val delim = ","
var readMessage: String = ""
//Loop to listen for received bluetooth messages
while (!(readMessage.contains('*'))) {
try {
bytes = bluetoothSocketInputStream.read(buffer)
readMessage = String(buffer, 0, bytes)
arrVal = readMessage.split(delim).toTypedArray()
} catch (e: IOException) {
e.printStackTrace()
break
}
}
}
Here is my function for reading the sensors.

Handler is not a Thread. See handler constructor source code, it is attached to Looper of the current thread. So your code in Runnable is posted to message queue of main thread and executed from there.
To prevent main thread from freezing, you have to do all IO and CPU consuming operations on separate threads.
Java high level API to do this is called Executors.
Kotlin way is Coroutines.
You can archive expected behaviour with Handler API (by creating a thread with looper and using Handler to post messages to that thread) but don't waste your time on this.

Related

Kotlin Coroutines - cannot return object from room db

I'm not super sure what I'm doing here so go easy on me:
I'm making a wordle clone and the word that is to be guessed is stored as a string in a pre-populated room database which I am trying to retrieve to my ViewModel and currently getting:
"StandaloneCoroutine{Active}#933049a"
instead of the actual data.
I have tried using LiveData which only returned null which as far as I'm aware is because it was not observed.
Switched to coroutines which seemed to make more sense if my UI doesn't need the data anyway.
I ended up with this so far:
DAO:
#Dao
interface WordListDao {
#Query("SELECT word FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
suspend fun readWord(): String
// tried multiple versions here only string can be converted from Job
// #Query("SELECT * FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
// fun readWord(): LiveData<WordList>
// #Query("SELECT word FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
// fun readWord(): WordList
}
repository:
class WordRepository(private val wordListDao: WordListDao) {
//val readWordData: String = wordListDao.readWord()
suspend fun readWord(): String {
return wordListDao.readWord()
}
}
model:
#Entity(tableName = "wordlist")
data class WordList(
#PrimaryKey(autoGenerate = true)
val id: Int,
val word: String,
var used: Boolean
)
VM:
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository
private var word: String
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
word = viewModelScope.launch {
repository.readWord()
}.toString()
Log.d("TAG", ": $word") // does nothing?
}
println(word) // StandaloneCoroutine{Active}#933049a
}
This is the only way that I have managed to not get the result of:
Cannot access database on the main thread
There is a better way to do this, I just can't figure it out.
You can access the return value of repository.readWord() only inside the launch block.
viewModelScope.launch {
val word = repository.readWord()
Log.d("TAG", ": $word") // Here you will get the correct word
}
If you need to update you UI when this word is fetched from database, you need to use an observable data holder like a LiveData or StateFlow.
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository
private val _wordFlow = MutableStateFlow("") // A mutable version for use inside ViewModel
val wordFlow = _word.asStateFlow() // An immutable version for outsiders to read this state
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
viewModelScope.launch {
_wordFlow.value = repository.readWord()
}
}
}
You can collect this Flow in your UI layer,
someCoroutineScope {
viewModel.wordFlow.collect { word ->
// Update UI using this word
}
}
Edit: Since you don't need the word immediately, you can just save the word in a simple global variable for future use, easy.
class HomeViewModel(application: Application) : ViewModel() {
private lateinit var repository: WordRepository
private lateinit var word: String
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
viewModelScope.launch {
word = repository.readWord()
}
// word is not available here, but you also don't need it here
}
// This is the function which is called when user types a word and presses enter
fun submitGuess(userGuess: String) {
// You can access the `word` here and compare it with `userGuess`
}
}
The database operation will only take a few milliseconds to complete so you can be sure that by the time you actually need that original word, it will have been fetched and stored in the word variable.
(Now that I'm at a computer I can write a bit more.)
The problems with your current code:
You cannot safely read from the database on the main thread synchronously. That's why the suspend keyword would be used in your DAO/repository. Which means, there is no way you can have a non-nullable word property in your ViewModel class that is initialized in an init block.
Coroutines are asychronous. When you call launch, it is queuing up the coroutine to start its work, but the launch function returns a Job, not the result of the coroutine, and your code beneath the launch call continues on the same thread. The code inside the launch call is sent off to the coroutines system to be run and suspend calls will in most cases, as in this case, be switching to background threads back and forth. So when you call toString() on the Job, you are just getting a String representation of the coroutine Job itself, not the result of its work.
Since the coroutine does its work asynchronously, when you try to log the result underneath the launch block, you are logging it before the coroutine has even had a chance to fetch the value yet. So even if you had assigned the result of the coroutine to some String variable, it would still be null by the time you are logging it.
For your database word to be usable outside a coroutine, you need to put it in something like a LiveData or SharedFlow so that other places in code can subscribe to it and do something with the value when it arrives.
SharedFlow is a pretty big topic to learn, so I'll just use LiveData for the below samples.
One way to create a LiveData using your suspend function to retrieve the word is to use the liveData builder function, which returns a LiveData that uses a coroutine under the hood to get the value to publish via the LiveData:
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository = WordListDatabase.getDatabase(application)
.wordDb.wordlistDao()
.let(::WordRepository)
private val word: LiveData<String> = liveData {
repository.readWord()
}
val someLiveDataForUi: LiveData<Something> = Transformations.map(word) { word ->
// Do something with word and return result. The UI code can
// observe this live data to get the result when it becomes ready.
}
}
To do this in a way that is more similar to your code (just to help with understanding, since this is less concise), you can create a MutableLiveData and publish to the LiveData from your coroutine.
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository
private val word = MutableLiveData<String>()
init {
val wordDb = WordListDatabase.getDatabase(application)
val wordDao = wordDb.wordlistDao()
repository = WordRepository(wordDao)
viewModelScope.launch {
word.value = repository.readWord()
}
}
val someLiveDataForUi: LiveData<Something> = Transformations.map(word) { word ->
// Do something with word and return result. The UI code can
// observe this live data to get the result when it becomes ready.
}
}
If you're not ready to dive into coroutines yet, you can define your DAO to return a LiveData instead of suspending. It will start reading the item from the database and publish it through the live data once it's ready.
#Dao
interface WordListDao {
#Query("SELECT word FROM wordlist WHERE used = 0 ORDER BY id DESC LIMIT 1")
fun readWord(): LiveData<String>
}
class HomeViewModel(application: Application) : ViewModel() {
private val repository: WordRepository = WordListDatabase.getDatabase(application)
.wordDb.wordlistDao()
.let(::WordRepository)
private val word: LiveData<String> = repository.readWord()
//...
}
The return value is as expected, because launch does always return a Job object representing the background process.
I do not know how you want to use the String for, but all operations which should be done after receiving the String must be moved inside the Coroutine or in a function which is called from the Coroutine.
viewModelScope.launch {
val word = repository.readWord()
// do stuff with word
// switch to MainThread if needed
launch(Dispatchers.Main){}
}

Flow message not delivered in unit test

I have a consumer that reads messages off MutableSharedFlow (which acts as an EventBus in my application). I am trying to write a unit test to show that passing a message into the Flow triggers my Listener.
This is my Flow definition:
class MessageBus {
private val _messages = MutableSharedFlow<Message>()
val messages = _messages.asSharedFlow()
suspend fun send(message: Message) {
_messages.emit(message)
}
}
Here is the Listener:
class Listener(private val messageBus: MessageBus) {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
init {
scope.launch {
messageBus.messages.collectLatest { message ->
when (message) {
is CustomMessage -> handleCustomMessage(message)
}
}
}
}
And finally here is my unit test:
class CommandTest {
#Test
fun `should process CustomMessage`(): Unit = runBlocking {
val messageBus = MessageBus()
val listener = Listener(messageBus)
messageBus.send(CustomMessage("test command"))
//argumentCaptor...verify[removed for brevity]
}
}
Unfortunately the above code does not trigger the break point in my Listener (breakpoint on line init is triggered, but a message is never received and no breakpoints triggered in the collectLatest block).
I even tried adding a Thread.sleep(5_000) before the verify statement but the result is the same. Am I missing something obvious with how coroutines work?
Edit: if it matters this is not an Android project. Simply Kotlin + Ktor
I imagine that since the code is in the init block in the Listener once you initialize val listener = Listener(messageBus, this) in the test it reads all messages and at this point you have none then in the next line you emit a message messageBus.send(CustomMessage("test command")) but your launch block should have finished by then. You can emit the message first or place your launch in an loop or in a different method that can be called after you emit the message
First of all I would recomend reading this article about how to test flows in Android.
Secondly in your example the issues arise from having the scope inside the Listener hardcoded. You should pass the scope as a parameter and inject it in the test:
class Listener(private val messageBus: MessageBus, private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()))
class CommandTest {
#Test
fun `should process CustomMessage`(): Unit = runBlockingTest {
val messageBus = MessageBus()
val listener = Listener(messageBus, this)
messageBus.send(CustomMessage("test command"))
//argumentCaptor...verify[removed for brevity]
}
}
I would also recomend using runBlockingTest instead of runBlocking so your tests don't have to actually wait. It will also fail in case any coroutines are left running once the test finishes.
You could use something like this
class Emitter {
private val emitter: MutableSharedFlow<String> = MutableSharedFlow()
suspend fun publish(messages: Flow<String>) = messages.onEach {
emitter.emit(it)
}.collect()
fun stream(): Flow<String> = emitter
}
the collect at the end of your onEach will be used to trigger the collection initially as a terminal operation... I need further understanding on emit because it does not work as I expect in all cases and when used in this way you have initially it does not post anything in your Flow unless you collect first to process
Then in your collector itself
class Collector {
suspend fun collect(emitter: Emitter): Unit = coroutineScope {
println("Starting collection...")
emitter.stream().collect { println("collecting message: $it") }
}
}
then your main (or test)
fun main() = runBlocking {
withContext(Dispatchers.Default + Job()) {
val emitter = Emitter()
val collector = Collector()
launch {
collector.collect(emitter)
}
emitter.publish(listOf("article#1", "article#2", "article#3", "article#4").asFlow())
}
}
output:
Starting collection...
collecting message: article#1
collecting message: article#2
collecting message: article#3
collecting message: article#4

Not able to wait for the coroutine to finish using join() in kotlin?

I am new to kotlin and coroutines.I have been working on a client-server part of an android app.I am trying to get the routercapabilities using the following code
suspend fun getRouterCapabilities(): String? = coroutineScope {
lateinit var routerRtpCapabilities: JSONObject
val job = launch {
socket?.emit("getRouterRtpCapabilities", "", Ack { args ->
routerRtpCapabilities = args[0] as JSONObject
Log.d(TAG, routerRtpCapabilities!!.getString("error"))
})
}
job.join()
Log.d(TAG, "$routerRtpCapabilities")
return#coroutineScope routerRtpCapabilities.toString()
}
In the above code, I am able to store and print the value of routerRtpCapabilites inside the emit().But I got below mentioned error when I tried to access it from outside the emit().
kotlin.UninitializedPropertyAccessException: lateinit property routerRtpCapabilities has not been initialized
And also I am not sure about the way coroutines are used here.Please correct me If I have missed something central.
The reason your job.join() doesn't wait for the acknowledgement is that the emit function launches an asynchronous action and immediately returns. The callback with the result is called later. But the coroutine job doesn't know to wait for some callback, so it immediately completes before the acknowledgement is received sometime later.
When you have a library with an asynchronous function with a callback, you can convert it to a suspend function so it can easily be used in coroutines. Here is how you could convert this emit function:
/** Emits the [event] and suspends until acknowledgement is received. Returns the
acknowledgement arguments. */
suspend fun Socket.awaitEmit(event: String, vararg arg: Any): Array<out Any?> =
suspendCoroutine { continuation ->
emit(event, *arg, Ack { args ->
continuation.resume(args)
})
}
Some libraries such as Retrofit and Firebase come with suspend function versions of their asynchronous functions, and for those, the above step would be necessary.
A proper suspend function can be called from other suspend functions and in coroutines without doing anything special like wrapping it in another coroutine or withContext block.
suspend fun getRouterCapabilities(): String? = routerCababilitiesMutex.withLock {
val acknowledgement = socket?.awaitEmit("getRouterRtpCapabilities", "")
acknowledgement ?: return null // must have null socket
val result = acknowledgement[0] as? JSONObject
Log.d(TAG, result?.getString("error") ?: "result is null")
return result?.toString()
}
The !! should be changed because it’s unsafe.
To call your suspend function from an Activity, you would do it in a lifecycleScope coroutine, for example:
mic.setOnClickListener{
lifecycleScope.launch {
val routerCap = someOtherClass.getRouterCapabilities()
// Do stuff with routerCap here.
}
}
Edit: How I would cache the value in your view model class. You can use the mutex to ensure it isn't retrieved redundantly.
private var routerCapabilities: String? = null
private val routerCapabilitiesMutex = Mutex()
suspend fun getRouterCapabilities(): String? = routerCapabilitiesMutex.withLock {
routerCapabilities?.let { return it }
val acknowledgement = socket?.awaitEmit("getRouterRtpCapabilities", "")
acknowledgement ?: return null // must have null socket
val result = acknowledgement[0] as? JSONObject
Log.d(TAG, result?.getString("error") ?: "result is null")
result?.toString().also { routerCapabilities = it }
}

Asynchronous XML parsing with SAX in Kotlin

I have a SAX parser reading an XML file (specifically, a .xlsx file) and returning the content as a list of Row objects: it's roughly like this
fun readExcelContent(data: InputStream) {
val pkg = OPCPackage.open(file)
val reader = XSSFReader(pkg)
val sst = reader.sharedStringsTable
val parser = XMLHelper.newXMLReader()
val handler = ExcelSheetHandler(sst)
parser.contentHandler = handler
val sheet = reader.sheetsData.next()
val source = InputSource(sheet)
parser.parse(source)
return handler.content
}
Where ExcelSheetHandler is a class that extends DefaultHandler and takes care of filling up a list:
class ExcelSheetHandler(sst: SharedStringsTable): DefaultHandler() {
private val content = mutableListOf<Row>()
#Throws(SAXException::class)
override fun endElement(uri: String?, localName: String?, name: String) {
// If it's the end of a content element, add a row to content
}
}
It's basically a slight modification of the event model example in the Apache POI howto.
I was wondering if there was a way to have readExcelContent return an asynchronous object, such as a flow, and emit the rows to its client as soon as they are read rather than having to wait for the whole file to be processed.
I'd prefer kotlinx.coroutines.Channel over kotlinx.coroutines.Flow for this use case since this is a hot stream of data triggered by the parse() method. Here's what Kotlin Language Guide states.
Flows are cold streams similar to sequences — the code inside a flow
builder does not run until the flow is collected
Here's a quick implementation that you can try.
class ExcelSheetHandler : DefaultHandler() {
private val scope = CoroutineScope(Dispatchers.Default)
private val rows = Channel<Row>()
override fun endDocument() {
// To avoid suspending forever!
rows.close()
}
#Throws(SAXException::class)
override fun endElement(uri: String?, localName: String?, name: String) {
readRow(uri, localName, name)
}
private fun readRow(uri: String?, localName: String?, name: String) = runBlocking {
// If it's the end of a content element, add a row to content
rows.send(row)
}
// Client code - if it needs to be somewhere else
// you can expose a reference to Channel object
private fun processRows() = scope.launch {
for(row in rows) {
// Do something
println(row)
}
}
}

How to suspend kotlin coroutine until notified

I would like to suspend a kotlin coroutine until a method is called from outside, just like the old Java object.wait() and object.notify() methods. How do I do that?
Here: Correctly implementing wait and notify in Kotlin is an answer how to implement this with Kotlin threads (blocking). And here: Suspend coroutine until condition is true is an answer how to do this with CompleteableDeferreds but I do not want to have to create a new instance of CompleteableDeferred every time.
I am doing this currently:
var nextIndex = 0
fun handleNext(): Boolean {
if (nextIndex < apps.size) {
//Do the actual work on apps[nextIndex]
nextIndex++
}
//only execute again if nextIndex is a valid index
return nextIndex < apps.size
}
handleNext()
// The returned function will be called multiple times, which I would like to replace with something like notify()
return ::handleNext
From: https://gitlab.com/SuperFreezZ/SuperFreezZ/blob/master/src/superfreeze/tool/android/backend/Freezer.kt#L69
Channels can be used for this (though they are more general):
When capacity is 0 – it creates RendezvousChannel. This channel does not have any buffer at all. An element is transferred from sender to receiver only when send and receive invocations meet in time (rendezvous), so send suspends until another coroutine invokes receive and receive suspends until another coroutine invokes send.
So create
val channel = Channel<Unit>(0)
And use channel.receive() for object.wait(), and channel.offer(Unit) for object.notify() (or send if you want to wait until the other coroutine receives).
For notifyAll, you can use BroadcastChannel instead.
You can of course easily encapsulate it:
inline class Waiter(private val channel: Channel<Unit> = Channel<Unit>(0)) {
suspend fun doWait() { channel.receive() }
fun doNotify() { channel.offer(Unit) }
}
It is possible to use the basic suspendCoroutine{..} function for that, e.g.
class SuspendWait() {
private lateinit var myCont: Continuation<Unit>
suspend fun sleepAndWait() = suspendCoroutine<Unit>{ cont ->
myCont = cont
}
fun resume() {
val cont = myCont
myCont = null
cont.resume(Unit)
}
}
It is clear, the code have issues, e.g. myCont field is not synchonized, it is expected that sleepAndWait is called before the resume and so on, hope the idea is clear now.
There is another solution with the Mutex class from the kotlinx.coroutines library.
class SuspendWait2 {
private val mutex = Mutex(locaked = true)
suspend fun sleepAndWait() = mutex.withLock{}
fun resume() {
mutex.unlock()
}
}
I suggest using a CompletableJob for that.
My use case:
suspend fun onLoad() {
var job1: CompletableJob? = Job()
var job2: CompletableJob? = Job()
lifecycleScope.launch {
someList.collect {
doSomething(it)
job1?.complete()
}
}
lifecycleScope.launch {
otherList.collect {
doSomethingElse(it)
job2?.complete()
}
}
joinAll(job1!!, job2!!) // suspends until both jobs are done
job1 = null
job2 = null
// Do something one time
}