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
}
}
Related
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()
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
I am working with the java rx library now, I am having problems with the internal calls with flatMap and map, for some reason the most internal call is not been done.
Besides, if I replace the flatMap with suscribe() in the internal call, (before the childrenItemsResponse.forEach code), this code is executed, bat the execution is not synchronous, I mean this call is done after the main flatMap execution finish.
This is my code:
override fun getSportList(dCSServiceContext: DCSServiceContext): Single<List<EntityBrowse>> {
return dCSService.get(dCSServiceContext).flatMap { item ->
val entityBrowseList = arrayListOf<EntityBrowse>()
val section = builSection(item?.firstOrNull()!!)
if (item.firstOrNull()?.collections?.get("NavItems")?.size!! > 0) {
dCSServiceContext.contentIds = item.firstOrNull()?.collections?.get("NavItems")
buildNavItems(dCSServiceContext).map { section ->
return#map section
}.map { items ->
section.items = items
return#map entityBrowseList
}
} else {
Single.just(entityBrowseList)
}
}
}
The problem is presented in the buildNavItems, method:
private fun buildNavItems(dCSServiceContext: DCSServiceContext): Single<MutableList<Item>> {
return dCSService.get(dCSServiceContext).map { itemsResponse ->
val items: MutableList<Item> = arrayListOf()
itemsResponse.forEach { item ->
val transformedItem = buildItem(item!!)
if (item?.collections?.get("NavItems") != null) {
dCSServiceContext.contentIds = item?.collections?.get("NavItems")
val childrenItems: MutableList<Item> = arrayListOf()
dCSService.get(dCSServiceContext).flatMap { childrenItemsResponse ->
childrenItemsResponse.forEach { childrenItem ->
val transformedChildrenItem = buildItem(childrenItem!!)
childrenItems.add(transformedChildrenItem)
}
val section = Section("", "", false,childrenItems )
val data = Data(section)
val children = Children(data)
transformedItem.children = children
items.add(transformedItem)
Single.just(items)
}
} else {
val transformedItem = buildItem(item!!)
items.add(transformedItem)
}
}
return#map items
//Single.just(items)
}
}
More specifically, the in the line code: dCSService.get(dCSServiceContext).flatMap { childrenItemsResponse ->
This code is never executed.
I am not sure about what could be the cause of the issue.
Thanks in advance!!
To activate an observer chain, there has to be a subscription. You don't subscribe to the observer chain that starts with dCSService.... flatMap() itself won't subscribe to its interior observables until the outer observable chain has been subscribed to.
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
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())