I'd like my activity to send the same event to multiple fragment. Instead of making my activity calling each individual fragment method : FragmentA.DoTask(), FragmentB.DoTask(), FragmentC.DoTask(), etc... I'd like rather make my activity send only one event and then make the fragment listening to this event.
On the developpers docs they make the activity "listen" to the fragment but then the activity calls the fragments' methods. Is it possible the other way around : to make the fragments "listen" to the activity.
Thanks
You could Use EventBus system.
Description on RRiBbit (though this library not related to Android) define it precisely:
An Eventbus is a mechanism that allows different components to
communicate with each other without knowing about each other. A
component can send an Event to the Eventbus without knowing who will
pick it up or how many others will pick it up. Components can also
listen to Events on an Eventbus, without knowing who sent the Events.
That way, components can communicate without depending on each other.
Also, it is very easy to substitute a component. As long as the new
component understands the Events that are being sent and received, the
other components will never know.
So what exactly is a component here? Well, a component could be
anything. In most Eventbuses, they are Java Objects. They send Events
and they also listen to Events.
In Android you can use EventBus.
I don't think there is a way around it. The word "listen" in the docs is more metaphorical. You have to call all fragments explicitly.
The closet thing you can get, is to have a list of Fragment maintained in your Activity class. e.g.: create a customized Fragment class:
public class MyFragment extends Fragment{
public abstract void doTask();
}
Have all your Fragments inherited from this class.
public class FragmentA extends MyFragment{
#Override
public void doTask(){
//exec code here
}
}
In your Activity class, each time you create a Fragment, add it to a List too. When the event occurs, call all Fragments.
public class MyActivity extends Activity{
List<myFragment> mFragmentList = new ArrayList<MyFragment>();
public void addFragment(MyFragment fragment){
mFragmentList.add(fragment);
}
public void onEvent(){
for(MyFragment fragment:mFragmentList){
fragment.doTask();
}
}
}
Note: If you don't have that many Fragments, this solution can be a overkill.
Related
In my app, I have to call an activity method from the fragment.
I know I can do this in two ways:
1. Via sending a broadcast to activity:
Intent intent = new Intent("FILTER");
intent.putExtra("EXTRA", 1);
sendBroadcast(intent);
2. Or Calling the activity method directly:
((MyActivity) getActivity()).method();
I would like to know which way is faster and safe to communicate. Any help would be appreciated.
Thank you.
Loosely Coupled Fragment?
I am not sure about the speed. But on the design perspective You should use an interface to communicate with an Activity rather calling Activity method directly from your Fragment. ie ((MyActivity) getActivity()).method();
Because using an interface makes your Fragment independent from your
Activity. Let's say in future you want to use your fragment in Some
other Activity then you will not have to change anything in your
Fragment.
Interface
public interface Somelistener {
public void someMethod();
}
Your Loosely coupled Fragment
YourFragment extends Fragment {
Somelistener listener;
public void onActivityCreated(Context context){
listener = (SomeLisner)context;
}
public void buttonClick()
{
listener.someMethod();
}
}
So if you are using in your MainActivity. No problem
MainActivity implements SomeListener{
#Override
public void someMethod()
{
// Activity method
}
}
In future you want to use Fragment in SomeOtherActivity. No problem
SomeOtherActivity implements SomeListener{
#Override
public void someMethod()
{
// somethother method
}
}
BroadcastReceiver Approach?
TBH I have seen this approach for Service-Activity Communication. Not for Activity - Fragment communication.
For communicating between Fragments and the Activity that contains it, there's actually a much better 3rd option.
The better option is to use an Interface as a callback method. This is described very well in this documentation: https://developer.android.com/training/basics/fragments/communicating
Using an interface is much more preferred over your two methods because it's both safer and more efficient.
For your first method of using Broadcast Receivers, this is actually a very inefficient solution due to Broadcast Receivers not being meant for a task like what you're after.
Let me quote you something from the Android documentation:
Warning: Limit how many broadcast receivers you set in your app. Having too many broadcast receivers can affect your app's performance and the battery life of users' devices. For more information about APIs you can use instead of the BroadcastReceiver class for scheduling background work, see Background Optimizations.
https://developer.android.com/guide/topics/manifest/receiver-element
So yes, Broadcast Receivers will have a bigger effect on your app's performance and the device's battery life over the other method you suggested and the method I suggested.
Additionally, don't forget that a Broadcast Receiver is meant to listen to broadcasts. The type of Broadcast Receiver you're using in your example is actually a Global Broadcast where you didn't explicitly limit it's "range", so any Broadcast Receiver could potentially "listen" in to your broadcast. In terms of security, using a Global Broadcast like this isn't safe either.
You also don't want other apps to potentially fire off a Broadcast that coincidentally coincides with your app's Broadcast Receiver, causing it to receive data not meant for it and crashing due to this accidental and coincidental naming.
Honestly, there's more potential issues of using a Broadcast Receiver in a way it's not meant for.
As for your second method of directly calling the Activity's method... this is actually very inefficient for managing code. You're basically tying the Fragment tightly together with that specific Activity.
However, Fragments, by design, makes it common to be swapped into other Activities or Fragments... you'll basically have to do multiple if statements and casts each time you want to run code from it's parent.
Also, keep in mind that if you later change code in MyActivity, it can cause problems for this fragment due to you forgetting how tightly bound it is to the Activity.
But if you use the more preferred Callback Interface approach, it's simply a middleman meant to deliver a "Hey, DO something for me" message. Quick and direct. It's also plays friendly with any Activity or Fragment you want to attach this Fragment to later since those Activities or Fragments simply have to implement the Interface and the callback bridge between both parent and child is formed.
It is better to use interface to communicate from fragment to activity rather than a Local broadcast.
Activity will implement the interface and fragment will call the methods.
I have a question about "proper programming" in Android.
I am currently developing an app using fragments. It involves dynamically added fragments to Activity, fragments inflated from XML, nested fragments from XML or dynamically added. Let's just say, a bit of everything.
The concept this question focuses on is the communication process involved with fragments. So, I've read the docs and this is not the first time I try to use fragments.
The common sense (and docs) tell that if a Fragment wants to speak or communicate with it's activity, we should use an interface.
Example:
TestFragment
public class TestFragment extends Fragment {
private TestFragmentInterface listener;
public interface TestFragmentInterface {
void actionMethod();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getActivity() instanceof TestFragmentInterface) {
listener = (TestFragmentInterface) getActivity();
}
// sending the event
if (listener != null) listener.actionMethod();
}
}
TestActivity
public class Test implements TestFragmentInterface {
#Override
public void actionMethod() {
..
}
}
Everything fine here.
This improves re-usability, as my TestFragment this way can interact with any kind of Activity, given the Activity implements the interface I declare.
The other way around, the Activity can interact with the fragment by holding a reference and calling its public methods. This is also the suggested way to fragment-to-fragment communication, using the Activity as a bridge.
This is cool, but sometimes it feels like using an interface for this is just a bit "too much".
Question A
In the scenario the fragments I attach have a pretty focused role, meaning they are done for that particular activity and would not be used otherwise, is it conceptually wrong to ignore the interface implementation and just do something like
((TestActivity) getActivity().myCustomMethod();
?
This also goes to the scenario where (not my case, but just taking it as a "at its worst") my activity has to deal with a wide variety of these DIFFERENT fragments, meaning it should implement one method for every fragment it should handle. This brings the code to a big mess of "potentially not-needed lines".
Moving further on: still with the use of "focused" fragments, aimed to work only under certain way, what is with the use of nested fragments?
Added them like
public class TestFragment extends Fragment {
private void myTestMethod() {
NestedFragment nested = new NestedFragment();
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, nested)
.commit();
}
}
this binds NestedFragment to TestFragment. I say it again, NestedFragment, just like TestFragment, is to be used only in this way, it has no meaning to work otherwise.
Back to the question, how should I behave in this situation?
Question B
1) should I provide an interface in NestedFragment, and make so that TestFragments implements NestedFragmentInterface? In this case I would act as following
NestedFragment
public class NestedFragment extends Fragment {
private NestedFragmentInterface listener;
public interface NestedFragmentInterface {
void actionMethodNested();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getParentFragment() instanceof NestedFragmentInterface) {
listener = (NestedFragmentInterface) getParentFragment();
}
// sending the event
if (listener != null) listener.actionMethodNested();
}
}
2) should (or could) I ignore interface, and just call
getParentFragment().publicParentMethod();
?
3) should I create the interface in NestedFragment, but let the activity implements it, so that the activity will call TestFragment ?
Question C
Regarding the idea of using the Activity as a bridge between fragments, I believe it is made so for proper handling lifecycle of all these objects. Is it still viable to do a direct fragment-to-fragment (using interface or directly call public methods) while trying to handle manually the exception the system might throw me?
Ill do my best to answer the wall of text here :)
Question A:
Fragments are designed to be reusable modules that can be plug and played with any activity. Because of this the only correct way way to interface with the activity is to have the activity inherit from a interface that the fragment understands.
public class MapFragment extends Fragment {
private MapFragmentInterface listener;
public interface MapFragmentInterface {
//All methods to interface with an activity
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// sending the event
if (listener != null) listener.anyMethodInTheAboveInterface();
}
}
Then have the activity implement the interface
public class MainActivity extends Activity implement MapFragmentInterface{
//All methods need to be implemented here
}
This allows your fragment to be used with any activity as long as the activity implements this interface. The reason why you need this interface is because the fragment can be used with any activity. Calling a method like
((TestActivity) getActivity().myCustomMethod();
relies on the fact that your fragment only can work within a test activity and therefore "breaks" the rules of fragments.
Question B and C:
Assuming that you are following the correct guidelines for fragments and that they are independent modules. Then you should never have a situation where fragments need to know about each other. 99% of the time that people think they need fragments to directly communicate they can re-factor their problem to the situation I gave above by using a MVC patten or something similar. Have the activity act like the controller and tell the fragments when they need to update and then create a separate data store.
I will try to clear it all a little bit.
First of all, consider you approach of setting listener for fragment. It is no good to set listener in onViewCreated method, because it leeds to excess reseting listener any fragment is created. It is enough to set it up into onAttach method.
I told about code lines. Take me to notice, it is good to have BaseFragment implemented common behavior in you app as setting FragmentListener creating view from resource.
And more than that to reduce code lines and to get part of code reuse you can use generic in BaseFragment. So look at next code snippet:
public abstract BaseFragment<T extends BaseFragmentListener> extends Fragment {
T mListener;
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Activity instanceof T)
mListener = (T) activity;
}
abstract int getLayoutResourceId();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(getLayoutResourceId(), null);
// you can use some view injected tools here, mb ButterKnife
return layout;
}
}
Answer A (For Question A):
If you have fragment only exactly one activity you need to decide: "Do you really need to use Fragment here?". But mb it is good to have fragment exactly for one activity to extract some view logic from activity and clearing base logic. But for clearing base architecture logic for your app use Listeners. This will make life easier for other developers
Answer B:
For nested fragments you need to solve, what they need to use exact activity or just fragments and use it as a bridge to other system. If you know that nested fragment will be nested all time you, you need to declare parent fragment as listener otherwise you have to use other approach.
Notice:
As the base approach to communicate between diff part of App you can use events, try too look at event bus for example. It gives you common approach for communication and you can extract logic of calling custom methods of listeners and more another, all logic will be situated into handling events and you will have one mediator system for cooperation.
Answer C:
I partially explain one of approaches to cooperating between fragments. Using one event dispatcher avoids you to have many listeners for all different communication. Some times it is very profitably.
Or I think it is more usable to use Activity, or other class lives in Activity, for a mediator for Fragments cooperation because there is many situation of Fragments changing during lifecycle both handling and system. And it focuses all this logic in one place and makes your code more clear.
Hope my considerations helps you.
I have a ViewPager of 3 fragments. All 3 fragments are of the same type, with identical layouts, but they are to hold different (text) information. I am trying to create my activity, where I create the fragments and prepare the text data that I will populate my fragments with. However, I can't seem to work with the fragments from within my activity. All the activity's lifecycle methods are executed before the fragment lifecycle methods. So if I try to update a textview in a fragment from within my activity, it won't work, because the textview is null in the fragment.
I'm going to need to make periodic updates to the fragments, so passing the data as a bundle is not an option. Plus, since I'm passing lots of text, I'm using a StringBuilder object, which is not something I can pass in a bundle (unless I make it Parcelable, which I don't want)
I think I could run a method from within my fragment class that would execute during fragment creation, but that means all 3 fragments would run this method. That's not really the level of control I'm looking for.
Is there a neat way to make this work?
Thanks
Keep references to your fragments, and let them all implement an interface with a common update-method. As an example, let's make it super clear and call the interface Updatable with one method called 'update':
public interface Updatable {
public void update(String text);
}
Now, in your Activity's onCreate, save references to your Updatables there (i.e. when you lookup or instantiate your Fragments).
It should now be trivial to update your Fragments when necessary from the Activity. Needless to say, the fragment implementation of the update code needs to do the actual update of the TextView(s).
If the update implementation is exactly the same for all your Fragments, your could save some lines of code and make a base class which implements Updatable and extends Fragment.
you might able to populating fragment fields during onActivityCreated(Bundle savedInstanceState).
Refer to this site for more information about fragments' life cycle:
http://developer.android.com/guide/components/fragments.html
You should consider using Observer pattern... there is a really great implementation which you can include as gradle dependency called EventBus:
https://github.com/greenrobot/EventBus
You can use Otto Bus to send data to your fragments from your activity.
http://square.github.io/otto/
Create a new bus in you application class
Bus bus = new Bus();
Create an event that contains your data which you'll pass to fragments.
bus.post(new MyDataEvent(data));
Register your fragment in your fragment's onResume() (Do not forget to unregister in your fragment's onPause())
bus.register(this);
And get data with subscribe in your fragment
#Subscribe
public void onDataReceived(MyDataEvent event) {
// TODO Do what ever you want
}
I hope this'll help you.
I have a fragment which extends DialogFragment and which need to notify other objects when its content changes.
The android fragment guide, state that the best way to do this is to require that the containing activity implements an specified interface and then cast the activity to that interface and call the method.
This does work, but it has the limitation that only the containing activity can listen for changes. What do I do if I want to be able to notify arbitrary objects?
I can't just take make a method which take a listener as argument, because the fragment may be re-constructed by Android behind my back. And I can't put the listener in a bundle, because I have no way of knowing if the class implement the listener can be persisted/serialized and I really don't all listener objects to be duplicated.
My fragment is used both in a popup, and as a normal fragment which is shown in the main view
You should try the EventBus library. Basically you can register to an event from anywhere and send events from anywhere. Events are plain Pojos which can contain any variables or other objects.
This makes the whole communication between Activities, Fragments and so on so much easier.
An alternate event bus solution is to use Square's Otto
Or you can try the LocalBroadcastManager
Make and interface, which suports callbacks, for example:
interface IListener{
public void onEventRaised(object arguments);
}
Then make a static class which has a List of listeners and calls them when you raise the event, for example:
public static class AppListenerDispatcher{
private static List<IListener> listeners; //make add and remove methods
public static void raiseMyEvent(object arguments){
for (IListener listener: listeners)
listener.onEventRaised(arguments);
}
}
And now you just need to implement this interface in the coresponding class that will listen for this event and add it to the list. Then you can call the AppListenerDispatcher.raiseMyEvent(arguments) anywhere and all the listeners in the list will do their corresponding work.
The Android documentation suggests that to communicate from an activity to a hosted fragment, the fragment can define a callback interface and require that the host activity implement it. The basic pattern involves implementing onAttach in your fragment, and casting the activity to a callback inteface. See http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity.
Here's an example of providing a fragment some initialization data, as well as listening for a navigation callback.
public class HostActivity extends Activity implements FragmentHost {
#Override
UiModel getUiModel() {
return mUiModel;
}
#Override
FragmentNavListener getNavListener() {
return mNavListener;
}
...
}
public class HostedFragment extends Fragment {
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentHost) {
FragmentHost host = (FragmentHost) activity;
setUiModel(host.getUiModel());
setNavListener(host.getFragmentNavListener());
}
}
...
}
Compare this to using onAttachFragment in the host activity to explicitly initialize the fragment:
public class HostActivity extends Activity {
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment instanceof HostedFragment) {
HostedFragment hostedFragment = ((HostFragment) fragment);
hostedFragment.setUiModel(mUiModel);
hostedFragment.setNavListener(mNavListener);
}
}
...
}
To me, it seems like the first pattern has some drawbacks:
It makes the fragment harder to use from different activities, since
since all of those activities must implement the required interface. I can imagine cases where a given fragment instance doesn't require being fully configured by the host activity, yet all potential host activities would need to implement the host interface.
It makes the code slightly harder to follow for someone unfamiliar with the pattern being used. Initializing the fragment in onFragmentAttached seems easier to follow, since the initialization code lives in the same class that creates the fragment.
Unit testing using a library like Robolectric becomes harder, since when calling onAttach, you must now implement FragmentHost rather than just calling onAttach(new Activity().
For those of you who've done activity to fragment communication, what pattern do you find preferable, and why? Are there drawbacks to using onAttachFragment from the host activity?
I cant speak personally with respect to testing but there is alternatives to fragment / activity callback interface communication.
For example you can use a event bus to decouple the fragments and your activity. An excellent event bus can be found here:
Otto - An event Bus by Square
It is actively being developed by some very talented engineers at Square.
You can also use the LocalBroadcastManager that is packaged in the Android Support Library.
LocalBroadcastManager
Eric Burke from square has a presentation where he mentions both which can be found here:
Android App Anatomy
Update : As per the latest guidelines we are supposed to use a sharedViewModel class to communicate between fragments and activities.
However if you don't use viewModels and use callbacks, you should still consider removing onAttachFragment callback as it is deprecated now.
Instead on onAttachFragment, the suggested way is to add a listener to your fragmentManager who is associated with that transaction using addFragmentOnAttachListener.
See the example below:
childFragmentManager.addFragmentOnAttachListener { _, fragment ->
if (fragment is RetryDialogFragment) {
//Do your work here.
}
}
You should also be mindful of where you add this listener. You most probably want to add this listener before you execute the fragment transaction and should make sure that you are not adding the listener more than once.
I have used the Fragment.onAttach(...) pattern in my last project. I see two advantages:
you can check early that the hosting activity implements the required interface and throw an exception if not
there's less risk of holding onto a reference of the hosting context after the fragment has been detached
In order to take advantage of 2., you must not store references to UiModel and NavListener, as you do in your code sample. Instead, whenever you want to interact with these instances, you should use code like ((FragmentHost) getActivity).getNavListener().onNav(this), or alternatively ((FragmentHost) getActivity).onNav(this). You could store the fragment host in a field which you set to null in onDetach(...) as a middle ground approach, if you want to avoid the constant casting.
I agree that initializing a fragment from the activity that creates it seems more intuitive.
Having said all that, I'm going to skip fragments altogether in my current project. The following post reflects the lessons learned from my last one pretty well: https://corner.squareup.com/2014/10/advocating-against-android-fragments.html