I have some methods in a class like this:
#Override
public void sendRemoteRecord(String token, int channelId, int eventId, final ServiceCallback<RemoteRecordResponse> callback) {
epgServicesApiManager.sendRemoteRecord(token, channelId, eventId)
.observeOn(scheduler)
.subscribe(new Action1<RemoteRecordResponse>() {
#Override
public void call(RemoteRecordResponse model) {
if (callback != null)
callback.onSuccess(model);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (callback != null)
callback.onError();
}
});
}
#Override
public void activateRemoteRecord(String token, String cardNumber, final ServiceCallback<RemoteRecordActivateResponse> callback) {
epgServicesApiManager.activateRemoteRecord(token, cardNumber)
.observeOn(scheduler)
.subscribe(new Action1<RemoteRecordActivateResponse>() {
#Override
public void call(RemoteRecordActivateResponse remoteRecordActivateResponse) {
if (callback != null)
callback.onSuccess(remoteRecordActivateResponse);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (callback != null)
callback.onError();
}
});
}
Is it possible to remove the duplication around the code after the observeOn() line?
The annoying part is making sure I do the null check on the callback before using it.
At present, I know of seven distinct methods I need in this class and possibly more.
Unfortunately, in Java 1.7 there is no way to fix this without increasing the amount of code. You can reduce the amount of code needed locally, by introducing some helper classes.
One solution is to move your anonymous inner classes to top-level classes. From there you can introduce a dummy callback and some null-checking work an an abstract class.
It may end up looking something like this (horizontal rules are used to highlight that these classes are in separate files).
This is a dummy callback class, it does exactly nothing, but is safe to call against. This will replace the null values.
public class NullServiceCallBack<T> implements ServiceCallBack<T> {
#Override
public void onSuccess(T target) {}
#Override
public void onError() {}
}
This is an abstract class that handles the validation, converting null values to instances of NullServiceCallback:
public abstract class CallBackAction<T> implements Action1<T> {
private final ServiceCallBack<T> Callback;
public CallBackAction(ServiceCallBack<T> callback) {
this.Callback = (null != callback) ? callback : new NullServiceCallBack<>();
}
protected ServiceCallBack<T> getCallback() {
return Callback;
}
}
This is the concrete class you'll use for success.
public class SuccessCallbackAction<T> extends CallBackAction<T> {
public SuccessCallbackAction(ServiceCallBack<T> callback) {
super(callback);
}
#Override
public void call(T target) {
getCallback().onSuccess(target);
}
}
This is the concrete class for errors. This doesn't do anything with the arguments to call, so we can make this implement for Object once and be done with it.
public class ErrorCallbackAction extends CallBackAction<Object> {
public ErrorCallbackAction(ServiceCallBack<Object> callback) {
super(callback);
}
#Override
public void call(Throwable target) {
getCallback().onError();
}
}
So in the end, your example above should look something like this:
#Override
public void sendRemoteRecord(String token, int channelId, int eventId, final ServiceCallback<RemoteRecordResponse> callback) {
epgServicesApiManager.sendRemoteRecord(token, channelId, eventId)
.observeOn(scheduler)
.subscribe(new SuccessCallbackAction<RemoteRecordResponse>(callback),
new ErrorCallbackAction(callback));
}
#Override
public void activateRemoteRecord(String token, String cardNumber, final ServiceCallback<RemoteRecordActivateResponse> callback) {
epgServicesApiManager.activateRemoteRecord(token, cardNumber)
.observeOn(scheduler)
.subscribe(new SuccessCallbackAction<RemoteRecordActivateResponse>(callback),
new ErrorCallbackAction(callback));
}
Locally, we've reduced the amount of code, and made the intent a little more clear. Globally, we've increased the complexity with the addition of 4 new classes. Whether this is worth it depends on the context your code lives in, and is your call.
Introduce a dummy callback that does nothing, then do safeCallback().onSuccess() or safeCallback().onError()
Also, you can do this:
class SuccessCallback<T> extends Action1<T>() {
#Override
public void call(T value) {
safeCallback().onSuccess(value);
}
}
class ErrorCallback extends Action1<Throwable>() {
#Override
public void call(T value) {
safeCallback().onError();
}
}
then...
subscribe(new SuccessCallback<RemoteRecordActivateResponse>(), new ErrorCallback());
Does this work?
Related
Code
new Retrofit.Builder().baseUrl(Constants.BASE_URL_FILES).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build().create(RetrofitInterface.class).getCountries().observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleObserver<List<CountryModel>>() {
#Override
public void onSubscribe(#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.VISIBLE);
}
#Override
public void onSuccess(#io.reactivex.rxjava3.annotations.NonNull List<CountryModel> countryModels) {
fragmentCountriesBinding.fragmentCountriesRecyclerViewCountries.setAdapter(new CountriesAdapter(requireContext(), countryModels));
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
if (!MainActivity.isNetworkConnected(requireContext()))
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.no_internet, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).setAction(R.string.retry, view ->
{
}).show();
else
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.something_went_wrong, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).show();
}
});
RetrofitInterface
public interface RetrofitInterface {
#GET("GetCountries.php")
Single<List<CountryModel>> getCountries();
}
CountryModel
public class CountryModel {
private int countryId;
private String countryName, countryIcon;
public int getCountryId() {
return countryId;
}
public String getCountryName() {
return countryName;
}
public String getCountryIcon() {
return countryIcon;
}
}
As you can see inside the onError method, I want when the user clicks on the retry button must retry the call with the server again.
I read the articles talking about retryWhen method and tried to understand this image, but I can't understand it. And I don't know how to use it with my scenario.
My problem is like this question exactly.
My solution is simple and just a temporary/hack rather than spending couple of days using retryWhen().
In the code below, I encapsulate your request call to a function.
If there is a network error, just do a recursive call with the function until it will be successful.
private void sendRequest(){
new Retrofit.Builder().baseUrl(Constants.BASE_URL_FILES).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build().create(RetrofitInterface.class).getCountries().observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleObserver<List<CountryModel>>() {
#Override
public void onSubscribe(#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.VISIBLE);
}
#Override
public void onSuccess(#io.reactivex.rxjava3.annotations.NonNull List<CountryModel> countryModels) {
fragmentCountriesBinding.fragmentCountriesRecyclerViewCountries.setAdapter(new CountriesAdapter(requireContext(), countryModels));
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
MainActivity.activityMainBinding.activityMainLinearProgressIndicatorLoading.setVisibility(View.GONE);
if (!MainActivity.isNetworkConnected(requireContext()))
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.no_internet, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).setAction(R.string.retry, view ->
{
}).show();
// IF NO NETWORK AVAILABLE just do recursive call of the method
sendRequest();
else
Snackbar.make(MainActivity.activityMainBinding.getRoot(), R.string.something_went_wrong, BaseTransientBottomBar.LENGTH_INDEFINITE).setMaxInlineActionWidth(1).show();
}
});
}
I didn’t reach a solution, so I decided to use retryUntil instead of retryWhen.
I'm creating an user event system using JDK 9 Flow API, so I have a room (which extends the UserSubscriver class above), it may have many users and each user can offer (dispatch) updates at any time.
public abstract class UserSubscriver implements Flow.Subscriber<Notification> {
private Flow.Subscription subscription;
#Override
public void onSubscribe(final Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
#Override
public void onError(final Throwable throwable) {
// ...
}
#Override
public void onNext(final Notification notification) {
// ...
subscription.request(1);
}
#Override
public void onComplete() {
// How can I know who was the publisher of this?
}
}
User class:
public class User extends SubmissionPublisher<Notification> {
....
public int offer(Notification item) {
return super.offer(item, (sub, msg) -> false);
}
}
On the onUpdate I can receive any args, so I can receive the publisher of the update, but there are no args on onComplete.
How can I know who was the publisher of an onComplete event?
I want to display progressDialog while observable is downloading file , and when it's done want to send file to subscriber.
I tried to make my custom subscriber by extends from Subscriber for example:
public abstract class MySubscriber<T> extends Subscriber {
abstract void onMessage(String message);
abstract void onDownloaded(File file);
}
and tried to subscribe with it:
`
MySubscriber mySubscriber = new MySubscriber() {
#Override
public void onMessage(String message) {
progessDialog.setMessage(message);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
}
};
observable.subscribe(mySubscriber);
observable is :
observable = Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<Void> subscriber) {
//file downloading code...
if (subscriber instanceof MySubscriber){
((MySubscriber) subscriber).onMessage("100%");
((MySubscriber) subscriber).onDownloaded(file);
}else{
Log.e(TAG,"subscriber is not instance of MySubscriber")
}
}
And answer is "subscriber is not instance of MySubscriber"
The reason for subscriber not being of type MySubscriber is because the instance you pass is eventually wrapped by subscribe() in SafeSubscriber:
private static <T> Subscription subscribe(Subscriber<? super T> subscriber, Observable<T> observable) {
...
if(!(subscriber instanceof SafeSubscriber)) {
subscriber = new SafeSubscriber((Subscriber)subscriber);
}
...
}
}
If you want to keep using your approach, you can cast subscriber to SafeSubscriber and call SafeSubscriber#getActual() on it to get your instance of MySubscriber.
In your case:
Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
Subscriber yourSubscriber = ((SafeSubscriber) subscriber).getActual();
((MySubscriber) yourSubscriber).onMessage("100%");
((MySubscriber) yourSubscriber).onDownloaded(file);
}
});
I have a little question here.
private boolean isSomethingTrue(String param) {
boolean result = false;
myService.hasAlerts(param,new Callback<Boolean>(
#Override
public void onSuccess(Boolean hasAlerts) {
result = hasAlerts;
}
});
return result;
}
On this code, how can i return the boolean hasAlerts that is received in the callback?
This doesn't work because the result variable is not final.
But when it's final, it can't be modified so...
I've done something like that:
private boolean isSomethingTrue(String param) {
class ResultHolder {
boolean result=false;
}
final ResultHolder resultHolder = new ResultHolder();
myService.findBoolean(param,new Callback<Boolean>(
#Override
public void onSuccess(Boolean hasAlerts) {
resultHolder.result = hasAlerts;
}
});
return resultHolder.result;
}
But is there a simpler solution to handle such a case?
I've found this problem while trying to call a GWT RPC service.
I can think of a few variations--none of them particularly exciting. You could merge the result holder and callback into a single class and make it static if you could use it elsewhere, but it's not really an improvement.
private boolean isSomethingTrue(String param) {
class MyCallback implements Callback<Boolean> {
boolean result = false;
#Override
public void onSuccess(Boolean hasAlerts) {
result = hasAlerts;
}
}
final MyCallback callback = new MyCallback();
myService.findBoolean(param, callback);
return callback.result;
}
You could implement a generic synchronous Future, but that might be misleading.
Finally, if you're doing this often you could genericize the value holder.
public class Result<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
private boolean isSomethingTrue(String param) {
final Result<Boolean> result = new Result<Boolean>();
myService.findBoolean(param,new Callback<Boolean>(
#Override
public void onSuccess(Boolean hasAlerts) {
result.set(hasAlerts);
}
});
return result.get();
}
What you need is a synchronous RPC. See >here< and >here< for details.
But I would prefer to change your coding style (assumed you have access to the code that is calling isSomethingTrue()). Supposed you have some code like this calling your method isSomethingTrue:
if(isSomethingTrue("foo")) {
doSomeCoolStuff();
}
You can transform this to a asynchronous coding style by changing it to something like this:
isSomethingTrue("foo", new Callback<Boolean>(
#Override
public void onSuccess(Boolean result) {
if(result) {
doSomeCoolStuff();
}
}
});
and
private void isSomethingTrue(String param, Callback callback) {
myService.hasAlerts(param,callback);
}
I have an addProductButton, click it and it brings up a DialogBox containing bookNameTextBox, bookCategoryTextBox and addBookButton. Click addBookButton, it inserts the bookNameTextBox and bookCategoryTextBox contents into a database. If successful, it should hide the DialogBox and updateList()
The following code works to accomplish that but I'm not sure if it's proper or if there is a better way to achieve the same results.
Main class
addProductButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
//display addBookDialogBox
AddBookDialog abd = new AddBookDialog();
abd.displayDialog(new Callback() {
#Override
public void onSuccess() {
updateList();
}
public void onFailure() {
}
});
}
});
public void updateList() {
}
AddBookDialog class
public class AddBookDialog extends DialogBox {
private Callback callback;
private static AddBookDialogUiBinder uiBinder = GWT
.create(AddBookDialogUiBinder.class);
interface AddBookDialogUiBinder extends UiBinder<Widget, AddBookDialog> {
}
public AddBookDialog() {
setWidget(uiBinder.createAndBindUi(this));
}
public void displayDialog(Callback callback2) {
callback = callback2;
this.center();
}
#UiHandler("addBookButton")
void onAddBookButtonClick(ClickEvent event) {
//save book to database
Database db = Database.openDatabase("Store", "1.0", "My Store", 5 * 1024 * 1024);
db.transaction(new TransactionCallback() {
public void onTransactionStart(SQLTransaction tx) {
tx.executeSql("INSERT INTO products (bookName, bookCategory) VALUES (?, ?)", new Object[] { bookNameTextBox.getText().toString(), bookCategoryTextBox.getText().toString() });
}
public void onTransactionFailure(SQLError error) {
}
public void onTransactionSuccess() {
callback.onSuccess();
}
});
this.hide();
}
}
Callback interface
public interface Callback {
void onSuccess();
void onFailure();
}
I don't see the use of it here, why don't you just put updateList(); inside the onTransactionStart() method ?
CallBacks are used for Asynchronism, and the TransactionCallback here already does that, so I don't see why you would use another callback inside.
And by the way, if you did need a callback, why don't you use the AsyncCallback provided by gwt?
EDIT :
Then the appropriate way is to make an interface with your updateList() method. Make your main class implement it, and change the parameter type of your displayDialog() method to your interface type instead of CallBack type.