Question

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);
        }
    }
}
Was it helpful?

Solution

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 Fragments 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top