I have implemented a bus for my app for communication between fragments and activities. I add a subscriber by adding an instance of either a Fragment
or an Activity
to a list. and I iterate through that list invoking a method to notify each of the subscribers of what is going on. Now I need to keep the list clean, I don't want to add multiple instances of of the same class in the list. I can use equals()
for an Activity
but I cant for a Fragment
because its final so I cant override it.
WHAT I HAVE TRIED
I have tried to keep a Class
object of each subscriber in the list which works fine until I go to invoke the method. You cant invoke a method without an instance to invoke it from. So that doesnt work.
I could also keep a separate list, one to hold Class
objects and one to hold the actual instance. But I want to avoid adding another dependency if at all possible.
I could also manually do a instanceof
check for each Fragment
, but I dont want to do that because I already have 5 fragments, and if I add or remove any then I have to come back here and update this method.
So my question is, other than adding another List
to hold the Class
objects or manual instanceof
checks, are there any other ways I can make sure I dont add multiple instances to the subscribers List
?
Here is the relevant code if you need it:
public void subscribe(Object object) {
if (!mSubscribers.contains(object)) {
mSubscribers.add(object);
}
}
public void notifySubscribers(BusEvent event) throws InvocationTargetException, IllegalAccessException {
for (Object o : mSubscribers) {
Method methodToCall = getMethodToCall(o);
if (methodToCall != null) {
methodToCall.invoke(o, event);
}
}
}
Ok I have found a suitable answer to my problem. I want to share it here in hopes that it will help someone else out. Android has a class called LocalBroadcastManager
. It is available in the v4 support library. In your activity you call 'LocalBroadcastManager.getInstance().registerReceiver()'. You pass into that method a class that extends BroadcastReceiver
and an 'IntentFilter' to tell the receiver what to listen for. Then in any class including Fragment
s you call LocalBroadcastManager.getInstance().sendBroadcast()
and pass in an Intent
that matches the IntentFilter
you used when registering. Here is the code I used to get it to work:
private void registerLocalBroadcastReceiver() {
// call this method in your activity (or any class you want to listen for broadcasts)
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
manager.registerReceiver(new OpenMenuBroadcastReceiver(), new IntentFilter("open-html"));
}
private void sendMessageToActivity(int position) {
// use this in a fragment (or any other class) to send a message
LocalBroadcastManager broadcast = LocalBroadcastManager.getInstance(getActivity());
Intent message = new Intent("open-html");
String name = (String) getListAdapter().getItem(position);
message.putExtra("name", name);
broadcast.sendBroadcast(message);
}
class OpenMenuBroadcastReceiver extends BroadcastReceiver {
// this is an inner class to my activity, when you send the message this method
// will be called to handle the message
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getStringExtra("name");
if (name != null && name.equalsIgnoreCase("home")) {
replaceFragment(Tag.HOME_FRAGMENT.getTag(), new HomeFragment(), R.id.main_frame);
mDrawerLayout.closeDrawer(Gravity.START);
return;
}
openMenuItemsFragment(name);
}
}
The good thing about this is that it is completely local to your app. External apps cant receive your broadcasts so its secure. You can find out more on how to use it on the Android developer site.