Reputation: 2706
I'm designing SDK for Android. As a web developer, I'm very used to and comfortable with callbacks, and as the SDK will include many async operations, I'm not sure what is the most common or "best" way to implement such a behavior on Android (or Java in general).
I've come up with a couple of options:
1) Listener interface - the developer that will use the SDK will implement a listener interface that will include all the callbacks, for example:
interface ISDKListener {
public void onA();
public void onB();
}
class SDK {
private ISDKListener _listener;
public SDK(ISDKListener listener) {
_listener = listener
}
public void a() {
// Do stuff
_listener.onA();
}
public void b() {
// Do stuff
_listener.onB();
}
}
As a web developer, using JS that looks a bit too much for me, "forcing" the user (developer) to implement all the listeners in advance, when he might no even use all of them.
2) Single listeners setters Basically set a listener to each async method. For example:
interface ISDKCallback {
public void onComplete();
}
class SDK {
private ISDKCallback _aCb;
private ISDKCallback _bCb;
public void setAListener(ISDKCallback aCb) {
_aCb = aCb
}
public void a() {
// Do stuff
if (_aCb != null) _aCb.onComplete();
}
public void setBListener(ISDKCallback bCb) {
_bCb = bCb
}
public void b() {
// Do stuff
if (_bCb != null) _bCb.onComplete();
}
}
3) Same as #2, but separate success and errors:
interface ISDKCallback {
public void onSuccess();
public void onError(Exception e);
}
class SDK {
private ISDKCallback _aCb;
public void setAListener(ISDKCallback aCb) {
_aCb = aCb
}
public void a() {
try {
// Do stuff
if (_aCb != null) _aCb.onSuccess();
} catch (Exception e) {
if (_aCb != null) _aCb.onError(e);
}
}
}
4) Combining #1 and #3 - a complete listener with all the callbacks, but each callback will be 2 callbacks, one for success and one for errors:
interface ISDKListener {
public void onA();
public void onAError(Exception e);
public void onB();
public void onBError(Exception e);
}
class SDK {
private ISDKListener _listener;
public SDK(ISDKListener listener) {
_listener = listener
}
public void a() {
try {
// Do stuff
_listener.onA();
} catch (Exception e) {
_listener.onAError(e);
}
}
public void b() {
try {
// Do stuff
_listener.onB();
} catch (Exception e) {
_listener.onBError(e);
}
}
}
The 3rd one seems most "natural" for me, due to the separation between success and error (like the promise then
and catch
on JS) and setting each callback separately. Actually the most natural to me was to pass the callback when I call the method, but I did not found such implementation anywhere in Java as far as I've searched.
Which one is the most common and will be the most "natural" to most Android/Java developers? Are there any other suggestions for implementing callbacks in that platform?
EDIT:
To clarify, the callbacks are either for HTTP responses to HTTP requests or BLE communication, for example method a
will send some request over BLE to a BLE peripheral, and the callback for a
will be called when the peripheral returned a response (the mobile and peripheral are implementing a client-server protocol over BLE)
Upvotes: 2
Views: 902
Reputation: 204
In general, callbacks or listeners interfaces are a valid approach, but I would choose Android LiveData. It's an observational data holder which wraps your data and let others listen to your changes. In your case, I would expose LiveData with some kind of model, and the users of your sdk would then observe your return value of type LiveData for future changes in the data. Thus the users of your sdk won't have to implement nothing.
I've just wrote a blog post where I go over callbacks (as well as event bus and LiveData), describing the scenarios in which we should use one over another and the pros and cons of using one rather than the other. I think it may be useful to you:
Upvotes: 0
Reputation: 594
I'm not the biggest expert out there but if you're asking which is the most common implementation I would say numer 1. You can take a look at a lot of libraries out there, I used a lot of them myself and this is what I found to be the most used solution.
One good example would be the usage of ExoPlayer (I'm choosing it just because I'm working on it at the moment).
As you can see the activity includes an instance of the player + all the objects it needs like the BandwidthMeter
and implements ExoPlayer.EventListener
inheriting all the callbacks like onPlayerStateChanged
.
Even the Android API itself makes use of this pattern, maybe too much. But this is another topic I guess. A lot of people finds this approach a bit confusing because you end up with a callback hell and I'm with them.
Edit
Another good example of a different approach can be found in the Google API Client (which suits your situation better).
As you can see you connect to the Client with two listener, and you have another optional listener for errors with a different interface and an additional callback.
Conclusion
I guess in the end it really depends on you: solution 1 and 2 both look good to me. Number 3 will work too but I'm not too familiar with it, maybe this is a sign that it's not a widely used pattern in Android Development.
Upvotes: 1