I has very simple task to send request for remote data. But need to handle my request and get local on remote error.
Functionality way like a mathematics has many ways to solve problem.
So first ost simple сomes to mind to do this:
val remote = remotePurchase
.getAvailableUsersMaxCount()
.subscribe({},{
//on failed
getLocal()
})
But i thing it is rx beginner guy also it is callback in callback bad way.
Also i think to do this:
override fun getActualUsersMaxCount(): Observable<Int> {
return remotePurchase
.getAvailableUsersMaxCount()
.flatMap {
updateUsersCount(it)
}
.onErrorReturn {
userLocal.getById(PrefProvider.userId).map {
it.chatUsersCount?:0
}.blockingFirst()
}
}
Bute some times handle:
Caused by: rx.exceptions.CompositeException: 2 exceptions occurred.
Like this github.com/ReactiveX/RxJava/issues/3679
Then i known about using toBlocking is not best idea. See post here.
After some time i decided mutch better this:
override fun getActualUsersMaxCount(): Observable<Int> {
val remote = remotePurchase
.getAvailableUsersMaxCount()
.flatMap {
updateUsersCount(it)
}.onErrorReturn { -1 }
return zip(remote, userLocal.getById(PrefProvider.userId).map {
it.chatUsersCount ?: 0
}, { remoteCount, localCount ->
if(remoteCount!= -1) remoteCount else localCount
})
}
So now i almost control situation. If i got error from network I can check it and receive local result
But what variation better as you think? May be there is more simplify decision?
You can use onErrorResumeNext to avoid the blockingFirst call in your second solution. This makes it the better solution, in my opinion. Not sure what two exceptions, you are talking about. Can give some more context on what they are, as I don't see the issue.
override fun getActualUsersMaxCount(): Observable<Int> {
return remotePurchase
.getAvailableUsersMaxCount()
.flatMap {
updateUsersCount(it)
}
.onErrorResumeNext(
userLocal.getById(PrefProvider.userId).map {
it.chatUsersCount?:0
})
}
Related
I am building an application in which user can read BPM data in mobile. I was reading in some posts that I need to build a queue for that, it run at a time and hold next command in queue until it finish the first job. I used some piece of code from the library. I want to check my existing queue why is it slow? if anything which is more efficient then ConcurrentLinkedQueue, then definitely I'll try that. I was reading some articles with Channel is type of Queue which behaves First In First Out. TBH I don't know it will work. Can you guys help me on this?
This is function of setupQueuePolling
private fun setupQueuePolling() {
viewModelScope.launch(Dispatchers.IO) {
Log.e(TAG, "Starting Polling")
while (true) {
synchronized(commandQueue) {
if (!commandQueue.isEmpty()) {
commandQueue.poll()?.let { qItem ->
qItem("This is input")
}
}
}
}
}
}
I have added the queue command for calling this function addItemToQueue
fun addItemToQueue(item: (input: String) -> Unit) {
Log.e(TAG, "Added Item ->> $item")
commandQueue.add(item)
}
I am calling addItemToQueue in MainActivity.kt, onConnectionStateChange, onServicesDiscovered & onCharacteristicChanged with GlobalScope using onServicesDiscovered & startScan.
I don't understand, why my queue is so slow to response back in time. Library is very fast to give response. My whole project is in here.
Thanks
At first glance, it's quite hard to say why it is slow. What I see is that synchronized(commandQueue) is being used while ConcurrentLinkedQueue is already a thread-safe queue, so the synchronized(commandQueue) can be omitted.
Using features of Kotlin coroutines, I would use a Flow in this case, particularly MutableSharedFlow. It is thread-safe and uses principles of queue. For example in this case it would look like the following:
private val commandFlow = MutableSharedFlow<(input: String) -> Unit>()
suspend fun addItemToQueue(item: (input: String) -> Unit) {
commandFlow.emit(item1) // emitting item to commandFlow
}
private fun setupQueuePolling() {
viewModelScope.launch {
// handle commands emitted in addItemToQueue() method.
commandFlow.collect { item ->
item("This is input")
}
}
}
If this doesn't improve the speed, further investigation should be made, perhaps BLE device executes commands slowly. Additional logs of each operation could be helpful.
Basically I want to make a network request when initiated by the user, collect the Flow returned by the repository and run some code depending on the result. My current setup looks like this:
Viewmodel
private val _requestResult = MutableSharedFlow<Result<Data>>()
val requestResult = _requestResult.filterNotNull().shareIn(
scope = viewModelScope,
started = SharingStarted.WhileViewSubscribed,
replay = 0
)
fun makeRequest() {
viewModelScope.launch {
repository.makeRequest().collect { _requestResult.emit(it) }
}
}
Fragment
buttonLayout.listener = object : BottomButtonLayout.Listener {
override fun onButtonClick() {
viewModel.makeRequest()
}
}
lifecycleScope.launchWhenCreated {
viewModel.requestResult.collect { result ->
when (result) {
Result.Loading -> {
doStuff()
}
is Result.Success -> {
doDifferentStuff(result.data)
}
is Result.Failure -> {
handleError()
}
}
}
}
The first time the request is made everything seems to work. But starting with the second time the collect block in the fragment does not run anymore. The request is still made, the repository returns the flow as expected, the collect block in the viewmodel runs and emit() also seems to be executed successfully.
So what could be the problem here? Something about the coroutine scopes? Admittedly I lack any sort of deeper understanding of the matter at hand.
Also is there a more efficient way of accomplishing what I'm attempting using Kotlin Flows in general? Collecting a flow and then emitting the same flow again seems a bit counterintuitive.
Thanks in advance:)
According to the documentation there are two recommended alternatives:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
//your thing
}
}
I rather the other alternative:
viewLifecycleOwner.lifecycleScope.launch {
viewModel.makeReques().flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect {
// Process the value.
}
}
I like the flowWithLifecycle shorter syntax and less boiler plate. Be carefull thar is bloking so you cant have anything after that.
The oficial docs
https://developer.android.com/topic/libraries/architecture/coroutines
Please be aware you need the lifecycle aware library.
Basically I have to make a network request using OkHttp in parallel to various addresses. I only care about the result of the first one that succeeds. Can I do this with Flow on Kotlin?
I've been looking around but I'm struggling with getting the requests to run in parallel, the always run in sequence.
The code basically takes a list of addresses and should return the only address that worked or null if none worked.
Thanks.
Edit: I should mention I plan on using this on Android. I can probably do it with RX but wanted to learn Flow. Also trying to limit the libraries I add to the app.
Edit: I have marked an answer as correct however that isn't how I did but it took me very close to how I did it but since I'm new to Flow I have no idea if how I did it is correct though I'm pretty sure it works after my testing.
I have a function that throws NoSuchElementException when not found. It calls searchForIPAsync which is a suspend function that does all the OkHttp work and returns true|false.
#Throws(NoSuchElementException::class)
private suspend fun findWorkingIP(ipsToTest: MutableList<String>): String? = ipsToTest
.asFlow()
.flatMapMerge(ipsToTest.size)
{ impl ->
flow<String?> {
val res = connectionHelper.searchForIPAsync(getURLToTest(impl))
if (res) {
emit(impl)
} else {
}
}
}.first()
Then I call this and catch the exception in case nothing is found:
try {
val ipFound = findWorkingIP(ipsToTest)
Log.w(TAG, "find: Got something " + ipFound);
return ipFound
} catch (ex: NoSuchElementException) {
Log.w(TAG, "find: not found");
}
Although the Flow-based solution in another answer is a close match to what you need, unfortunately as of Kotlin 1.3.2 the Flow implementation has a bug that breaks it. The bug already has a proposed fix so this should be resolved with the next patch release of Kotlin. In the meantime, here's a similar solution that uses async and Channel instead:
suspend fun getShortUrl(urls: List<String>): String = coroutineScope {
val chan = Channel<String?>()
urls.forEach { url ->
launch {
try {
fetchUrl(url)
} catch (e: Exception) {
null
}.also { chan.send(it) }
}
}
try {
(1..urls.size).forEach { _ ->
chan.receive()?.also { return#coroutineScope it }
}
throw Exception("All services failed")
} finally {
coroutineContext[Job]!!.cancelChildren()
}
}
So, I'm using MVVM architecture with Repository pattern to make different API calls.
I have a Repository called X, where I have different related API calls.
Before any of these calls are made, I would like to do validation. If that proceeds successfully, only then network request should be made.
fun getSomethingX(data: Data): Single<Data> {
return if (validation(data)) {
service.getSomethingX()
.onErrorResumeNext(...)
.map { ... ->
...
}
} else {
Single.just(null)
}
}
fun getSomethingY(data: Data): Single<Data> {
return if (validation(data)) {
service.getSomethingX()
.onErrorResumeNext(...)
.map { ... ->
...
}
} else {
Single.just(null)
}
}
As you can see I might have many network request functions called getSomething..(). I see this as a boiler-plate code.
Is there some other way of dealing with validation (not only token validation but permission in general)? If so, can you show me an example?
Is it okay to do permission validation in the Repository level?
Maybe a better approach would be doing validation in Interceptor? But I don't see a clean way of canceling the request if validation does not pass.
A better approach will be to keep the validation at repository level only and keeping your viewmodel dumb as possible. It’s very simply with Kotlin’s Function literals with receiver.
In your repository
fun getSomethingX(
data: Data,
onSuccess: (Single<Data>) -> Unit,
onError: (String) -> Unit
) {
if (validation(data)) {
// Do the network call
onSuccess(//pass result)
} else onError(“Invalid data”)
}
In your ViewModel
repository.getSomethingX(
data,
onSuccess = {
//it will give you Single<Data>
//Update the value
},
onError = {
//Emit error to view
}
)
How would I go about starting a chain with a Completable based on a condition?
I have the code below in getThings() which works, but it doesn't feel like correct usage of RxJava based on the examples I've seen. The content of downloadThings() and getCachedThings() doesn't matter for this example, but the return types do.
fun downloadThings(): Completable {
...
}
fun getCachedThings(): Flowable<List<Task>> {
...
}
fun getThings(): Flowable<List<Task>> {
return if (condition) {
downloadThings()
} else {
Completable.complete()
}.andThen(getCachedThings())
}
My understanding of RxJava is lacking so I can't explain it well, but it looks like the condition is "outside" the stream.
Is there a more correct way to do this, or is the way I'm doing it alright?
Thank you.
Completable.create(...) can be used here, so you can encapsulate data loading logic inside the flow.
fun getThings(): Flowable<List<Task>> {
Completable.create {
if (condition) { downloadThings() }
it.onComplete()
}.andThen(getCachedThings())
}
That's if it is about refactoring without logic corruption. Otherwise, it is considerable to analyze if Maybe fits your logic.