I have following code that works well.
Observable.from(...)
.map { // List<Object>
if (My_Condition_is_true) {
//...
}
val newList = getNewListIfConditionIsOkay(it)
newList.map { item -> toSomethingElse(item) }
}
.subscribeBy(myErrorFun) {
//...
}
I feel map operator does not looks cool but I have no idea how to fix it. This is what is in my mind.
Observable.from(...)
.doOnNext {// List<Object>
if (My_Condition_is_true) {
//...
return getNewListIfConditionIsOkay(it)
}
return it
.map { // List<Object>
it.map { item -> toSomethingElse(item) }
}
.subscribeBy(myErrorFun) {
//...
}
My Observable returns only a list. What is your recommendation?
map is fine. Save doOnNext for side effect tasks, doOnNext actually doesn't return any value, so I don't think your code would even work here.
(I don't know if I completely understand your idea or not)
As far as I know, currently there no operator allows us to do as you want.
So, in order to solve your problem, the way I always try is combine operations.
Please see the details below:
First: a method to get Your List
private List getYourList() {
// do something here to get your list
return yourList;
}
Second: A method to get List with condition, remember to use Observable.fromCallable
private Observable<List> getListWithCondition() {
return Observable.fromCallable(new Callable<List<Employee>>() {
#Override
public List<Employee> call() throws Exception {
// check your condition if needed
if (My_Condition_is_true) {
//...
}
val newList = getNewListIfConditionIsOkay(it);
return newList;
}
});
}
Finally, do your work by calling function above
public void doYourWork() {
getListWithCondition().map(new Func1<List<>, Object>() {
item -> toSomethingElse(item)
}).subscribe();
}
Please let me know if I'm not get your point correctly, I'll remove my answer.
Hope that help.
Related
I'm just exploring Rxjava in one of my android application, and got stuck at one place, honestly speaking I'm very new to this library so don't mind if my question frustrate someone;-)
So I'm trying to access the Room Database using RxJava where I'm returning the Observable List, once I get this Observable I'm trying to use map operator to get a list of ids & query again the database, which again returns me the Observable List but the map operator expects List as a return type. How can I tackle this please suggest?
Below is the code snippet:
private void getAllPcbs() {
isLoading.setValue(true);
getCompositeDisposable().add(
getRepositoryManager().loadAllPcbDetails()
.flatMap((Function<List<PcbDetails>, ObservableSource<?>>) pcbDetails -> {
List<Long> pcbList = new ArrayList<>();
for (PcbDetails details : pcbDetails)
pcbList.add(details.getPcbId());
return getRepositoryManager().loadAllPcbs(pcbList);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError)
);
}
private void onError(Throwable throwable) {
isLoading.setValue(false);
}
private void onSuccess(Object o) {
isLoading.setValue(false);
pcbList.setValue((List<Pcb>) o);
}
public interface DbHelper {
Observable<List<PcbDetails>> loadAllPcbDetails();
Observable<List<Pcb>> loadAllPcbs(List<Long> pcbIdList);
}
Go like
getRepositoryManager().loadAllPcbDetails()
.flatMapIterable {
listPcbDetail-> listPcbDetail
// listPcbDetail is ArrayList<PcbDetails>
// Converts your list of ids into an Observable
// which emits every item in the list
}
.flatMap { pcbDetail ->
// pcbDetail is PcbDetails
getRepositoryManager().loadAllPcbs(pcbDetail.pcbIdList)
}.subscribe { listPcb ->
// listPcb is ArrayList<Pcb>
}
I have a specific task to get several packs of data from server by calling same request several times. While answers contains more flag - i have to recall this request.
It seems something like this:
fun getData(some params): Single<Response<DataEntity>>
//
repository.getData(params)
.flatMap {
if (it.body()?.more == false)
more = false
else (
// here i want to repeat my request
// repository.getData(params) to get more data
)
}
.flatMap {// here i want to get all the data from
//previous requests to save to db etc.
}
Maybe i have to use something like repeatWhen or repeautUntil operators but i can't find the solution for now. Please help!)
You can use the concatMap operator in a recursive way, and as exit condition return just the result:
Single<Response<DataEntity>> getDataAndContinue(params) {
return getData(params)
.concatMap(new Func1<Response<DataEntity>, Single<Response<DataEntity>>>() {
#Override
public Single<Response<DataEntity>> call(Response<DataEntity> response) {
if (!response.body().hasMore()) {
return Single.just(response);
}
return Single.just(response)
.concatWith(getDataAndContinue(params));
}
});
}
I want to convert every object in my list to an another object. But in doing so my code stucks in converting them back to List
override fun myFunction(): LiveData<MutableList<MyModel>> {
return mySdk
.getAllElements() // Returns Flowable<List<CustomObject>>
.flatMap { Flowable.fromIterable(it) }
.map {MyModel(it.name!!, it.phoneNumber!!) }
.toList() //Debugger does not enter here
.toFlowable()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
Everything is fine until mapping. But debugger does not even stop at toList or any other below toList. How can I solve this?
Using flatMap() you'll only flatten the Flowable of lists to a single Flowable of the elements. Calling toList() on it requires the Flowable to complete and therefore you'll most likely never get there. If you only want to map the elements in the list and have an item with the new list emitted, you should do the mapping within flatMap() or maybe try using concatMap() to keep the order:
...
.concatMapSingle { list ->
Observable.fromIterable(list).map {
MyModel(it.name!!, it.phoneNumber!!)
}.toList()
}
...
Here is my solution to this. Thanks to Tim for leading me to right answer.
override fun myFunction(): LiveData<MutableList<MyModel>> {
return mySdk
.getAllElements() // Returns Flowable<List<CustomObject>>
.flatMapSingle { Observable.fromIterable(it).map { MyModel(it.name!!, it.phoneNumber!!) }.toList() }
.toFlowable()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
I've implemented a recyclerview with paging with the Android's Paging Library (https://developer.android.com/topic/libraries/architecture/paging.html). It works fine on fetching data and retrieve subsequent pages. However, how to filter the PagedList ? Say I have a Search widget, and I want to search the list currently on screen. PagedList.filter() returns a List and PagedListAdapter.setList() won't accept a List.
I think you might be able to solve this with a MediatorLiveData.
Specifically Transformations.switchMap and some additional magic.
Currently I was using
public void reloadTasks() {
if(liveResults != null) {
liveResults.removeObserver(this);
}
liveResults = getFilteredResults();
liveResults.observeForever(this);
}
But if you think about it, you should be able to solve this without use of observeForever, especially if we consider that switchMap is also doing something similar.
So what we need is a LiveData that is switch-mapped to the LiveData> that we need.
private MutableLiveData<String> filterText = new MutableLiveData<>();
private final LiveData<List<T>> data;
public MyViewModel() {
data = Transformations.switchMap(
filterText,
(input) -> {
if(input == null || input.equals("")) {
return repository.getData();
} else {
return repository.getFilteredData(input); }
}
});
}
public LiveData<List<T>> getData() {
return data;
}
This way the actual changes from one to another are handled by a MediatorLiveData. If we want to cache the LiveDatas, then we can do in the anonymous instance that we pass to the method.
data = Transformations.switchMap(
filterText, new Function<String, LiveData<List<T>>>() {
private Map<String, LiveData<List<T>>> cachedLiveData = new HashMap<>();
#Override
public LiveData<List<T>> apply(String input) {
// ...
}
}
Basically , I want to check if i have data in my DB, and if i dont have, make an api call. I'm using this logic for making the request to the API:
private void requestDataToApi() {
mSubscribe = createRequest()
.delay(DELAY_SPLASH_SCREEN_SECONDS, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(categoryModels -> {
writeDataToDb(categoryModels);
}, (throwable -> {
dealError();
}));
}
And this logic to verify if there any data stored:
if (mRealm.where(CategoryModel.class).findAll().size() == 0) {
requestDataToApi();
} else {
getView().openMainActivity(readDataFromDb());
}
There is any way to join this both logics? Basically, be the dispose verifying the db and just make the call if needed?
You can use filter and switchIfEmpty operator
#Test
public void ifEmpty() throws InterruptedException {
Observable.just(getDataFromDatabase())
.filter(value -> !value.isEmpty())
.switchIfEmpty(Observable.just("No data in database so I go shopping"))
.subscribe(System.out::println);
}
private String getDataFromDatabase() {
if(new Random().nextBoolean()){
return "data";
}
return "";
}
You can learn more from reactive world here https://github.com/politrons/reactive
Looks like you need the Repository Pattern
What this pattern does it isolate the business logic from the data origin. So you just ask for data and don'r care where this data come from. So you could hava something like:
public class CategoryModelRepo {
public Observable<CategoryModel> getAll() {
return Observable.defer(() -> {
List<CategoryModel> fromRealm = mRealm.where(CategoryModel.class).findAll();
if (fromRealm.size() == 0) {
return requestDataToApi()
.onNext(dataList -> storeDataInRealm(dataList))
} else {
return Observable.just(fromRealm);
}
}
}
// This code fragment could be improved by applying a DAO pattern
// http://www.oracle.com/technetwork/java/dataaccessobject-138824.html
private Observable<CategoryModel> requestDataToApi() {
return createRequest()
.delay(DELAY_SPLASH_SCREEN_SECONDS, TimeUnit.SECONDS)
}
So from your business layer (or, in your case, view layer) you can load the data to ensure it has been stored locally.
Don't forget to use .subscribeOn(...) and .observeOn(...) where necessary
If you are willing to add one more dependency to your project, Store is a (very) nice solution. Otherwise, I would recommend using concepts from the Repository pattern.