Communication Patterns For Android Components
Delhi mobile development meetup
29.11.2014
Achin Kumar
@infernus321
Any android specific classes (Activities, Services, Fragments, etc) or
other java classes (Models, Helper classes, Threads) inside an application
Class A uses another class or interface B,
then A depends on B. A cannot carry out it's work without B
A is dependent
B is dependency
Dependency affects reusability, development speed, code quality, code readability.
In order to reuse a Class, how much other code needs to be reused along with it?
It's neither binary nor discrete, but linear.
That is, you cannot entirely get rid of dependencies but reduce the degree of it.
Degree of dependency among components is relatively high.
Components keep references of each other and call methods on them directly.
Inflexible, error-prone, modularization not possible, debugging can be painful.
class SongsListFragment extends Fragment {
private void onSongSelected(int trackId) {
MusicPlayerActivity mpAct =
(MusicPlayerActivity) getActivity();
mpAct.playSong(trackId);
}
}
SongsListFragment tightly coupled with MusicPlayerActivity
With above degree of coupling, SongsListFragment cannot be reused without MusicPlayerActivity
Components have very little knowledge about other significant components. Relatively low degree of dependency.
Adaptable, modular and plug-n-playable, less error-prone, easier to debug.
Our goals while laying down Communication channel are
1. Reduce degree of dependency
2. To keep code efficient
3. Less pain while development
A component implements an interface and other components interact rather with the interface.
Standard and efficient but requires lot of boiler plate code and isn't perfectly decoupled as interface still needs to be shared among nodes.
public interface TrackStateListener {
public void onTimeUpdate(long ms);
public void onTrackComplete();
}
public class MediaPlayer implements TrackStateListener {
...
public void onTimeUpdate(long ms) {
// Update progress bar
}
public void onTrackComplete() {
// Play next track and notify SongsList
}
...
}
public class MusicPlayerActivity {
...
musicPlayerService.setTrackStateListener(mediaPlayer);
...
}
Using Interface to pass message from MusicPLayerService to MediaPlayer
All of the above for reverse communication. Lot of boilerplate!!
When nodes of communication are multiple layers apart, it would involve multiple interfaces.
MediaPlayer
PlayerActivity
Interface1
ListController
Interface2
Interface3
SongsList
Pubsub communication.
Components can subscribe on MessageBus for specific action or event.
Publishers can post actions or events on MessageBus and subscribers would get notified.
Based on BroadcastReceiver.
Data is bundled in order to pass, thus involves the overhead of making custom data parcelable or serializable.
Highest degree of decoupling.
Data has to be bundled in order to pass, thus involves the overhead of making custom data parcelable or serializable.
An event can be any java class.
Events can be posted on Bus, subscribed to and notified of.
Highly decoupled, simple and less boiler plate code.
But not standard and not as efficient as Interface
class DownloadProgressEvent {
private float progress;
public DownloadProgressEvent(float progress) {
this.progress = progress;
}
public float getProgress() {
return progress;
}
}
1. Define an Event
2. Register Subscriber
3. Post an Event
4. Receive the Event
1. Define an Event
public class MyEvent { }
2. Register Subscriber
EventBus.getInstance().register(this)
3. Post an Event
EventBus.getInstance().postEvent(event)
4. Receive the Event
public void onEvent(MyEvent event) { }
Some events carry imformation that is of interest even after the event is posted.
For example, 'last known location'.
A subscriber gets notified of such events during registration and these events can be queried anytime.
Square's Otto: Annotation based, light-weight and easy to use. Guava library's EventBus optimized for Android. Provides limited threading options.
GreenRobot's EventBus: Convention based. Better performance than any other EventBus for Android.
EventBus keeps track of events and subscriber methods of active classes in map data-structures, such as following used by Otto.
private static final Map<Class<?>, Map<EventClass, Set<Method>>> SUBSCRIBERS_CACHE =
new HashMap<Class<?>, Map<EventClass, Set<Method>>>();
Whenever an Event is posted on Bus, this map is looped through and all the methods registered for the Event are called synchronously.
Mapping is Done in following two steps
1. Loop over methods of a class using reflection.
2. If it's an annotation-based EventBus, loop over annotations and fill the map. In convention based EventBus, use the convention to fill the map.
Did you spot the monster here that feeds on performance?
Reflection is a processor intensive task.
Looping over methods of a class using reflection scans through it's superclasses as well.
Class.getMethods()
There could be many things taking slice of this start up time. And ideally, an aggregate lag of ~300 ms can be sad. So saving every bit of it is worth an effort.
If multiple classes register on EventBus during startup, reflection is going to eat up ~50-100 ms
Give another option to replace reflection for subscriber discovery.
Let the developer take the pain of documenting subscribers for EventBus.
class MyActivity extends Activity {
onCreate() {
...
ArrayList<String> subscriberNames = new ArrayList<String>();
subscriberNames.add("subscriberMethod1");
subscriberNames.add("subscriberMethod2");
subscriberNames.add("subscriberMethod3");
EventBus.getInstance().registerWithSubscribers(this, subscribers);
}
}
Classes that would register on Bus using this special register method will skip reflection and make use of following method while subscribers mapping.
Class.getMethod("methodA");
As a rule of thumb, use Interfaces when components are directly in touch.
And prefer MessageBus when components are multiple layers apart or multiple components are interested in an event.
Further, use EventBus when custom data is to be passed or else prefer Implicit Intent MB.
Contact Me
@infernus321
https://github.com/Infernus666
talk.to.achin@gmail.com
www.vinsol.com
MusicPlayer Activity
ListFragments
(songs, albums)
MediaPlayer
Fragment
MusicPlayerService
A Situation
ListController
.... and many more