The way I'm getting callbacks from network requests is via interfaces.
Suppose there are two classes, A & B. Class A initiates all network requests which are performed by B. When B finishes the task, it has to respond to A.
The way I do it is:
public interface MyCallback {
void onTaskDone(String successMessage);
void onTaskFailed(String failMessage);
}
public class A {
onCreate() {
B objectB = new B();
objectB.loginUser(username, password, new MyCallback {
void onTaskDone(successmessage) {
//this is called when task is successful
}
void onTaskFailed(failMessage) {
//this is called when task is failed
});
}
}
}
public class B {
public void loginUser(String username, String password, MyCallback callback) {
//after task is performed
if (task.isSuccessful()) {
callback.onTaskDone("Successful");
} else {
callback.onTaskFailed("Programming is fun they said...");
}
}
}
As you can see, if a task is successful the interface methods are called from B which is received in A.
What my question is: Are there better ways to get callbacks besides using interfaces, or can this technique be made better? One issue I face while implementing this technique is, say I'm using same interface with many methods. In a particular case only one or two methods are used, while the rest remain unused, e,g. class B may never call onTaskFailed(). Is it normal that some methods are completely unused?
Android has a very good third party library like EventBus
https://github.com/greenrobot/EventBus
You can see its documentation, very easy to use.
public class A{
onCreate(){
B objectB = new B();
objectB.loginUser(username,password); //no need to pass callback
#Subscribe(threadMode = ThreadMode.MAIN)
public void onSuccessEvent(SuccessEvent successEvent) {
//this is called when task is successful
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onErrorEvent(ErrorEventsuccessEvent) {
//this is called when task is failed
}
}
public class B{
public void loginUser(String username, String password){
//after task is performed
if(task.isSuccessful()){
EventBus.getDefault().post(new SuccessEvent("Successful"));
}else{
EventBus.getDefault().post(new ErrorEvent("Programming is fun they said..."));
}
}
Your event classes
public class SuccessEvent {
private String message;
public SuccessEvent(String message) {
this.message=message;
}
}
public class ErrorEvent {
private String message;
public ErrorEvent(String message) {
this.message=message;
}
}
I found the answer to the question at the bottom: i.e the interface methods going unused.
To solve that I used abstract class! i.e an abstract class will implement all the interface callbacks.
Then while passing callback, simply pass the abstract class instance instead of the interface. This way, only the *required method can be overridden for getting the result.
Related
I need to pass some data between two activities MainActivity and ChildActivity. Button click on MainActivity should open ChildActivity and send event with data. I have singleton:
Subject<Object, Object> subject = new SerializedSubject<>(PublishSubject.create());
and in MainActivity I have the following button click handler:
public void onClick(){
startActivity(new Intent(MainActivity.this, ChildActivity.class));
subject.onNext(new SomeEvent(data));
}
and event listener subscription in ChildActivity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addEventListeners();
}
private void addEventListeners() {
subject.ofType(SomeEvent.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribe(
event -> {
loadData(event.getData());
});
}
When I send event after starting activity and call addEventListeners in ChildActivity onCreate is still not subscribed to this event and loadData() is not called.
What is proper way to pass data between activities using RxJava (if it's possible)?
if anybody needs a complete solution to send data between activities using RxJava2
1- Create the bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
2- the sender activity
//the data to be based
MyData data =getMyData();
RxBus.getSubject().onNext(data) ;
startActivity(new Intent(MainActivity.this, AnotherAct.class));
3-the receiver activity
disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof MyData) {
Log.d("tag", (MyData)o.getData();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
4-unSubscribe to avoid memory leacks:
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Reason:
Problem is that you are using PublishSubject. As per documentation of PublishSubject emits all the subsequent items of the source Observable at the time of the subscription. So in your case it will emit event only if it is subscribed.
Fix for your problem
Instead of using PublishSubject use BehaviorSubject which emits the most recently emitted item and all the subsequent items of the source Observable when a observer subscribe to it.
Browse following link for more details.
Before using rx.Observable, I used a custom callback with retrofit so I can add some specific logic for handling response/error and not have to do that inside the callback for every request as boilerplate code.
I force users to use the custom callback by putting it in the method signature like this:
#GET("/user_endpoint/")
void getUser(CustomCallback<User> callback);
#GET("/profile_endpoint/")
void getProfile(CustomCallback<Profile> callback);
but now that I'm returning an Observable:
#GET("/user_endpoint/")
Observable<User> getUser();
#GET("/profile_endpoint/")
Observable<Profile> getProfile();
I can't figure out a way to make sure that a custom callback always proxies the error/response.
Also, with retrofit2.0, how can I force the user to use a custom callback with the returned Call object?
CustomCallback for reference:
public abstract class CustomCallback<T> implements Callback<T> {
#Override public final void success(T t, Response response) {
// do some logic
onSuccess(t);
}
#Override public final void failure(RetrofitError error) {
// do something with the error here such as show a Toast
Toast.makeText(Application.getInstance(), error.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
onFailure(error);
}
public abstract void onSuccess(T response);
public abstract void onFailure(Throwable error);
}
Stop. You're thinking this the wrong way.
Instead consider this: You have the normal Retrofit interface:
interface Foo {
#GET("/user_endpoint/")
Observable<User> getUser();
}
And then you have your decorator class:
public class FooDecorator implements Foo {
private Foo delegate = ...; // inject or create the Retrofit instance.
#Override
public Observable<User> getUser() {
return delegate.getUser().doOnNext(...).doOnError(...);
}
}
Then you use only the second class everywhere in your code (preferably just let the DI system use that) and you're set.
If you're feeling adventurous, you could even adapt the RxJavaCallAdapterFactory so that it modifies the returned observables without the need of a custom class.
I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger
I am trying to create a handler to make my api calls using retrofit.
My call work and I get a response, but I would like to know what would be a best practice to return the response.
Here is a simplified version of my code:
public class ApiHandler{
private static String username = "myUsername";
private static String sessionID = "mySessionID";
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse ObjectResponse, Response response) {
//Request succes
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
Then I just call from my fragment: myObjectResponse = ApiHandler.getObjects(id);
My problem is that when the success() method is called in the callback, the getObject() method already ended and I'm not able to return the response.
I saw that I could use an eventbus (otto) to achieve that but I'm not sure how that works or if it even is the best way to do that.
So how could I achieve that?
I finally found my answer.
Using otto I just need to send the objectResponse to the bus as followed:
public class ApiHandler{
private static String username = myUsername;
private static String sessionID = mySessionID;
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse objectResponse, Response response) {
//Request success
BusProvider.getInstance().post(objectResponse);
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
And then in my fragment, register as a receiver:
#Override
public void onPause(){
super.onPause();
//Unregister the fragment from the bus provider
BusProvider.getInstance().unregister(this);
}
#Override
public void onResume(){
super.onResume();
//Register the fragment to be able to receive events through the bus provider
BusProvider.getInstance().register(this);
}
#Subscribe
public void onObjectResponse(ObjectResponse objectResponse){
System.out.println("We did it!!!");
//Do what you want with your object!
}
Before EventBus, an interface was required as a private member of the ApiHandler class (in your case). Now on success, you call your unimplemented method of your interface.
You implement this method somewhere in your UI, and you call getObject() to run in the background. Once the method is successful, it will trigger your interface method which you have implemented in your UI along with the objectResponse to play around with.
EventBus has quite similar concept except that it is a central (or singular) and works in a channel/subscribe manner. Try to use Eventbus + Retrofit + any dependency injector to ease your life.
During the most recent Google IO, there was a presentation about implementing restful client applications. Unfortunately, it was only a high level discussion with no source code of the implementation.
In this diagram, on the return path there are various different callbacks to other methods.
How do I declare what these methods are?
I understand the idea of a callback - a piece of code that gets called after a certain event has happened, but I don't know how to implement it. The only way I've implemented callbacks so far have been overriding various methods (onActivityResult for example).
I feel like I have a basic understanding of the design pattern, but I keep on getting tripped up on how to handle the return path.
In many cases, you have an interface and pass along an object that implements it. Dialogs for example have the OnClickListener.
Just as a random example:
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callbackCall() {
// callback code goes here
}
}
worker.callback = new Callback();
// Option 2:
worker.callback = new MyCallback() {
void callbackCall() {
// callback code goes here
}
};
I probably messed up the syntax in option 2. It's early.
When something happens in my view I fire off an event that my activity is listening for:
// DECLARED IN (CUSTOM) VIEW
private OnScoreSavedListener onScoreSavedListener;
public interface OnScoreSavedListener {
public void onScoreSaved();
}
// ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD
// FROM WITHIN ACTIVITY
public void setOnScoreSavedListener(OnScoreSavedListener listener) {
onScoreSavedListener = listener;
}
// DECLARED IN ACTIVITY
MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
slider.setOnScoreSavedListener(new OnScoreSavedListener() {
#Override
public void onScoreSaved() {
Log.v("","EVENT FIRED");
}
});
If you want to know more about communication (callbacks) between fragments see here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
No need to define a new interface when you can use an existing one: android.os.Handler.Callback. Pass an object of type Callback, and invoke callback's handleMessage(Message msg).
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
to clarify a bit on dragon's answer (since it took me a while to figure out what to do with Handler.Callback):
Handler can be used to execute callbacks in the current or another thread, by passing it Messages. the Message holds data to be used from the callback. a Handler.Callback can be passed to the constructor of Handler in order to avoid extending Handler directly. thus, to execute some code via callback from the current thread:
Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>
Callback callback = new Callback() {
public boolean handleMessage(Message msg) {
<code to be executed during callback>
}
};
Handler handler = new Handler(callback);
handler.sendMessage(message);
EDIT: just realized there's a better way to get the same result (minus control of exactly when to execute the callback):
post(new Runnable() {
#Override
public void run() {
<code to be executed during callback>
}
});
You can also use LocalBroadcast for this purpose. Here is a quick guide
Create a broadcast receiver:
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter("speedExceeded"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
Double currentLatitude = intent.getDoubleExtra("latitude", 0);
Double currentLongitude = intent.getDoubleExtra("longitude", 0);
// ... react to local broadcast message
}
This is how you can trigger it
Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
unRegister receiver in onPause:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
asume the main function is the activity that is triggering the event:
fun main() {
val worker = Worker()
worker.setOnCallListener(
object: OnCallListener {
override fun onCall() {
// here we define what should happen
// when the event accures
print("event happend")
}
}
)
// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}
the Worker class has an instance of Type OnCallListener interface
and a method to set its value:
class Worker() {
lateinit var listener: OnCallListener
fun setOnCallListener(listener: OnCallListener) {
this.listener = listener
}
}
and the OnCallListener interface looks like this:
interface OnCallListener {
fun onCall()
}