I'm using the packages:
// Apollo
implementation "com.apollographql.apollo:apollo-runtime:2.4.1"
implementation "com.apollographql.apollo:apollo-rx3-support:2.4.1"
// RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.4'
And I call function:
public static Flowable<Response<CallSubscription.Data>> callSubscription(String room) {
ApolloSubscriptionCall<CallSubscription.Data> call = getApolloClient()
.subscribe(new CallSubscription(room));
return Rx3Apollo.from(call)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext((dataResponse -> {
System.out.println("datat" + dataResponse);
if (dataResponse.getErrors() != null) {
throw new ApolloException(dataResponse.getErrors().get(0).getMessage());
}
}))
.filter((dataResponse -> dataResponse.getData() != null));
}
which is used here:
NetworkService.callSubscription(room).subscribeWith(new DisposableSubscriber<Response<CallSubscription.Data>>() {
#Override
protected void onStart() {
super.onStart();
System.out.println("onStart");
}
#Override
public void onNext(#NonNull Response<CallSubscription.Data> dataResponse) {
System.out.println("onNext " + dataResponse);
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("onError " + e);
}
#Override
public void onComplete() {
System.out.println("onComplete ");
}
});
And I need to handle exception from NodeJs inside subscription resolver, which is throw new NotFoundException('SOME_ERROR');, but I got just "onStart", nothing else (no "onNext", "onError", "onComplete"). While playground works fine and I'm handling exception there, and query and mutations also work fine with exceptions. What happened with Rx3Apollo subscriptions? it's my mistake or this package has bug?
Related
I was trying to connect SSE from android app but it have a problem.
It is not showing data 100%. Like in first attempt someone trigger notify command then it won't work but after that second attempt everybody get notify.
Here is my code:
//calling this code from activity on create
EventHandler eventHandler = new SimpleEventsListener();
String url = String.format(ApiInterface.ENDPOINT + "notify/");
EventSource.Builder builder = new EventSource.Builder(eventHandler, URI.create(url)).connectTimeoutMs(29 * 1000);
try (EventSource eventSource = builder.build()) {
for (; ; ) {
eventSource.start();
TimeUnit.SECONDS.sleep(30);
}
}
here is EventSource implementation.
public class SimpleEventsListener implements EventHandler {
#Override
public void onOpen() throws Exception {
System.out.println("SimpleEventsListener onOpen ");
}
#Override
public void onClosed() throws Exception {
System.out.println("SimpleEventsListener onClosed");
}
#Override
public void onMessage(String event, MessageEvent messageEvent) throws Exception {
System.out.println("SimpleEventsListener Event received " + messageEvent.getData());
// here implemented data recieved code which coming from messageEvent.getData()
}
#Override
public void onComment(String comment) throws Exception {
System.out.println("SimpleEventsListener onComment");
}
#Override
public void onError(Throwable t) {
System.out.println("SimpleEventsListener onError: " + t);
}
}
What is the of best way to receive data on the first attempt too?
I'm a little bit new to RxJava. I am trying to emit another item if onError() get called without losing the error(I still want onError() to be called on the observer). but when I'm implementing each of the error handling methods declared in the docs the error being swallowed and on error isn't being called. any solutions?
edit:
that's what I've tried to do yesterday -
#Override
public Observable<ArrayList<Address>> getAirports() {
return new Observable<ArrayList<AirportPOJO>>() {
#Override
protected void subscribeActual(Observer<? super ArrayList<AirportPOJO>> observer) {
try {
// get airports from api list and map it
ArrayList<AirportPOJO> airportsList = apiDb.getAirportsList(POJOHelper.toPOJO(AppCredentialManager.getCredentials()));
observer.onNext(airportsList);
} catch (Exception e) {
e.printStackTrace();
observer.onError(handleException(e));
}
}
}.map(AirportsMappers.getAirportsPojoToDomainAirportsMapper()).doOnNext(new Consumer<ArrayList<Address>>() {
#Override
public void accept(ArrayList<Address> airportsList) throws Exception {
// if airports loaded from api - save them to local db
if (airportsList != null) {
try {
localDb.saveAirportList(AirportsMappers.getAirportsToLocalDbAirportsMapper().apply(airportsList));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).onErrorResumeNext(new Function<Throwable, ObservableSource<? extends ArrayList<Address>>>() {
#Override
public ObservableSource<? extends ArrayList<Address>> apply(final Throwable throwable) throws Exception {
// load the local airports -
ArrayList<LocalDbAirportEntity> localAirportsEntities = localDb.getAirports();
// map
ArrayList<Address> airports = AirportsMappers.getLocalDbAirportsToAirportsMapper().apply(localAirportsEntities);
// return the concat observable with the error
return Observable.just(airports).concatWith(Observable.
<ArrayList<Address>>error(new Callable<Throwable>() {
#Override
public Throwable call() throws Exception {
return throwable;
}
}));
}
});
}
today I tought I might doing it wrong and tried -
#Override
public Observable<ArrayList<Address>> getAirports() {
ArrayList<Observable<ArrayList<Address>>> observables = new ArrayList<>();
observables.add(apiDb.getAirportsList(POJOHelper.toPOJO(AppCredentialManager.getCredentials())).map(AirportsMappers.getAirportsPojoToDomainAirportsMapper()));
observables.add(localDb.getAirports().map(AirportsMappers.getLocalDbAirportsToAirportsMapper()));
Observable<ArrayList<Address>> concatenatedObservable = Observable.concatDelayError(observables);
return concatenatedObservable;
}
but I've got the same result. the onNext() called with the data of the second observable and the onError() not being called afterwards.
Resume with the desired value concatenated with the original error:
source.onErrorResumeNext(error ->
Observable.just(item).concatWith(Observable.<ItemType>error(error))
);
I have an hour of experience using RxJava and I am trying to implement it in my project instead of using interfaces and listeners.
I have an async task which calls a google cloud endpoint method in a separate module and receives a List<Profile> when done.
In the onPostExecute() method of the async task, I call onNext so that any subscribers receive this data.
Here is what the AsyncTask looks like:
private BirthpayApi mApi;
private String mUserId;
private ReplaySubject<List<Profile>> notifier = ReplaySubject.create();
public GetFriends(String userId) {
mUserId = userId;
}
public Observable<List<Profile>> asObservable() {
return notifier;
}
#Override
protected List<Profile> doInBackground(Void... params) {
if (mApi == null) {
BirthpayApi.Builder builder = new BirthpayApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
mApi = builder.build();
}
try {
return mApi.getFriends(mUserId).execute().getItems();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(List<Profile> friends) {
super.onPostExecute(friends);
notifier.onNext(friends);
}
In my Fragment I then want to collect this data from the async task calling the onNext() method. I therefore use implements Action1<List<Profile>> when declaring my class which also extends Fragment.
In the onCall() method that comes from the Action1 interface I collect the data sent from the Async task:
#Override
public void call(List<Profile> profiles) {
if (profiles.size() > 0) {
updateAdapter(profiles);
} else
setUpNoFriendsViews();
}
I am following along with treehouse but they use a object to model their data which becomes the observable instead of using an async class and they use an adapter as the observer. Am I doing this wrong, either way how do I get it to work?
It doesn't look like you're subscribing to the Observable you're creating anywhere and it's not clear where you're calling execute on the AsyncTask. Also I don't think you'd want a ReplaySubject but that depends on what you're trying to achieve.
All that aside I'd suggest completely switching over to Rx rather than mixing up Rx and AsyncTasks. In this case in your model class make a method something like:
public Observable<List<Profile>> getProfiles() {
return Observable.defer(new Func0<Observable<List<Profile>>>() {
#Override
public Observable<List<Profile>> call() {
if (mApi == null) {
BirthpayApi.Builder builder = new BirthpayApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
mApi = builder.build();
}
try {
List<Profile> profiles = mApi.getFriends(mUserId).execute().getItems();
return Observable.just(profiles);
} catch (IOException e) {
return Observable.error(e);
}
}
});
}
Then in your Fragment:
modal.getProfiles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<List<Profile>>() {
//...
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}
);
How can you create a failsafe Spring XD stream, which will keep running properly after an exception is triggered for one specific message (i.e.logs the error but continues consuming the next messages in the stream), without having to add try catch(Throwable) in every Stream step?
Is there any easy way of doing this with the Reactor or RxJava model?
Example stream using Reactor:
#Override
public Publisher<Tuple> process(Stream<GenericMessage> inputStream) {
return inputStream
.flatMap(SomeClass::someFlatMap)
.filter(SomeClass::someFilter)
.when(Throwable.class, t -> log.error("error", t));
}
RxJava can be used by a processor module. On creation the subscription needs to be created and to handle errors the subscriber needs to add an onError handler:
subject = new SerializedSubject(PublishSubject.create());
Observable<?> outputStream = processor.process(subject);
subscription = outputStream.subscribe(new Action1<Object>() {
#Override
public void call(Object outputObject) {
if (ClassUtils.isAssignable(Message.class, outputObject.getClass())) {
getOutputChannel().send((Message) outputObject);
} else {
getOutputChannel().send(MessageBuilder.withPayload(outputObject).build());
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
}
}, new Action0() {
#Override
public void call() {
logger.error("Subscription close for [" + subscription + "]");
}
});
Look at more examples here: https://github.com/spring-projects/spring-xd/tree/master/spring-xd-rxjava/src
I've implemented Gdxpay into my libgdx game but when I call requestPurchase(), nothing happens. I followed this tutorial https://github.com/libgdx/gdx-pay/wiki/Integration-example-with-resolvers but I'm not sure where I'm going wrong.
Here is the main game class where the purchase observer is:
public MyGame extends Application adapter {
public MyGame(IActivityRequestHandler handler) {
// TODO Auto-generated constructor stub
super();
myRequestHandler = handler;
// ---- IAP: define products ---------------------
purchaseManagerConfig = new PurchaseManagerConfig();
purchaseManagerConfig.addOffer(new Offer().setType(OfferType.ENTITLEMENT).setIdentifier(SKU_REMOVE_ADS));
}
public PurchaseObserver purchaseObserver = new PurchaseObserver() {
#Override
public void handleRestore (Transaction[] transactions) {
for (int i = 0; i < transactions.length; i++) {
if (checkTransaction(transactions[i].getIdentifier()) == true) break;
}
// to make a purchase (results are reported to the observer)
PurchaseSystem.purchase(SKU_REMOVE_ADS);
}
#Override
public void handleRestoreError (Throwable e) {
// getPlatformResolver().showToast("PurchaseObserver: handleRestoreError!");
Gdx.app.log("ERROR", "PurchaseObserver: handleRestoreError!: " + e.getMessage());
throw new GdxRuntimeException(e);
}
#Override
public void handleInstall () {
// getPlatformResolver().showToast("PurchaseObserver: installed successfully...");
Gdx.app.log("handleInstall: ", "successfully..");
}
#Override
public void handleInstallError (Throwable e) {
//getPlatformResolver().showToast("PurchaseObserver: handleInstallError!");
Gdx.app.log("ERROR", "PurchaseObserver: handleInstallError!: " + e.getMessage());
throw new GdxRuntimeException(e);
}
#Override
public void handlePurchase (Transaction transaction) {
checkTransaction(transaction.getIdentifier());
}
#Override
public void handlePurchaseError (Throwable e) {
if (e.getMessage().equals("There has been a Problem with your Internet connection. Please try again later")) {
// this check is needed because user-cancel is a handlePurchaseError too)
// getPlatformResolver().showToast("handlePurchaseError: " + e.getMessage());
}
throw new GdxRuntimeException(e);
}
#Override
public void handlePurchaseCanceled () {
}
};
protected boolean checkTransaction (String ID) {
boolean returnbool = false;
if (SKU_REMOVE_ADS.equals(ID)) {
myRequestHandler.showAds(false);
returnbool = true;
}
return returnbool;
}
public void create() {
...
Here is where requestPurchase is called:
public class MainMenu extends Screen {
#Override
public void update() {
...
if (removeBounds.contains(touchPoint.x, touchPoint.y)) {
MyGame.getPlatformResolver().requestPurchase(MyGame.SKU_REMOVE_ADS);
}
}
...
}
Many thanks.
Edit: Ok logcat says the following error when I request a purchase:
5188-5220/com.comp.myGame.android I/ERRORīš gdx-pay: requestPurchase(): purchaseManager == null
So that means pruchaseManager is null, but according to the tutorial in this instance it should cause the correct purchaseManager to be called so I'm still confused...
I had exactly the same issue. I followed the tutorial as well, but changed the distributed resolver system to a more local defined system where all app store keys are set in the main game class.
This didn't work (with the same error you got). I then re-engineered the code to follow exactly the tutorial - with all the resolver bells and whistles. Next, I got a "no suitable app store found" error while creating the purchaseManager (at this point, I celebrated because it at least TRIED to create it).
I think that it worked the second try has something to do with the sequence flow:
In the android/AndroidLauncher.java, onCreate:
MyGame myGame = new MyGame(this);
initialize(myGame, config);
// init IAP
myGame.setPlatformResolver(new AndroidResolver(myGame, this));
In core/MyGame.java, declarations:
public PurchaseObserver purchaseObserver = new BrainsPurchaseObserver();
public PurchaseManagerConfig purchaseManagerConfig;
In core/MyGame.java, constructor:
purchaseManagerConfig = new PurchaseManagerConfig();
Offer iap15Tipps = new Offer();
iap15Tipps.setIdentifier(Product.brains_hints_15.name());
iap15Tipps.setType(OfferType.CONSUMABLE);
purchaseManagerConfig.addOffer(iap15Tipps);
PlatformResolver.java and AndroidResolver.java as described in the tutorial. This worked to the point of the above error "no app store found".
Then I switched from gdx-pay 0.3.0 to 0.4.0 (by just incrementing the version in the gradle settings, it is already available in the repository), AND IT WORKED!
I suggest you check the sequence of IAP initializing you execute and switch to 0.4.0 if you are not already using it.
-- Michael