I created an Observable(RxJava2 + Volley) that repeat for each 5 seconds,
It works but when I Dump Java Heap(memory),there are many Instance of my Model JAVA class,and it will increase for each time that the Observable get repeating.
Why RX create several instance of my model? How can I use only ONE instance of it?
Model
public RequestFuture<String> getLiveRefreshFuture() {
RequestFuture<String> future = RequestFuture.newFuture();
VolleyStringRequest request = new VolleyStringRequest(Request.Method.POST
, REFRESH_URL
, future
, future) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return getRefreshParams();
}
};
VolleySingleton.getInstance().addToRequestQueue(request);
return future;
}
Activity
private final CompositeDisposable disposables = new CompositeDisposable();
final LiveRemoteModel model = DaggerLiveComponent.builder().build().getModel();
Observable<LiveResponse> observable = Observable
.interval(Constants.TOOLBAR_BADGES_REFRESH_DELAY, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.map(dummy -> model.getLiveRefreshFuture())
.map(RequestFuture::get)
.map(LiveResponse::new)
.observeOn(AndroidSchedulers.mainThread());
DisposableObserver<LiveResponse> disposableObserver =
new DisposableObserver<LiveResponse>() {
#Override
public void onNext(#NonNull LiveResponse liveResponse) {
setToolbarBadges(liveResponse.getToolbarBadges());
}
public void onError(#NonNull Throwable e) {
Log.e("RX", "onError: ", e);
}
#Override
public void onComplete() {
Log.d("RX", "onComplete: ");
}
};
disposables.add(observable.subscribeWith(disposableObserver));
Why RX create several instance of my model? How can I use only ONE instance of it?
If you look carefully the object in the heapdump is LiveRemoteModel$2 which indicates it is an anonymous class within LiveRemoteModel.
Looking at your code this is probably the VolleyStringRequest object that gets created each time model.getLiveRefreshFuture() is called. There is nothing retaining that object within the RX pipeline so there must be something within Volley retaining it.
Related
In my android app, I want to make multiple http requests using retrofit and rxjava to fetch json data. The number of request depends on the user's preferences (1 up to 40). Each request is independent and returns same type. So, i tried to apply the way that is recommended in this question (How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android) which uses the zip function of rx-java. But i couldn't find a way to get and combine the results of each request. Response type that i used for single request in retrofit was Response<List<NewsItem>> where NewsItem is my custom object. (the response is json array actually but in a single request retrofit automatically handles it and converts it into list of my custom object) What i tried so far is below:
My API Interface
public interface API {
String BASE_URL = "xxx/";
#GET("news/{source}")
Observable<List<NewsItem>> getNews(#Path("source") String source);
}
Viewmodel class to fetch data
public class NewsVM extends AndroidViewModel {
public NewsVM(Application application){
super(application);
}
private MutableLiveData<List<NewsItem>> newsLiveData;
public LiveData<List<NewsItem>> getNewsLiveData(ArrayList<String> mySourceList) {
newsLiveData = new MutableLiveData<>();
loadNews(mySourceList);
return newsLiveData;
}
private void loadNews(ArrayList<String> mySourceList) {
Gson gson = new GsonBuilder().setLenient().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
API api = retrofit.create(API.class);
//Gathering the requests into list of observables
List<Observable<?>> requests = new ArrayList<>();
for(String source: mySourceList){
requests.add(api.getNews(source));
}
// Zip all requests
Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
#Override
public List<NewsItem> apply(Object[] objects) throws Exception {
// I am not sure about the parameters and return type in here, probably wrong
return new ArrayList<>();
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(
new Consumer<List<NewsItem>>() {
#Override
public void accept(List<NewsItem> newsList) throws Exception {
Log.d("ONRESPONSE",newsList.toString());
newsLiveData.setValue(newsList);
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
Log.d("ONFAILURE", e.getMessage());
}
}
).dispose();
}
}
It doesn't give error but doesn't give response also since I couldn't handle the response. Can anybody help me in combining the results of the each request? I've searched all the questions but can't find an example like this.
try to use Observable.from(Iterable<? extends T> iterable) (Observable.fromArray() in rx-java2) instead of zip
So you'll have something like:
Observable.from(mySourceList)
.flatMap(new Func1<String, Observable<List<NewsItem>>>() {
#Override
public Observable<List<NewsItem>> call(String source) {
return api.getNews(source);
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.toList() // This will give you List<List<NewsItem>>
.map(new Func1<List<List<NewsItem>>, List<NewsItem>>() {
#Override
public List<NewsItem> call(List<List<NewsItem>> listOfList) {
//Merged list of lists to single list using Guava Library
List<NewsItem> list = Lists.newArrayList(Iterables.concat(listOfList));
return list;
}
})
.subscribe(new Subscriber<List<NewsItem>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(List<NewsItem> newsList) {
//Attached the final newslist to livedata
newsLiveData.setValue(newsList);
}
});
EDITED Updated the method
Object[] objects is the array of items returned by the requests. Assuming that each of your request returns a List<NewsItem> and you wanted to combine all NewsItem into a single List<NewsItem>, I think you can do something along the lines of:
private void loadNews(ArrayList<String> mySourceList) {
...
// Zip all requests
Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
#Override
public List<NewsItem> apply(Object[] objects) throws Exception {
List<NewsItem> combinedNewsItems = new ArrayList<>();
for (Object response : objects) {
combinedNewsItems.addAll((List<NewsItem>) response);
}
return combinedNewsItems;
}
})
.subscribeOn(Schedulers.io())
...
}
Be aware of the type casting.
If the type of data that you are getting is same List and the requests are multiple then you can use Recursion Method to make 1 to n requests and add data List on every success.
I tried googling this but results are all about what's changed between rxjava 1 and 2... less than helpful, lol.
I'm new to Rx Java and am working through it. I'm able to create an observable pretty easily and all subscribers are getting the right calls. I just have a question:
Object myObject = something1;
I create the observable and all subscribers and they get myObject in the onNext() method.
But, somewhere down the line, we do "myObject = something2".
How do I notify the subscribers that myObject has changed?
Here's how I'm creating the observable:
Observable<MyObject> myObservable = Observable.create(new ObservableOnSubscribe<MyObject>() {
#Override
public void subscribe(ObservableEmitter<MyObject> e) throws Exception {
MyObject myObject = someObject1;
e.onNext(myObject);
}
});
Here's how I'm subscribing:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(mDisposableObserver);
}
private DisposableObserver<MyObject> mDisposableObserver = new DisposableObserver<MyObject>() {
#Override
public void onNext(MyObject myObject) {
// Do UI stuff with 'myObject' variables
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
Side Note: If anybody has links or suggestions to really good Rx Java 2 tutorials (like... super beginner through god-status), I'd greatly appreciate it
You can't get notified straight after your object has been changed, if you dont observe for the "setters" of the object which notify somehow if the object has been changed. Using Observables from android architecture library fixes this issue.
Anyway, you can poll your Object for changes and notify the subscribers if the object has been changed. Using repeatWhen and distintUntilChanged makes sure that you dont get your data emitted if those are not changed. Example (untested) may be:
Observable<MyObject> myObservable = Observable.just(yourObject)
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1000, TimeUnit.MILLISECONDS)))
.distinctUntilChanged();
myObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(d -> Log.d("Data", "Got Object "+ d), e -> Log.e("Error", "Received Error:" + e);
Another better attempt is (like Observables from architecture components) that you modify your Object/Model to observe for changes.
That means that you create a Publish Subject inside your model which gets notified if the setter has been called emitting itself as value. Subscribing to this subject means that all subscribers who subscribe to this subject gets notified as soon as the setter has been called with the object which has been changed.
public static class ModelClass {
private PublishSubject<ModelClass> changeObservable = PublishSubject.create();
private String field1;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
changeObservable.onNext(this);
}
public Observable<ModelClass> getModelChanges() {
return changeObservable;
}
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ModelClass myModel = new ModelClass ();
myModel.getModelChanges().subscribe(c -> Log.d("Change:" + c));
myModel.setField1("1");
myModel.setField1("2");
myModel.setField1("3");
}
(Android/Java professional, RxJava/Lambda novice) I'm trying to create the following pojo:
public class ProductCombinedPojo {
private ProductPojo product;
private List<TemplatePojo> templates;
// builder pattern...
}
where ProductPojo is:
public class ProductPojo {
private List<String> templateUrls; // each url returns a TemplatePojo
// lots of other stuff...
}
I have the following Retrofit2 implementations:
#GET
Observable<ProductPojo> getProduct(#Url String url);
#GET
Observable<TemplatePojo> getTemplate(#Url String url);
So the first Observable returns the ProductPojo, the resulting list uf urls within are iterated over and input to the second Observable to get the list of TemplatePojos, finally the results are all combined into a new ProductCombinePojo using a builder pattern. To further complicate matters, due to the nature of the MVP framework, this all has to be done in a single Func0<<Observable<ProductCombinedPojo>> chained RxJava implementation.
I'm having difficulty at the end of the chain cleanly getting the original ProductPojo to inject into the builder. Here is my working but ugly solution (assume mUrl, mProductApi & mTemplateApi are all injected and defined as above):
#Override
public Observable<ProductCombinedPojo> call() {
final ProductPojo[] aProductPojo = new ProductPojo[1]; // <------------ Ugly!
return mProductApi
.getProduct(mUrl)
.flatMapIterable(new Func1<ProductPojo, List<String>>() {
#Override
public List<String> call(ProductPojo productPojo) {
aProductPojo[0] = productPojo; // <------------ Ugly!
return productPojo.getTemplateUrls();
}
})
.flatMap(new Func1<String, Observable<TemplatePojo>>(){
#Override
public Observable<TemplatePojo> call(String templateUrl) {
return mTemplateApi.getTemplate(templateUrl);
}
})
.toList()
.map(new Func1<List<TemplatePojo>, ProductCombinedPojo>() {
#Override
public ProductCombinedPojo call(List<TemplatePojo> templatePojos) {
return ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(aProductPojo[0]) // <------------ Ugly!
.templates(templatePojos)
.build();
}
});
}
How do I rewrite this so that I don't need the ugly final ProductPojo[]? After exhuastive searching and reviewing many similar questions on this forum, I think a
Func2<ProductPojo, List<TemplatePojo>, ProductCombinedPojo>
should be plugged in somewhere but I can't figure out exactly where. Whilst I am interested in what a Lambda solution will look like, the correct answer will be awarded to any solution using the format above.
The problem is that with every operation like map or flatMap, you transform the input into a new output. If you don't include the input in the output, you won't be able to access that input later on.
This is what you're facing here: you want to be able to access the ProductPojo further down the stream.
You could circumvent this by returning a Pair<ProductPojo, List<String>> in your flatMapIterable function, but this doesn't get any better either.
Instead you can create a new Observable in the scope of your ProductPojo:
public Observable<ProductCombinedPojo> call() {
return mProductApi.getProduct(mUrl)
.flatMap(new Func1<ProductPojo, Observable<ProductCombinedPojo>>() {
#Override
public Observable<ProductCombinedPojo> call(ProductPojo productPojo) {
return combinedPojoFor(productPojo);
}
});
}
private Observable<ProductCombinedPojo> combinedPojoFor(final ProductPojo productPojo) {
return Observable.from(productPojo.getTemplateUrls())
.flatMap(new Func1<String, Observable<TemplatePojo>>() {
#Override
public Observable<TemplatePojo> call(String templateUrl) {
return mTemplateApi.getTemplate(templateUrl);
}
})
.toList()
.map(new Func1<List<TemplatePojo>, ProductCombinedPojo>() {
#Override
public ProductCombinedPojo call(List<TemplatePojo> templatePojos) {
return ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(productPojo)
.templates(templatePojos)
.build();
}
});
}
Using lambdas:
public Observable<ProductCombinedPojo> call() {
return mProductApi.getProduct(mUrl)
.flatMap((productPojo) -> combinedPojoFor(productPojo));
}
private Observable<ProductCombinedPojo> combinedPojoFor(final ProductPojo productPojo) {
return Observable.from(productPojo.getTemplateUrls())
.flatMap((templateUrl) -> mTemplateApi.getTemplate(templateUrl))
.toList()
.map((templatePojos ->
ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(productPojo)
.templates(templatePojos)
.build()
));
}
I have a list of Observables like so:
List<Observable<MyObj>> listObservables = new ArrayList<Observable<MyObj>>();
I'd like to combine all Observable in a single one, I can handle it if I know the number of Observable using zip(), for example we have 3 Observable:
Observable<MyObj1> obs1= MyRestClient.getSomeData1();
Observable<MyObj2> obs2= MyRestClient.getSomeData2();
Observable<MyObj3> obs3= MyRestClient.getSomeData3();
I have a wrapper obj:
class MyWrapperObj {
private MyObj1 onj1;
private MyObj2 onj2;
private MyObj3 onj3;
public MyWrapperObj(MyObj1 onj1, MyObj2 onj2, MyObj3 onj3) {
this.onj1 = onj1;
this.onj2 = onj2;
this.onj3 = onj3;
}
}
So I can combine them like so:
Observable<MyWrapperObj> combinedObservable = Observable.zip(obs1, obs2, obs3, new Func3<MyObj1, MyObj2, MyObj3, MyWrapperObj>() {
#Override
public MyWrapperObj call(MyObj1 obj1, MyObj2 obj2, MyObj3 obj3) {
return new MyWrapperObj(obj1, obj2, obj3);
}
});
combinedObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyWrapperObj>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(MyWrapperObj wrapperObj) {
}
});
Everything is working fine, so my problem is how to organize this combination to be for n Observable?
RESPONSE
as #maciekjanusz mentioned in it's answer I did:
Observable<MyWrapperObj> combinedObservable = Observable.zip(listObservables, new FuncN<MyWrapperObj>() {
#Override
public MyWrapperObjcall(Object... args) {
return null;
}
});
If you want to zip n Observables, put them in a list and apply the public static <R> Observable<R> zip(#NotNull java.lang.Iterable<? extends Observable<?>> ws, rx.functions.FuncN<? extends R> zipFunction) factory method.
List<Observable<String>> observables = Arrays.asList(Observable.just("String 1"), Observable.just("String 2"));
Observable.zip(observables, args -> {
// put your zipping code here
});
For example, if you want to create a list of strings for each emission from all observables:
Observable.zip(observables, Arrays::asList);
Or, if using RxJava on android without retrolambda:
Observable.zip(observables, args -> Arrays.asList(args));
Suppose you have the list:
List<Observable<MyObj>> listObservables
You might consider using Observable.concatDelayError
The advantage if it is finishing all Obbservable's even if any of them finishes with error (resulting in an error in such case).
Remember, that every Observable in this sequence must return the result to onNext method of Subscriber. The result also must have the same type.
Example:
Observable.concatDelayError(listObservables);
You can wait until all observables is complete by using
.zip(observable1, ..., observableN, funcN).first() operators. There is an overload, accepting Observable> argument (as in FlatMap).
First overload takes Iterable> - you can pass list of observables of arbitrary size and second argument - FuncN - receives list of values.
I'm trying to use RxJava to combine the responses from two API calls. The scenario is the following: The first API call returns a list of User objects. Each User object contain a postcode, but they do not contain a city name. To get the city name a second API is called, with the parameter being the postcode.
Ideally I want to modify the original list of User objects, and add the city name after it has been fetched in the second API call, but I'm stuck at figuring out how to do it.
This seems like a pretty straightforward observable:
Dummy API for users:
public static List<String> getUsers() {
return new ArrayList<String>() {
/**
*
*/
private static final long serialVersionUID = 1L;
{
add("Anakin");
add("Darth");
}
};
}
Dummy API for city:
public static String getCity(String name) {
// dummy ..
return "Mos Espa";
}
Merging together:
public static void main(String[] args) throws InterruptedException {
Observable.from(getUsers()).flatMap(
// we have the user
user -> Observable.just(getCity(user)).map(
// we have the city
city -> user + " from " + city)) // do stuff with user + city
.subscribe(result -> System.out.println(result));
}
Here is my understanding of your question. You have 2 API calls...
public Observable<List<User>> getUsers();
public Observable<String> getCityName(String postalCode);
And you want to set each user's city name based on the call to getCityName. You can convert the List into an observable stream by flatMapping it into an Observable from an Iterable. Here is one way to do that.
Observable<User> usersWithCity = getUsers()
.flatMap(new Func1<List<User>, Observable<User>>() {
#Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}})
.flatMap(new Func1<User, Observable<User>>() {
#Override
public Observable<User> call(User user) {
return getCityName(user.getPostalCode())
.doOnNext(new Action1<String>() {
#Override
public void call(String t) {
user.setCityName(t);
}
})
.map(new Func1<String, User>() {
#Override
public User call(String cityName) {
return user;
}
});
}
});
Note that this makes the assumption that there will only ever be one cityName for each postalCode.