I'm using RxAndroidBle with kotlin.
So far I could set multiple notifications and i'm receiving them.
How can i map the notification to it's specific characteristic?
So far:
enter code here connectionObserver
.flatMap { rxBleConnection ->
rxBleConnection.discoverServices().flatMap { services -> services.getService(ServiceUUID).map(BluetoothGattService::getCharacteristics) }
.flatMapObservable { s -> fromIterable(s) }
.flatMap { characteristic -> rxBleConnection.setupNotification(characteristic)
.flatMap{ notificationObservable -> notificationObservable}}
}.observeOn(AndroidSchedulers.mainThread())
.subscribe{notification->notification}
Thank You
for those strugling with kotlin and RxAndroidBluetooth, after some work I make it to work, so the original code looks like
enter code here connectionObserver
.flatMap { rxBleConnection ->
rxBleConnection.discoverServices().flatMap { services -> services.getService(ServiceUUID).map(BluetoothGattService::getCharacteristics) }
.flatMapObservable { s -> fromIterable(s) }
.flatMap { characteristic ->
rxBleConnection.setupNotification(characteristic)
.flatMap { notificationObservable -> notificationObservable.flatMap { result -> Observable.just(result).zipWith(fromArray(getBytesFromUUID(characteristic.uuid))) } }
}
}.observeOn(AndroidSchedulers.mainThread())
.subscribe { notification -> onNotificationReceived(notification)
}
Related
webSocket("/ws") {
try {
while (true) {
when(val frame = incoming.receive()){
is Frame.Text -> {
val text = frame.readText() //i want to Serialize it to class object
send(Frame.Text(processRequest(text)))
}
else -> TODO()
}
}
} finally {
TODO()
}
}
i want to Serialize frame.readText() to return class object
i'm totally new to Ktor world and i don't know if that is possible
You can use the underlying kotlinx.serialization that you might have already set up for ContentNegotiation already. If you haven't, instructions can be found here. This will require to make your class (I assumed the name ObjectType) serializable with #Serializable. More details here on how to make a class serializable and also how to encode/decode to JSON format. I included the solution snippet:
webSocket("/ws") {
try {
while (true) {
when(val frame = incoming.receive()){
is Frame.Text -> {
val text = Json.decodeFromString<ObjectType>(frame.readText())
send(Frame.Text(processRequest(text)))
}
else -> TODO()
}
}
} finally {
TODO()
}
}
I would usually use a flow (requires kotlinx.coroutines)
incoming.consumeAsFlow()
.mapNotNull { it as? Frame.Text }
.map { it.readText() }
.map { Json.decodeFromString<ObjectType>(it) }
.onCompletion {
//here comes what you would put in the `finally` block,
//which is executed after flow collection completes
}
.collect { object -> send(processRequest(object))}
I'm looking for the most "clean" way to implement the following logic:
I have N methods, everyone returns Flow<Result<SOME_TYPE>> (type are different)
I want to chain these methods, so if 1 returns Result.Success, then call 2nd and so on.
The most obvious way to do it is:
methodA().map { methodAResult ->
when (methodAResult) {
is Result.Success -> {
methodB(methodAResult).map { methodBResult ->
when (methodBResult) {
is Result.Success -> {
methodC(methodAResult).map { methodCResult ->
when (methodCResult) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
But it looks like a well-known "callback hell". Do u have any ideas how to avoid it?
I believe this could be flattened with transform operator:
methodA().transform { methodAResult ->
when (methodAResult) {
is Success -> methodB(methodAResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodBResult ->
when (methodBResult) {
is Success -> methodC(methodBResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodCResult ->
when (methodCResult) {
is Success -> TODO()
is Failure -> TODO()
}
}
A slight modification to the solution provided by Михаил Нафталь
methodA()
.flatMapMerge {
when (it) {
is Result.Success -> methodB(it)
is Result.Failure -> emptyFlow()
}
}.flatMapMerge {
when (it) {
is Result.Success -> methodC(it)
is Result.Failure -> emptyFlow()
}
}.collect {
when (it) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
Merging the output of one flow to another flow is the goal of flatMap so using flatMap seems a little cleaner.
If this Result class has a map, fold, or getOrNull type method this could be cleaned up a bit more and the when blocks could be removed.
Also if you need to propagate the failure to collect then you could replace the calls to emptyFlow with a flow that just outputs the failure that you want.
Badly a flatMap method still doesn't exist.
But you can use mapCatching :
methodA
.mapCatching { a -> methodB(a).getOrThrow() }
.mapCatching { b -> methodC(b).getOrThrow() }
Or make your own flatMap extension function :
fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> {
return this.mapCatching {
block(it).getOrThrow()
}
}
methodA
.flatMap { a -> methodB(a) }
.flatMap { b -> methodC(b) }
I believe that in this use case you should probably use suspend functions and compose them using await().
Errors should be passed through exceptions as described here.
var responseMap = mutableMapOf<VendorType, ChargeResponse>()
requests.forEach {
val response = when (it.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> {
throw NotImplementedError("${it.vendorType} does not support yet")
}
}
responseMap[it.vendorType] = response
}
responseMap
So I want all the service.charge function run in separate thread. Return the map when all is done
Hope to solve your problem:
Assume your service and request like this:
interface Service {
suspend fun chargeForType1(): ChargeResponse
suspend fun chargeForType2(): ChargeResponse
}
data class Request(val vendorType: VendorType)
suspend fun requestAll(requests: List<Request>): Map<VendorType, ChargeResponse> {
return coroutineScope {
requests
.map { request ->
async {
request.vendorType to when (request.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> throw NotImplementedError("${request.vendorType} does not support yet")
}
}
}
.awaitAll()
.toMap()
}
}
I would like to convert my rxJava Code to Kotlin CoRoutine.
Below is the code makes both the api and db call and returns the data to UI whatever comes first. Let us say if DB response happens to be quicker than the api. In that case still, the api response would continue until it receives the data to sync with db though it could have done the UI update earlier.
How Would I do it?
class MoviesRepository #Inject constructor(val apiInterface: ApiInterface,
val MoviesDao: MoviesDao) {
fun getMovies(): Observable<List<Movie>> {
val observableFromApi = getMoviesFromApi()
val observableFromDb = getMoviesFromDb()
return Observable.concatArrayEager(observableFromApi, observableFromDb)
}
fun getMoviesFromApi(): Observable<List<Movie>> {
return apiInterface.getMovies()
.doOnNext { it ->
it.data?.let { it1 -> MoviesDao.insertAllMovies(it1) }
println("Size of Movies from API %d", it.data?.size)
}
.map({ r -> r.data })
}
fun getMoviesFromDb(): Observable<List<Movie>> {
return MoviesDao.queryMovies()
.toObservable()
.doOnNext {
//Print log it.size :)
}
}
}
As the first step you should create suspend funs for your ApiInterface and MovieDao calls. If they have some callback-based API, you can follow these official instructions.
You should now have
suspend fun ApiInterface.suspendGetMovies(): List<Movie>
and
suspend fun MoviesDao.suspendQueryMovies(): List<Movie>
Now you can write this code:
launch(UI) {
val fromNetwork = async(UI) { apiInterface.suspendGetMovies() }
val fromDb = async(UI) { MoviesDao.suspendQueryMovies() }
select<List<Movie>> {
fromNetwork.onAwait { it }
fromDb.onAwait { it }
}.also { movies ->
// act on the movies
}
}
The highlight is the select call which will simultaneously await on both Deferreds and act upon the one that gets completed first.
If you want to ensure you act upon the result from the network, you'll need some more code, for example:
val action = { movies: List<Movie> ->
// act on the returned movie list
}
var gotNetworkResult = false
select<List<Movie>> {
fromNetwork.onAwait { gotNetworkResult = true; it }
fromDb.onAwait { it }
}.also(action)
if (!gotNetworkResult) {
action(fromNetwork.await())
}
This code will act upon the DB results only if they come in before the network results, which it will process in all cases.
Something along those lines should work:
data class Result(val fromApi: ???, val fromDB: ???)
fun getMovies(): Result {
val apiRes = getMoviesFromApiAsync()
val dbRes = getMoviesFromDbAsync()
return Result(apiRes.await(), dbRes.await())
}
fun getMoviesFromApiAsync() = async {
return apiInterface.getMovies()
.doOnNext { it ->
it.data?.let { it1 -> MoviesDao.insertAllMovies(it1) }
println("Size of Movies from API %d", it.data?.size)
}
.map({ r -> r.data })
}
fun getMoviesFromDbAsync() = async {
return MoviesDao.queryMovies()
}
I don't know what you're returning, so I just put ??? instead.
I created a function which returns an Observable<String> with file names, but I don't get any event in my subscription where I call this method. Also there is no call of onError, or onComplete
See my code:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory() }
return observable { subscriber ->
allFiles.toObservable()
.map { f -> "${f.name}" }
.doOnNext { println("Found file $it") }
.subscribe { subscriber}
}
}
fun test() {
getAllFiles()
.doOnNext { println("File name$it") }
.subscribe(
{n -> println("File: $n")},
{e -> println("Damn: $e")},
{println("Completed")})
}
Though everything is being called in the getAllFiles() function, so what am I missing?
observable is for creating an Observable from scratch but you already have Observable<String> from toObservable() so you don't need it. The code below works for me:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory }
return allFiles.toObservable()
.map { f -> "${f.name}" }
}
fun test() {
getAllFiles()
.doOnNext { println("File name $it") }
.subscribe(
{ n -> println("File: $n") },
{ e -> println("Damn: $e") },
{ println("Completed") })
}
You can also fix this by changing from:
.subscribe{subscriber}
to
.subscribe(subscriber)
but this nested Observable version is confusing to me.