Using rxjava to get bitmap for every list item - android

I'm using Retrofit to get data from REST service. After recive json (list of objects) I want to get a bitmap for every position on the list. What is the best way to do this? I'm trying to use map/flat map on my Observable created by Retrofit, but It's not works (or, propably, I'm doing it wrong).
Retrofit interface method:
#POST("links/promoted/appkey/{appkey}/page/{page}/sort/{sort}")
public Observable<List<Link>> promoted(#Path("appkey") String appkey,
#Path("page") int page,
#Path("sort") String sort);
Usage:
linksService
.promoted(Extras.APP_KEY, page, Sort.DAY)
.subscribeOn(Schedulers.newThread())
.subscribe(links -> {
view.appendLinks(links);
});
When I'm trying to use flatMap() or map() it's always return a list, but i need every item separately.
Any help would be greatly appreciated!

Do it like this:
linksService
.promoted(Extras.APP_KEY, page, Sort.DAY)
.subscribeOn(Schedulers.newThread())
.flatMapIterable(list -> list)
.subscribe(link -> {
view.appendLinks(link);
});

Related

Android and RxJava: insert into Room database a Retrofit Response

I have two slightly different Question classes. One is an retrofit call results object, and the other is a Room #Entity in my Android App.
And now I want from my Interactor class (Use-case) class do the following:
Make a call to the API and result (List where question is
the Retrofit response class)
On success, make a new Game object in my Room database. This operation have long (#Entity id which is autogenerated) as return
type.
for each Question from retrofit response (from (1)), question -> Converter which converts from retrofit.Question to
database.Question. Converter method takes 2 parameters, the
retrofit.Question object and the ID which was returned in step (2).
After conversion, add to database.
Observe on AndroidSchedulers.mainthread. (subscribeOn is called from repository)
Now the problem I am having is creating this stream with RxJava from my Interactor class.
Here is all the classes and calls. First is my Interactor.class method which should do the stream described above:
public Single<List<Question>> getQuestionByCategoryMultiple(String parameter);
The API CALL from MyAPI.class:
//this Question is of database.Question.
Single<List<Question>> getQuestionByCategory(String parameter);
The Room database repository.class:
Single<Long> addGameReturnId(Game game);
Completable addQuestions(List<Question> questions);
Converter.class:
public static List<database.Question> toDatabase(List<retrofit.Question> toConvert, int id);
I am having trouble creating the stream described above with these methods. I tried a mix of .flatmap, .zip, .doOnSuccess, etc without successfully creating the stream.
If there is anything else you need me to explain, or explain the problem better, please comment below.
public Single> getQuestionByCategoryMultiple(String parameters){
return openTDBType
.getQuestionByCategory(paramters) //step 1
// step 2
// step 3
.observeOn(AndroidSchedulers.mainThread()); //step 4
}
EDIT:
I tried something like this:
return openTDBType
.getQuestionByCategory(parameters)
.map(QuestionConverter::toDatabase)
.flatMap(questions -> {
int id = gameRepositoryType.addGameReturnId(new Game(parameters).blockingGet().intValue();
questions.forEach(question -> question.setqId(id));
gameRepositoryType.addQuestions(questions);
return gameRepositoryType.getAllQuestions(); })
.observeOn(AndroidSchedulers.mainThread());
^^ I don't know if this is the best way to go about this one? Can anyone confirm if this is a good way to design what I want to do here, or if there are better ways or any suggestions?
Try not use blockingGet especially when it is avoidable. Also, addQuestions won't be executed at all because it is not subscribed. You can add both addGameReturnId and addQuestions into the chain like this:
return openTDBType
.getQuestionByCategory(parameters)
.map(QuestionConverter::toDatabase)
.flatMap(questions -> {
return gameRepositoryType.addGameReturnId(new Game(parameters)) // returns Single<Long>
.map(id -> {
questions.forEach(question -> question.setqId(id));
return questions;
})
}) // returns Single<List<Question>> with the GameId attached
.flatMapCompletable(questions -> gameRepositoryType.addQuestions(questions)) // returns Completable
.andThen(gameRepositoryType.getAllQuestions()) // returns Single<>
.observeOn(AndroidSchedulers.mainThread());

Combine, Zip or how to include or squeeze in a stream into a stream

I am not sure what exactly to use but lately, I have had a lot of trouble with RxJava when I am working with code that has streams for everything.
In my case, let say I have to get an instance of an object, that I need for some processing from a stream that is available, let's call this NeededInstance and so I have access to Observable of NeededInstance.
Next, what I am doing is I have a Single of a List of SomeObject and what I do is I need to iterate over all items and update them.
I do this in the following way:
.map(/*in this map the Single<List<SomeObject>> is created*/)
.flatMap(Single<List<SomeObject>> -> updateWithData(Single<List<SomeObject>>);
this is how I wanted my updateWithData function to look like:
private Single<List<SomeObject>> updateWithData(List<SomeObject> list) {
return
Observable.just(list)
.flatMapIterable(listItem -> listItem)
.flatMapSingle(listItem -> updateListItem(listItem))
.toList();
}
I do the above code so that I can transform a chain from handling a single list to an observable of items that I update and return to a list again. Below is the updateListItem function, where trouble comes when I try to get something from that other stream I mention in the beginning:
updateListItem(ListItem listItem) {
return
Observable<NeededInstance>.map(Optional::get)
.flatMapSingle(neededInstance -> workWithListItemAndNeededInstace(listItem, neededInstance))
.map(integer -> {
// do something with integer soming from the above method and with a listItem passed into this function
}
return Single.just(updatedListItem)
}
so, to be sure, workWithListItemAndNeededInstance can't update the listItem, I just get an Integer object there and with that, I have to do my own updating of listItem. Then I am trying to either return a Single of a listItem or listItem itself and somehow make it available for a .toList() so that in the end I still have a Single of a List of ListItem in the stream.
I am trying with combine but can't really make it work and I find RxJava a bit weird when I have streams that I need to just "drop in" and leave something that I can use for processing.
Any clarification is welcome.
//You have a list of string object
List<String> intList = new ArrayList<>();
Collections.addAll(intList, "1", "2", "3", "4", "5");
//Now what you want here is append neededInstance to each item in list and get it as a list.
//So output would be like List of (test1, test2, test3, test4, test5);
Observable
//iterate through the list of items and pass one by one to below stream
.fromIterable(intList)
//Each item from the list is passed down to workWithListItemAndNeededInstace
.flatMap(item -> workWithListItemAndNeededInstace(item))
.toList()
.subscribe();
/*
This method will combine item from list with the neededInstance and return a stream of combined data
*/
private Observable<String> workWithListItemAndNeededInstace(String item) {
return neededInstance().map(instance -> instance + item);
}
/*
This will be your stream from which you get needed stream
*/
private Observable<String> neededInstance() {
return Observable.just("Need instance");
}
Hope this solution gives you a rough idea on what you would want to achieve. Let me know if I missed anything, so that I can update this answer.

Chaining requests in Retrofit + RxJava

I have 2 APIs that I want to make request to in sequence and store their data in SQLite.
First I want to make request to API A and store its data in SQL table a. Then make request to API B and store its data in table b and some data in table a_b. The data stored in a_b is from request B alone.
How can I do this using RxJava. I read somewhere about using flatMap for this, something like this
apiService.A()
// store in DB here? How? maybe use map()?
.flatMap(modelA -> {
// or maybe store modelA in DB here?
return apiService.B().map(modelB -> {
storeInDB()l // store B here ?
return modelB;
});
});
If I wasn't using lambda functions, this would look as ugly as normal nested calls. Is this a better way to do it?
I don't think using map operator is the best way to go with things like storing the result of the api call.
What I like to do is to separate those things inside doOnNext operators. So your example would be something like this:
apiService.A()
.doOnNext(modelA -> db.store(modelA))
.flatMap(modelA -> apiService.B())
.doOnNext(modelB -> db.store(modelB));
(add necessary observeOn and subscribeOn yourself, exactly like you need them)
Yes, you can use flatmap for this exact purpose. See the below example (Assuming your service A returns Observable<FooA> and service B returns Observable<FooB>)
api.serviceA()
.flatMap(new Func1<FooA, Observable<FooB>>() {
#Override
public Observable<FooB> call(FooA fooA) {
// code to save data from service A to db
// call service B
return api.serviceB();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<FooB>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(FooB fooB) {
// code to save data from service B to db
}
});

how to map values returned from observable to be used in another observable

I have the following observable to get list of feed ids from database ( i use sugar ORM library)
public Observable<Set<Long>> getFeedIdsFromDB() {
return Observable.create(subscriber -> {
Set<Integer> subscribedFeedIds = new HashSet<>();
//get feed ids from FeedEntity Table
for (FeedEntity feed : FeedEntity.listAll(FeedEntity.class)){
if (feed.isSubscribed()){
subscribedFeedIds.add(feed.getFeedId());
}
}
});
}
this Observable should emits ids to be used for api call in the following:
public Observable<StoryCollectionEntity> storyEntityList(final int page) {
return this.restApi.storyCollection(/* this is feed ids*/ id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction)
}
i guess i should use some sort of mapping but have no idea how can i implement it.
EDIT:
i did the following modification:
// To map feed ids (retrieved from database) to getAllStoryEntityList Observable:
#Override
public Observable<StoryCollectionEntity> storyEntityList(final int page) {
return this.mNewsCache.getFeedIdsFromDB().flatMap(id -> getAllStoryEntityList(page, id));
}
//call restApi
public Observable<StoryCollectionEntity> getAllStoryEntityList(final int page, Set<Long> id){
return this.restApi.storyCollection( id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction);
}
but api service is never called. something wrong in the mapping.
#GET("story")
Observable<StoryCollectionEntity> storyCollection(
#Query("feed_ids") Set<Long> feedIds,
#Query("page") int page);
The Observable created in getFeedIdsFromDB isn't emitting any items, so your flatMap and other data transformations never occur because the stream actually has no data. You can test this by subscribing directly to the returned Observable and doing something for onNext.
getFeedIdsFromDB().subscribe(feedId -> System.out.println(feedId));
You should see that nothing gets printed. When using Observable#create, the onNext method of subscriber in the anonymous class must be manually called with whatever data you wish to pass downstream. The documentation provides sample code for this.
So modifying your Observable to call onNext, we get this:
public Observable<Set<Long>> getFeedIdsFromDB() {
return Observable.create(subscriber -> {
Set<Integer> feedIds = new HashSet<>();
// get feed ids from FeedEntity Table
for (FeedEntity feed : FeedEntity.listAll(FeedEntity.class)){
feedIds.add(feed.getFeedId());
}
// emit a single Set and complete
if (subscriber.isSubscribed()) {
subscriber.onNext(feedIds);
subscriber.onCompleted();
}
});
}
Now the Set should get passed along. If your goal is to end up with a the emission of a single StoryCollectionEntity object after the transformations (and if I'm reading this properly), then your mapping looks correct.
I'm not sure what the expected output is, but I'll show you a way to do this sort of thing. Maybe you can alter it to fit your use case.
The first step is to allow id as a function parameter in storyEntityList:
public Observable<StoryCollectionEntity> storyEntityList(final int page, int id) {
return this.restApi.storyCollection(/* this is feed ids*/ id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction)
Now you can use an Observable.flatMap:
public Observable<StoryCollectionEntity> getAllStoryEntityList(int page){
return getFeedIdsFromDB().flatMap(id -> storyEntityList(page, id));
}
The naming might be off, but again - I'm not sure what the entities are.

Combining API calls with RX Java

I'm new to RXJava and i'm having trouble understanding how to chain together the result of API calls.
I'm making two API calls using retrofit, A and B, which both return an observable List of objects. Both API calls are independent so I want to make both at the same time, but to achieve my final result, I need to first take the result of A, do some work, then combine that with the result of B to populate my list adapter.
Make API Call A
Make API Call B
Take A's result and create result X
Take Result of B + X and populate adapter
#GET("/{object_id}/object_a")
Observable<List<Object_A>> getObjectAList(
#Path("object_id") long object_id);
#GET("/{object_id}/object_b")
Observable<List<Object_B>> getObjectBList(
#Path("object_id") long object_id);
This is where I get lost trying to use RX java. I can take the result of api call A and do my work
but I'm not sure how to take the result I just generated and combine it with API Call B.
aService. getObjectAList(object_a.getID())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(new Action1<List<Object_A>>() {
#Override
public void call(List<Section> sections) {
// Do Stuff Here...
// Now i need to take this result and combine it with API Call B...
}
});
I want to make both API calls at the same time, but i'm not sure how to chain together and combine API calls. Any help is appreciative.
Something like this?
Observable
// make api call for A list and B list
.combineLatest(getObjectAList(), getObjectBList(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(List<Object_A> o, List<Object_B> o2) {
// Do what you need to do in the background thread
List x = createX(o);
List y = createY(x, o2);
return y;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object y) {
// UI thread, do what you need e.g. renders the list
mAdapter.setList(y);
}
});
Taking care of replacing the proper types should bring you quite close to the solution.
The question is : how would you combine results ?
Building a new result from List and List ? Combine A objects with B objects ?
Answer to this question help to find the right operator for your problem.
A simple example of combining results can be this :
getObjectAList().zipWith(getObjectBList(), (aList, bList) -> // combine both list to build a new result)).subscribe()
You can combine elements of the list too with another operator (combineLatest for example)
aObs = getObjectAList().flatMap(Observable::from);
bObs = getObjectBList().flatMap(Observable::from);
Observable.combineLatest(aObs, bObs, (a,b) -> // combine a object with b object).subscribe();
For all of this examples above, requests will be done in parallel by retrofit.
I'd probably do something like the following
Observable convertedObservable = getObjectAList
.map(object_as -> convertAToX(object_as));
Observable.combineLatest(convertedObservable, getObjectBList, (listx, listb) -> {
return listx.addAll(listb);
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(r -> {
setAdapterWith(r);
});
Keep in mind this is using lambdas instead of anonymous classes but you should get the gist. Map is a great way of converting one object type to another (results of A to Results of X). So you can decide how convertAToX method works for you. Then you can use combineLastest on the converted A-X and B to return the list of R which updates your adapter
Ideally this is all in a ViewModel of some kind where getObjectAList and getObjectBList can me mocked on with Mock observables and you can test all the logic easily :)

Categories

Resources