Observable.zip issues - java

I download posts from two pages from Facebook using Retrofit and RxJava. I want to download them by Observable.zip but I'm getting onError : An operation is not implemented: not implemented.
My code:
var fb1 = dataManager.getPosts(ApplicationConstants.FACEBOOK_PAGE_1, ApplicationConstants.FACEBOOK_APP_TOKEN, "70")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
var fb2 = dataManager.getPosts(ApplicationConstants.FACEBOOK_PAGE_2, ApplicationConstants.FACEBOOK_APP_TOKEN, "70")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
val observableZip : Observable<PostList> = Observable.zip(fb1,fb2, object: Function<PostList, PostList>, BiFunction<PostList, PostList, PostList> {
override fun apply(t: PostList): PostList {
}
override fun apply(t1: PostList, t2: PostList): PostList {
}
})
compositeDisposable.add(observableZip.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(
{result -> posts.addAll(result.data)},
{t : Throwable? -> Log.d("TAG",t?.message) },
{view.setAdapter(posts)
view.hideProgressBar()}
))
I not exactly understend how to make Observable.zip as examples in the Internet are not clearly explained.
Q: How to change my code to make Observable.zip working ?

Your zip should look like this:
val observableZip : Observable<PostList> = Observable.zip(fb1,fb2, object : BiFunction<PostList, StriPostList, PostList> {
override fun apply(t1: PostList, t2: PostList): PostList {
// do the zipping
}
})
There is no zip with parameters of type Function, and BiFunction. Only the list of observables and then a BiFunction (or, alternatively a single ObservableSource and a normal Function)
It's important to use the BiFunction from Rx, so make sure you're using import io.reactivex.functions.BiFunction not java.util.function.BiFunction

Related

Kotlin multi platform flow block convert to custom wrapper flow use for ios

I have a kotlin multi platform project which contains apollo graphql api
in this project i have BaseRepository Class and in this class there is a method to execute query or mutations
suspend fun <D : Query.Data> executeQuery(query: Query<D>): ApolloResponse<D> {
val response = getApolloClient().query(query).execute()
checkOperation(response)
return response
}
suspend fun <D : Mutation.Data> executeMutation(mutation: Mutation<D>): ApolloResponse<D> {
val response = getApolloClient().mutation(mutation).execute()
checkOperation(response)
return response
}
For example i want to use this method in Some repository like this
class HelpRepository : BaseRepository() {
fun test(request: AddFeedBackRequest) = flow {
val feedBackType = if (request.type == AddFeedBackType.Bug) {
FeedbackType.BUG
} else {
FeedbackType.FEEDBACK
}
val input = AddFeedbackInput(request.note, Optional.presentIfNotNull(feedBackType))
emit(true)
val mutation = AddFeedbackMutation(input)
val response = executeMutation(mutation)
emit(false)
}
}
when i add the flow scope i shouldn't be had to convert this method to a suspend function
i dont want to use suspend function because of ios application. When i use suspend function its convert "Kotlinx_coroutines_coreFlowCollector" in xcode
so i found a wrapper function like this
fun <T> Flow<T>.asCommonFlow(): CommonFlow<T> = CommonFlow(this)
class CommonFlow<T>(private val origin: Flow<T>) : Flow<T> by origin {
fun listen(block: (T) -> Unit): Closeable {
val job = Job()
onEach {
block(it)
}.launchIn(CoroutineScope(Dispatchers.Main + job))
return object : Closeable {
override fun close() {
job.cancel()
}
}
}
}
when i use this wrapper with single variable it works exactly what i want in xcode.
but in functions i couldn't find a proper way to do this
i need a wrapper like
= commonFlow {
}
instead of this
= flow {
}
to use this method as a commonFlow wrapper
Can you help me ?
We have pretty much the same thing in one of our projects. We have a extension function that converts the regular flow to a "common" flow so it can be used in both Android and iOS.
You can created flow like always, and wrap it at the end.
fun <T> Flow<T>.wrap(): CommonFlow<T> = CommonFlow(this)
class HelpRepository : BaseRepository() {
fun test(request: AddFeedBackRequest) = flow {
val feedBackType = if (request.type == AddFeedBackType.Bug) {
FeedbackType.BUG
} else {
FeedbackType.FEEDBACK
}
val input = AddFeedbackInput(request.note, Optional.presentIfNotNull(feedBackType))
emit(true)
val mutation = AddFeedbackMutation(input)
val response = executeMutation(mutation)
emit(false)
}
}.wrap()

Combine dynamic API requests in RxJava Android

I want to loop multiple API requests after response of one API and on complete of all requests I have to perform action.
First API Response(getModels) =
{
"message":{
"models":[
{
"equipno":"000000000001027019",
},
{
"equipno":"000000000001027020",
},
{
"equipno":"000000000001027021",
},
{
"equipno":"FL3-PINRO-0001/011",
}
]
}
}
Then I have to call second API in loop for all equipno received in first API response .
Second API response(getPrediction) -
{"body":{
"status":"success",
"message":{
"equipno":"000000000001027019",
"predictions":[
{
"date":"2020-09-01",
"maintenance":true,
"accuracy":13.07
}
],
"elapsed":0.03
}
}
}
I tried to do using following Rx java code but its not working.
val predictiveMaintenanceServiceCall = PredictiveMaintenanceServiceCall(context)
val data = predictiveMaintenanceServiceCall.getModels(this).map { it.predictionMessage?.models?.map { it1 -> it1?.equipno } }
data.map { items ->
items.map { item ->
predictiveMaintenanceServiceCall.getPrediction(item, dateFormatter.format(Calendar.getInstance().time))
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ it ->
Log.i("v ", "loadAlertsList zip ${it.size}")
Observable.zip(it, Arrays::asList)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ it ->
for (i in it as List<PredictionModel>)
Log.i("v ", "loadAlertsList ${i.message?.equipno}")
}, {
Log.i("v ", "loadAlertsList ${it}")
})
},
{
})
}
First and foremost, if API is in your control then I suggest you update getPrediction API to except a list of equipno, so that you can call the API once and avoid all the unnesseary API calls.
As for how to do it currently, I suggest that instead of using RX you look into kotlin coroutines. your problem can be solved by following below steps.
Make your retrofit functions suspendable
Retrofit 2.6.0 and above support suspend function, so all you have to do is to mark your getPrediction and getModel with suspend modifier as
suspend fun getModel(): List<Model>
suspend fun getPredication(equipno: String): List<Predication>
Launch a coroutine to call API's (ideally this should be done in ViewModel)
private fun callAPI() = viewModelScope.launch {
val models = getModels()
models.forEach {
val predication = getPrediction(it.equipno)
// Do something with predication
}
}

How to group data from async sources using RxJava

I'm working on a trading app. When the user select some products I need to show for each product if the market is open and its latest price. The user can select for example 2 products and what I have to do is to show the data only when I have all the info for the 2 products. The data can change at any time (i.e. the market for one of the products got closed). This is my code:
data class ProductInfo(
val productCode: String,
val isMarketOpen: Boolean,
val latestPrice: BigDecimal,
)
// This observable could emit at any time due to user interaction with the UI
private fun productsCodeObservable(): Observable<List<String>> = Observable.just(listOf("ProductA", "ProductB"))
// Markets have different working hours
private fun isMarketOpenObservable(productCode: String): Observable<Boolean> {
return Observable.interval(2, TimeUnit.SECONDS)
.map {
// TODO: Use API to determine if the market is open for productCode
it.toInt() % 2 == 0
}
}
// The product price fluctuates so this emits every X seconds
private fun latestPriceObservable(productCode: String): Observable<BigDecimal> {
return Observable.interval(2, TimeUnit.SECONDS)
.map { productPrice -> productPrice.toBigDecimal() }
}
#Test
fun `test`() {
val countDownLatch = CountDownLatch(1)
productsCodeObservable()
.switchMap { Observable.fromIterable(it) }
.flatMap { productCode ->
Observable.combineLatest(
isMarketOpenObservable(productCode),
latestPriceObservable(productCode)
) { isMarketOpen, latestPrice ->
ProductInfo(productCode, isMarketOpen, latestPrice)
}
}
.toList()
.doOnSuccess { productsInfo ->
println(productsInfo)
}
.subscribe()
countDownLatch.await()
}
I don't know what the problem is because the test method never prints anything. I don't know much about RxJava but my understanding is that toList is not working because the source observables never complete. Any idea about how I can collect the data for the product codes and emit a list when any of the data changes? :)
If you want to receive new product info list every time any of these products has changed:
productsCodeObservable()
.switchMap { list ->
val productInfoObservables = list.map { productCode ->
Observable.combineLatest(
isMarketOpenObservable(productCode),
latestPriceObservable(productCode)
) { isMarketOpen, latestPrice ->
ProductInfo(productCode, isMarketOpen, latestPrice)
}
}
Observable.combineLatest(productInfoObservables) { it.toList() as List<ProductInfo> }
}
.doOnNext { productsInfoList ->
println(productsInfoList)
}
.subscribe()
Use the RxJava’s Emitter interface and implement its methods:
public interface Emitter<T> {
void onNext(T value);
void onError(Throwable error);
void onComplete();
}
I think you need an ObservableEmitter, Please take a look at the following page:
https://www.raywenderlich.com/2071847-reactive-programming-with-rxandroid-in-kotlin-an-introduction

How to return value from Observable to Rxjava 2

I ran into a problem that onNext cannot contain return, but I need to return the string.
The request is made using Retrofit with a factory inside the interface (ApiService).
fun getNameAnimal(name : String) : String {
var nameAnimal = " "
val api = ApiService.create()
api.getAnimal("Cat")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ animal ->
// It works
Log.i(LOG, animal.name)
// It NOT works (empty value)
nameAnimal = animal.name
},
{ error ->
Log.e(LOG, error.printStackTrace())
}
)
return nameAnimal
}
In the logs, the answer comes in the format I need.
The method is in a class that is not an activity or fragment.
How can I implement my plan?
fun getNameAnimal(name : String) : Single<String> {
val api = ApiService.create()
return api.getAnimal("Cat")
.map { animal -> animal.name }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
2. In activity or fragment
apiWorkingClassInstance.getNameAnimal()
.subscribe(
{ animalName ->
Log.i(LOG, animalName)
//todo
},
{ error ->
Log.e(LOG, error.printStackTrace())
}
)
Thanks for the tip Alex_Skvortsov
An easy and clean way to achieve this would be to use an interface.
Create your interface
interface AnimalCallbacks {
fun onAnimalNameReturned(name: String)
}
Have your class implement the interface
class MyAnimal: AnimalCallbacks {
override fun onAnimalNameReturned(name: String) {
//handle logic here
}
}
From here you can pass your interface in the method call and send the result back using the method you defined in the interface.
fun getNameAnimal(name : String, callbacks: AnimalCallbacks) {
var nameAnimal = " "
val api = ApiService.create()
api.getAnimal("Cat")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ animal ->
// It works
Log.i(LOG, animal.name)
// It NOT works (empty value)
nameAnimal = animal.name
callbacks.onAnimalNameReturned(animal.name)
},
{ error ->
Log.e(LOG, error.printStackTrace())
}
)
}

Android JUnit test blocks indefinitely when Observable observed on AndroidSchedulers.mainThread()

I'm writing a simple test that is equivalent to:
Test fun testObservable() {
val returnedObservable = Observable.create(object : Observable.OnSubscribe<String> {
override fun call(t: Subscriber<in String>) {
t.onNext("hello")
t.onCompleted()
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
val result = returnedObservable.toBlocking().first()
assertEquals("hello", result)
}
The test blocks indefinitely on returnedObservable.toBlocking().first() when .observeOn(AndroidSchedulers.mainThread()) is present.
Is there a way to transform the observable to return the result?
The returnedObservable is returned from method call with .subscribeOn and .observeOn already applied so removing those is not an option.
I guess it is a bug mentioned here: https://github.com/ReactiveX/RxAndroid/issues/50
btw why don't you use RxKotlin?
You example will look much better:
val returnedObservable = observable<String> { subscriber ->
subscriber.onNext("hello")
subscriber.onCompleted()
}
.subscribeOn(Schedules.io())
.observeOn(AndroidSchedulers.mainThread())

Categories

Resources