How to Define Callbacks in Android? - android

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()
}

Related

Best way to get callbacks from interface

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.

Mockito: Runnable.run() of Runnable parameter passed to a method is not called

I am testing with Mockito. I Have a callback interface:
interface Callback {
void onMessageRetrieved(String message);
void onRetrievalFailed(String error);
}
Then, I have a method that accepts Runnable object that calls that Callback method:
Firstly, interactor.run() calls this method that calls postMessage:
#Override
public void run() {
final String message = mMessageRepository.getWelcomeMessage();
if (message == null || message.length() == 0) {
notifyError();
return;
}
postMessage(message);
}
private void postMessage(final String msg) {
mMainThread.post(new Runnable() {
#Override
public void run() {
mCallback.onMessageRetrieved(msg);
}
});
}
But line mCallback.onMessageRetrieved(msg); is never executed and so I get:
Wanted but not invoked:
callback.onMessageRetrieved(
"Welcome, friend!"
);
Why is run() not executed?
I am working with the project from this articles. This is my Test class:
public class ExampleUnitTest {
private MessageRepository mMessageRepository;
private Executor mExecutor;
private MainThreadImpl mMainThread;
private WelcomingInteractor.Callback mMockedCallback;
#Before
public void setUp() {
mMessageRepository = mock(MessageRepository.class);
mExecutor = mock(Executor.class);
mMainThread = mock(MainThreadImpl.class);
mMockedCallback = mock(WelcomingInteractor.Callback.class);
}
#Test
public void testWelcomeMessageFound() throws Exception {
String msg = "Welcome, friend!";
when(mMessageRepository.getWelcomeMessage()).thenReturn(msg);
WelcomingInteractorImpl interactor = new WelcomingInteractorImpl(
mExecutor,
mMainThread,
mMockedCallback,
mMessageRepository);
interactor.run();
Mockito.verify(mMessageRepository).getWelcomeMessage();
Mockito.verifyNoMoreInteractions(mMessageRepository);
Mockito.verify(mMockedCallback).onMessageRetrieved(msg);
}
}
Here:
mMainThread.post(new Runnable()
But in your unit test, you have:
mExecutor = mock(Executor.class);
mMainThread = mock(MainThreadImpl.class);
That post() call receives a Runnable object. And I assume that your impl class runs Runnable.run() in some thread.
But: within your unit test, you are mocking all those elements.
You have to understand: mMainThread = mock(MainThreadImpl.class); doesn't create a real MainThreadImpl object. It returns something that looks like an object of that class; but this is a mock. It is completely decoupled from your actual implementation. When you do mMainThread.poll() ... nothing will happen. That run method is never executed. Because the mock object does nothing about that parameter when you invoke poll() on it.
In other words: you have to configure all your mocks. For example you can use an ArgumentCaptor and capture the Runnable object given to the post() method.
But of course, as that will be just some instance of an anonymous inner class, this will not help too much.
Long story short: you should step back; and do some more reading how/why you use mock objects...

How to unit test methods posted by Event Bus in android?

I am using Otto's event bus in my application. In one of my classes I am posting the event.
MyEvent myevent = new MyEvent();
uiBus.post(myEvent);
I am able to test the post method.
Now there is another class which is receiving the event.
//ReceiverClass.java
#Subscribe
public void onEventReceived(MyEvent myevent) {
callAMethod();
}
How do I unit test that this method was invoked. I tried with the following test code
#Mock
Bus uiBus;
#Test
public void testBusReceviedEvent() {
ReceiverClass instance = new ReceiverClass();
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myEvent);
//Test
verify(instance, times(1)).callAMethod();
}
But this code doesn't work.
I'm a little late to the party but here is an example of a class which works and accounts for async calls. Instead of Mocking EventBus we simply let it do it's thing and register it in the TestDriver class below.
The thing that makes this work is the CountDownLatch which, with the help of the abstract DataTransferCallback class, waits for latch.countDown() to be called or 5 seconds to go by.
Just register your test class and in the #Subscribe method, pass it back to the method that created the DataTransferCallback and do your assertions there.
#RunWith(AndroidJUnit4.class)
public class TestDriver {
private final CountDownLatch latch = new CountDownLatch(1);
private EventBus eventBus;
private DataTransferCallback transferCallback;
public abstract class DataTransferCallback {
abstract void onSuccess(DataTransfer event);
}
#Before
public void setUp() {
EventBus.getDefault().register(this);
eventBus = spy(EventBus.getDefault());
}
#SuppressWarnings("unchecked")
#Test
public void test200Resposne() throws InterruptedException {
// Get known good JSON
final String json = TestJSON.get200Response();
// Class under test
final Driver driver = new Driver(InstrumentationRegistry.getTargetContext());
final JsonParser jsonParser = new JsonParser();
//boolean to hold our test result
final boolean[] testPassed = new boolean[]{false};
transferCallback = new DataTransferCallback() {
#Override
public void onSuccess(DataTransfer event) {
assertNotNull(event);
verify(eventBus).post(event);
assertThat(event.getStatus(), is("OK"));
assertTrue(event.getData() != null);
testPassed[0] = true;
}
};
//Set our test EventBus object
driver.setEventBus(eventBus);
// The actual method under test
driver.parseData(jsonParser.parse(json));
// Set a countdown latch to wait for the result (5s)
latch.await(5000, TimeUnit.MILLISECONDS);
// will wait here until 5s or the #Subscrube method is hit
assertTrue(testPassed[0]);
}
//Because we want to examine EventBus Output, register it
//to this class and pass the event back through our custom abstract class
#Subscribe
public void onReceiveEventBusEvent(DataTransfer event) {
assertNotNull(transferCallback);
transferCallback.onSuccess(event);
//notify latch so that we can proceed
latch.countDown();
}
}
It does not work because instance is not a mock. You will have to verify the effects of callAMethod or put that method in another class and inject a mock of this new class into your ReceiverClass class.
For example...
private class ReceiverClass {
private MyNewClass theNewClassIWasTalkingAbout;
// Stick in a setter for that ^
#Subscribe
public void onEventReceived(MyEvent myevent) {
theNewClassIWasTalkingAbout.callAMethod();
}
}
Then your test will have to change slightly...
#Mock
private MyNewClass mockNewClass;
#InjectMocks // This will be the "solid" implementation of the thing you are trying to test, it is not a mock...
private ReceiverClass instance;
#Test
public void testBusReceivedEvent() {
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myevent);
verify(mockNewClass, times(1)).callAMethod();
}
Hope this helps.

How to notify an activity when GlobalVariables are changed

I have an android application that is connected to the computer via USB cable. I use a TCPServer Class to send messages and listen. For example:
When I send a message like: request:x
I get the response: response:x:55
I need to make changes on my activity according to the response I get. At the moment I temporarily solved the problem by passing activity and activity class object to the TCPServer's constructor
public TCPServer(int portNum, Activity activity, IntroActivity ia) {
super();
port = portNum;
this.activity = activity;
this.ia = ia;
}
Then after I receive the response:
void updateButton(final int color, final String txt) {
activity.runOnUiThread(new Runnable() {
public void run() {
ia.getConnectionButton().setBackgroundColor(color);
ia.getConnectionButton().setText(txt);
}
});
}
As you see, this is not effective at all. I need to somehow notify the activity whenever a relevant variable is received. I use a Class for GlobalVariables and change those static variables after listen(), however I am having troubles notifying the activity.
First of all, it is almost always bad practice to pass Activity instances around. This is a time when it's bad.
Define an interface and use a callback to let the activity know that a response has been received.
public interface ResponseReceivedListener {
void onResponseReceived(int arg1, string arg2); // <- add arguments you want to pass back
}
In your TCPServer class:
ArrayList<ResponseReceivedListener> listeners = new ArrayList<>();
// ...
public void setResponseReceivedListener(ResponseReceivedListener listener) {
if (!listeners.contains(listener) {
listeners.add(listener);
}
}
public void removeResponseReceivedListener(ResponseReceivedListener listener) {
if (listeners.contains(listener) {
listeners.remove(listener);
}
}
When you receive a response:
for (ResponseReceivedListener listener : listeners) {
listener.onResponseReceived(arg1, arg2);
}
In your Activity:
public class MainActivity extends Activity implements ResponseReceivedListener {
// ...
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
tcpServer.setResponseReceivedListener(this);
// ...
}
public void onResponseReceived(int arg1, string arg2) {
// do whatever you need to do
}
// ...
}
All from memory so please excuse typos.
This approach decouples the classes. The TCP Server has no knowledge of the activities. It simply calls back to any listeners registered. Those listeners might be Activities, they might be services. They might be instances of MySparklyUnicorn. The server neither knows nor cares. It simply says "if anyone's interested, I've received a response and here are the details".

Android - event listener

I hope this will be simple question.
I have main activity, on this activity I create an instance of some class. How to send some event form one class to main one? How to setup some kind a listener to send notifications between classes. Only option what I know/use right now is to keep reference to parent class and call directly some function from child class.
I'm wonder if it possible to create something like is in ActionScript, where I can call to dispatchEvent(new Event("name")) and later setup addEventlistener("name" function) ??
If "I implement some class" means that you have declared a nested class inside your Activity class than nested non-static class will have a reference to parent class object.
In general, you can always create dispatcher/listener pattern your self. Create listener interface and add either addListener or setListener method to class that will dispatch event.
Example of listener:
public interface IAsyncFetchListener extends EventListener {
void onComplete(String item);
void onError(Throwable error);
}
Example of event dispatcher:
public class FileDownloader {
IAsyncFetchListener fetchListener = null;
...
private void doInBackground(URL url) {
...
if (this.fetchListener != null)
this.fetchListener.onComplete(result);
}
public void setListener(IAsyncFetchListener listener) {
this.fetchListener = listener
}
}
Example of class with event listener:
public class MyClass {
public void doSomething() {
FileDownloader downloader = new FileDownloader();
downloader.setListener(new IAsyncFetchListener() {
public void onComplete(String item) {
// do something with item
}
public void onError(Throwable error) {
// report error
}
});
downloader.start();
}
}
Just implement a listener(or a list of listeners) on the class that generates the events.
When an event is generated iterate over this list and call a method that all the listeners must implement (via an interface maybe?)
Hope it helped,
JQCorreia

Categories

Resources