I have a chained observable that I created like this:
Disposable disposable = currentUsedAdapter.connect(ip)
.observeOn(AndroidSchedulers.mainThread())
.concatMap(fallbackAdapter(ProtocolType.V2))
.delay(500, TimeUnit.MILLISECONDS)
.concatMap(fallbackAdapter(ProtocolType.V1))
.subscribeWith(connectionSubscriber);
and this is the method fallbackAdapter:
private Function<Boolean, Observable<Boolean>> fallbackAdapter(ProtocolType protocolType) {
return new Function<Boolean, Observable<Boolean>>() {
#Override
public Observable<Boolean> apply(#NonNull Boolean isConnected) throws Exception {
if (isConnected) {
return Observable.just(true);
} else {
TempAdapter adapter = new TempAdapter(context, protocolType);
return currentUsedAdapter.connect(ipAddress);
}
}
};
}
currently this is done staticlly, and it's working fine.
Though I want to create a list of those fallbackAdapter(ProtocolType.*) because I only know the amount of fallbacks during runtime.
So I created this:
ArrayList<Function<Boolean, Observable<Boolean>>> adaptersList = new ArrayList<>();
adaptersList.add(fallbackAdapter(ProtocolType.V2));
...
adaptersList.add(fallbackAdapter(ProtocolType.V9));
Disposable disposable = Observable.fromIterable(adaptersList)
.concatMap(adapter ->
adapter.apply(true))
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(connectionSubscriber);
I have created a list that I can update dynamiclly.
BUT, I am not sure how I can pass the value of isConnected from one adapter to another. I currently pass true to everyone, but some of them should return false, but I'm not sure how I can pass this value from one emitter to another using the Observable.fromIterable.
So my question is how should I change this .concatMap(adapter -> adapter.apply(true)) so that I won't always send true but rather I will send the value that been processed by the previous adapter?
thank you
If it helps anyone...
I didn't find an rxjava way to solve it so I solved in an on old java fashion way...
I have created a builder class and added an observable to my main observable and at the end I returned everything.
something like that:
public class DisposableBuilder {
Observable<Boolean> observable;
public DisposableBuilder() {
}
public void build(String ip) {
observable = currentUsedAdapter.connect(host);
if (adaptersNames != null) {
for (int i = 1; i < adaptersNames.size(); i++) { // skip first adapter (currentUsedAdapter adapter)
this.append(AdapterFactory.getAdapter(context, adaptersNames.get(i)));
}
}
}
public void append(CustomAdapter adapter) {
observable = observable
.delay(200, TimeUnit.MILLISECONDS)
.concatMap(fallbackAdapter(adapter));
}
public Observable<Boolean> getObservable() {
return observable;
}
}
and then I used it like so:
disposableBuilder.build(ip);
this.disposable = disposableBuilder.getObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(connectionSubscriber);
Related
Google's android architecture components tutorial here has a part that explains how to abstract the logic of getting data over the network. In it, they create an abstract class called NetworkBoundResource using LiveData to create a reactive stream as the basis for all reactive network requests.
public abstract class NetworkBoundResource<ResultType, RequestType> {
private final AppExecutors appExecutors;
private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
#MainThread
NetworkBoundResource(AppExecutors appExecutors) {
this.appExecutors = appExecutors;
result.setValue(Resource.loading(null));
LiveData<ResultType> dbSource = loadFromDb();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldFetch()) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, newData -> result.setValue(Resource.success(newData)));
}
});
}
private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
LiveData<ApiResponse<RequestType>> apiResponse = createCall();
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource, newData -> result.setValue(Resource.loading(newData)));
result.addSource(apiResponse, response -> {
result.removeSource(apiResponse);
result.removeSource(dbSource);
//noinspection ConstantConditions
if (response.isSuccessful()) {
appExecutors.diskIO().execute(() -> {
saveCallResult(processResponse(response));
appExecutors.mainThread().execute(() ->
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb(),
newData -> result.setValue(Resource.success(newData)))
);
});
} else {
onFetchFailed();
result.addSource(dbSource,
newData -> result.setValue(Resource.error(response.errorMessage, newData)));
}
});
}
protected void onFetchFailed() {
}
public LiveData<Resource<ResultType>> asLiveData() {
return result;
}
#WorkerThread
protected RequestType processResponse(ApiResponse<RequestType> response) {
return response.body;
}
#WorkerThread
protected abstract void saveCallResult(#NonNull RequestType item);
#MainThread
protected abstract boolean shouldFetch();
#NonNull
#MainThread
protected abstract LiveData<ResultType> loadFromDb();
#NonNull
#MainThread
protected abstract LiveData<ApiResponse<RequestType>> createCall();
}
From What I understand, the logic of this class is to:
a) Create a MediatorLiveData called "result" as the main return object and set its initial value to Resource.loading(null)
b) Get the data from Android Room db as dbSource LiveData and add it to "result" as a source LiveData
c) On dbSource LiveData's first emission, remove the dbSource LiveData from "result" and call "shouldFetchFromNetwork()" which will
IF TRUE, call "fetchDataFromNetwork(dbSource)" which creates a network call through "createCall()" that returns a LiveData of the response encapsulated as an ApiResponse object
add back dbSource LiveData to "result" and on set emitted values to Resource.loading(data)
add apiResponce LiveData to "result" and on first emission remove dbSource and apiResponce LiveDatas
If apiResponse is successful, call "saveCallResult(processResponse(response))" and add back dbSource LiveData to "result" and set emitted values to Resource.success(newData)
If apiResponse failed, call "onFetchFailed()" and add back dbSource LiveData to "result" and set emitted values to Resource.error(response.errorMessage, newData))
IF FALSE, just add the dbSource LiveData to "result" and set emitted values to Resource.success(newData)
Given that this logic is the correct interpretation, I have tried to refactor this class to use RxJava Observables instead of LiveData. This is my attempt at a successful refactoring (I removed the initial Resource.loading(null) as I see this as superfluous).
public abstract class NetworkBoundResource<ResultType, RequestType> {
private Observable<Resource<ResultType>> result;
#MainThread
NetworkBoundResource() {
Observable<Resource<ResultType>> source;
if (shouldFetch()) {
source = createCall()
.subscribeOn(Schedulers.io())
.doOnNext(apiResponse -> saveCallResult(processResponse(apiResponse)))
.flatMap(apiResponse -> loadFromDb().toObservable().map(Resource::success))
.doOnError(t -> onFetchFailed())
.onErrorResumeNext(t -> {
return loadFromDb()
.toObservable()
.map(data -> Resource.error(t.getMessage(), data))
})
.observeOn(AndroidSchedulers.mainThread());
} else {
source = loadFromDb()
.toObservable()
.map(Resource::success);
}
result = Observable.concat(
loadFromDb()
.toObservable()
.map(Resource::loading)
.take(1),
source
);
}
public Observable<Resource<ResultType>> asObservable() {return result;}
protected void onFetchFailed() {}
#WorkerThread
protected RequestType processResponse(ApiResponse<RequestType> response) {return response.body;}
#WorkerThread
protected abstract void saveCallResult(#NonNull RequestType item);
#MainThread
protected abstract boolean shouldFetch();
#NonNull
#MainThread
protected abstract Flowable<ResultType> loadFromDb();
#NonNull
#MainThread
protected abstract Observable<ApiResponse<RequestType>> createCall();
}
As I am new to RxJava, my question is am I correctly refactoring to RxJava and maintaining the same logic as the LiveData version of this class?
public abstract class ApiRepositorySource<RawResponse extends BaseResponse, ResultType> {
// result is a Flowable because Room Database only returns Flowables
// Retrofit response will also be folded into the stream as a Flowable
private Flowable<ApiResource<ResultType>> result;
private AppDatabase appDatabase;
#MainThread
ApiRepositorySource(AppDatabase appDatabase) {
this.appDatabase = appDatabase;
Flowable<ApiResource<ResultType>> source;
if (shouldFetch()) {
source = createCall()
.doOnNext(this::saveCallResult)
.flatMap(apiResponse -> loadFromDb().toObservable().map(ApiResource::success))
.doOnError(this::onFetchFailed)
.onErrorResumeNext(t -> {
return loadFromDb()
.toObservable()
.map(data -> {
ApiResource apiResource;
if (t instanceof HttpException && ((HttpException) t).code() >= 400 && ((HttpException) t).code() < 500) {
apiResource = ApiResource.invalid(t.getMessage(), data);
} else {
apiResource = ApiResource.error(t.getMessage(), data);
}
return apiResource;
});
})
.toFlowable(BackpressureStrategy.LATEST);
} else {
source = loadFromDb()
.subscribeOn(Schedulers.io())
.map(ApiResource::success);
}
result = Flowable.concat(initLoadDb()
.map(ApiResource::loading)
.take(1),
source)
.subscribeOn(Schedulers.io());
}
public Observable<ApiResource<ResultType>> asObservable() {
return result.toObservable();
}
#SuppressWarnings("WeakerAccess")
protected void onFetchFailed(Throwable t) {
Timber.e(t);
}
#WorkerThread
protected void saveCallResult(#NonNull RawResult resultType) {
resultType.saveResponseToDb(appDatabase);
}
#MainThread
protected abstract boolean shouldFetch();
#NonNull
#MainThread
protected abstract Flowable<ResultType> loadFromDb();
#NonNull
#MainThread
protected abstract Observable<RawResult> createCall();
#NonNull
#MainThread
protected Flowable<ResultType> initLoadDb() {
return loadFromDb();
}
}
So here is what I have decided on using after many iterations. This is currently in production and is working well for my app. Here are some take away notes:
Create a BaseResponse interface
public interface BaseResponse {
void saveResponseToDb(AppDatabase appDatabase);
}
and implement it in all of your api response object classes. Doing this means you don't have to implement save_to_database logic in every ApiResource, you can just default it to what ever the response's implementation is, if you want.
I have chosen to handle Retrofit error responses in the onErrorResumeNext block for simplicity, but I recommend you create a Transformer class that can hold all this logic. In this case, I added an extra Status enum value for ApiResources called INVALID for 400-level responses.
You might be tempted to use the the Reactive Streams architecture component library for LiveData
implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version" and add a method to this class called
public LiveData<ApiResource<ResultType>> asLiveData {
return LiveDataReactiveStreams.fromPublisher(result);
}
In theory, this would work perfectly as our ViewModels wouldn't have to convert Observable emissions to LiveData emissions or implement lifecycle logic for Observables in Views. Unfortunately, this stream gets rebuilt on every configuration change because it disposes of the LiveData in any onDestroy called (whether isFinishing is true or false). Thus, we have to manage the lifecycle of this stream, which defeats the purpose of using it in the first place, or have duplicated calls every time the device rotates.
Here is an example of a UserRepository creating an instance of an ApiNetworkResource
#Singleton
public class UserRepository {
private final RetrofitApi retrofitApi;
private final AppDatabase appDatabase;
#Inject
UserRepository(RetrofitApi retrofitApi, AppDatabase appDatabase) {
this.retrofitApi = retrofitApi;
this.appDatabase = appDatabase;
}
public Observable<ApiResource<User>> getUser(long userId) {
return new ApiRepositorySource<UserResponse, User>(appDatabase) {
#Override
protected boolean shouldFetch() {
return true;
}
#NonNull
#Override
protected Flowable<User> loadFromDb() {
return appDatabase.userDao().getUserFlowable(userId);
}
#NonNull
#Override
protected Observable<UserResponse> createCall() {
return retrofitApi.getUserById(userId);
}
}.asObservable();
}
}
How to sew two Observable in RxJava ?
Observable<List<CalendarEvent>>, for each CalendarEvent, I want to do network operation to read the (lat,lon) and fetch place name, and then combine place name back to CalendarEvent.
public Observable<List<CalendarEvent>> getEvents() {
// get events
// translate each Event LatLng to Place and bind it to Event
// return the events
}
public Observable<List<CalendarEvent>> getEvents() {
List<CalendarEvent> sourceList = ...
return Observable.from(sourceList) //emits each item separately
.concatMap(calendarEvent -> applyPlaceName(calendarEvent)) //fetches places and applies them for each item
//fyi: concatMap executes requests sequentially, if you want do it in parallel - use flatMap instead
.toList(); //collects items to list
}
//somewhere in your Networking class
public Observable<CalendarEvent> applyPlaceName(CalendarEvent calendarEvent) {
return Observable ... //do network call and apply placeName on item
}
//p.s. don't forget to apply appropriate Schedulers
No need for something fancy here, this would roughly do what you want I think:
public class Foobar {
void doSomethingWithEvents() {
getEvents().subscribe(new Action1<List<CalendarEvent>>() {
#Override
public void call(List<CalendarEvent> calendarEvents) {
for (CalendarEvent event : calendarEvents) {
getPlaceForEvent(event).subscribe(new Action1<Place>() {
#Override
public void call(Place place) {
event.setPlace(place);
}
});
}
}
});
}
Observable<Place> getPlaceForEvent(CalendarEvent event) {
return Observable.just(new Place());
}
Observable<List<CalendarEvent>> getEvents() {
return Observable.just(new ArrayList<CalendarEvent>());
}
}
Declaration
#GET("api/Game/SearchGames")
Observable<List<GameModel>> searchGames();
This is the network call
public static Observable<List<GameModel>> searchGames () {
VersusAPI client = VersusServiceGenerator.createService(VersusAPI.class);
Observable<List<GameModel>> ob = client.searchGames();
return ob;
}
Here is where I implement.
mAdapterMyGames = new RecyclerViewAdapter(searchGames());
searchGames() returns rx.Observable<java.util.list<GameModel>>. How do I change that to only java.util.list<GameModel>?
You don't properly understand what is an Observable.
It is an object, to which You can subscribe() to get the result of it's operation. Usually, only when subscribing to an Observable it starts and you can get the result inside Subscriber's onNext() function.
So in your case:
Subscribe to this Observable.
Look for the result inside this subscriber's onNext function.
searchGames().subscribe(new new Subscriber<List<GameModel>>() {
#Override
public void onNext(List<GameModel> gameModels) {
//TODO make sth useful with models
}
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
)
I'm using Retrofit and RxJava to perform some background tasks. Code looks like this:
public class MyLoader{
public Observable<MyData> getMyData(){
return setupHelper().flatMap(new Func1<MyHelper, Observable<MyData>>() {
#Override
public Observable<MyData> call(MyHelper myHelper) {
return queryData(myHelper);
}
});
}
private Observable<MyData> queryData(MyHelper myHelper){
...
}
private Observable<MyHelper> setupHelper(){
return Observable.create(new Observable.OnSubscribe<MyHelper>() {
#Override
public void call(final Subscriber<? super MyHelper> subscriber) {
try{
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
subscriber.onNext(helper);
subscriber.onCompleted();
}catch(RetrofitError e){
subscriber.onError(e)
}
}
}
}
}
This fails with RetrofitError, due to NetworkOnMainThread Exception at this line:
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
Subscribing to my Observable:
myLoader.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyData>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyData inventory) {
}
});
According to Rx documentation flatMap doesn't operate on any background thread. My question is how do I ensure that the whole getMyData() method runs in background.
I just add observeOn(Schedulers.newThread()) before flatMap and it works!
This moves just one step in the pipeline to the background thread:
Observable<Integer> vals = Observable.range(1,10);
vals.flatMap(val -> Observable.just(val)
.subscribeOn(Schedulers.computation())
.map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));
Originally answered here:
https://stackoverflow.com/a/35429084/2908525
There is a good chance when you create the MyLoader object in the main thread the Observable.create be executed as well (or maybe somewhere else before in your code (?) ). If it's so, the .subscribeOn(Schedulers.io()) will have no effect on changing the thread.
You can try wrap the .create() with a .defer() to make sure the Observable is created only when it's subscribed.
e.g. defer(() -> create(....))
I have a problem to implement following problem: I'm making a request that fetches all active leagues. Then, for each of them I need to make another request to grab the matches. I think it's possible to implement the solution using flatMapIterable, but don't know how.
For now I have following retrofit interfaces:
public interface LeaguesApi
{
#GET("/api/get-leagues")
Observable<ArrayList<League>> getLeagues(#Query("active_only") boolean activeOnly);
}
public interface LeagueApi
{
#GET("/api/get-league-fixtures/{leagueId}")
Observable<ArrayList<Match>> getMatchesPlayed(#Path("leagueId") int leagueId, #Query("played") boolean played);
}
Please advise how to iterate through all leagues in order to perform getMatchesPlayed for each of them. Best would be without lambda expressions, since I'm not using them in my project.
Try this code:
leaguesApi // your REST adapter from retrofit
.getLeagues(true) // fetch leagues
.flatMapIterable(leagues -> leagues) //Transform from ArrayList<Liague> to Observable<Liague>
.flatMap(l -> leagueApi.getMatchesPlayed(l.getId(), true))
.subscribe(
(match) -> {/*This is onNext*/},
t -> t.printStackTrace(), //onError
() -> {/*onComplete*/}
);
UPDATE:
Without lambdas:
leaguesApi // your REST adapter from retrofit
.getLeagues(true) // fetch leagues
.flatMapIterable(new Func1<ArrayList<League>, Iterable<?>>() {
#Override
public Iterable<?> call(ArrayList<League> leagues) {
return leagues;
}
}) //Transform from ArrayList<Liague> to Observable<Liague>
.flatMap(new Func1<League, Observable<Match>>() {
#Override
public Observable<Match> call(League l) {
return leagueApi.getMatchesPlayed(l.getId(), true);
}
})
.subscribe(
new Action1<Match>() {
#Override
public void call(Match match) {
//onNext
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//onError
}
},
new Action0() {
#Override
public void call() {
//onComplete
}
}
);
I'd change that API so it reads like this otherwise you lose a lot of the flexibility of streams:
public interface LeaguesApi
{
#GET("/api/get-leagues")
Observable<League> getLeagues(#Query("active_only") boolean activeOnly);
}
public interface LeagueApi
{
#GET("/api/get-league-fixtures/{leagueId}")
Observable<Match> getMatchesPlayed(#Path("leagueId") int leagueId, #Query("played") boolean played);
}
Can you do that?
If not then to get Observable<T> from Observable<ArrayList<T>> you do:
observable.flatMapIterable(
new Func1<ArrayList<T>, ArrayList<T>>() {
#Override
public ArrayList<T> call(ArrayList<T> list) {
return list;
}
});
much nicer to just say observable.flatMapIterable(x -> x) of course.
To get all played matches for all active leagues just do this:
Observable<League> leagues= getLeagues(true);
Observable<Match> matches =
leagues.flatMap( league -> getMatchesPlayed(league.leagueId, true));
or without lambdas (I wish you hadn't asked for that)
Observable<Match> matches = leagues.flatMap(
new Func1<League, Observable<Match>> () {
#Override
public Observable<Match> call(League league) {
return getMatchesPlayed(league.leagueId, true);
}
});