Android - Fragments, using reference to call instance methods - android

I have specific fragments which will perform actions in a background thread (or will start activities for result) and I would like to set a callback (from the hosting activity), to get some data from the fragment after such actions, for instance:
FragmentManager fm = getFragmentManager();
TakePhotoFragment takePhotoFragment = (TakePhotoFragment) fm.findFragmentById(R.id.frTakePhoto);
takePhotoFragment.setListener(new TakePhotoFragment.OnNewBitmapListener() {
#Override
public void onNewBitmap(Bitmap bitmap) {
// do things in the activity
}
});
I have read that it is safer to feed arguments into the fragment using Bundles, but there are several cases which I would like to pass object references rather than simple String data using Bundles. Is there any problem in getting a reference using a FragmentManager and calling methods on it? If yes, which are the workarounds?

If your fragment is not yet created use bundle, otherwise use instance one.
It is safer to pass bundle becoz when android recreate your fragment your pass data in bundle is not lost.
If you use the instance one to pass data you have to save and restore that data yourself
Note: you can pass complex object through bundle by making them parcelable or serializable.
Also passing big data in bundle is not recommended
If your fragment is defined in xml you can get the instance by findFragmentById() of fragmentManager class

Related

How to have shared data/object between fragment android kotlin

Everytime i am in need of having a shared data to use or to be updated in fragment, i always put the data in activity and create a method to get/update the data then access it in fragment through something like this
(activity as HomeActivity).updateData()
i feel like this is not the good way to do this, is there any better way to do this? having the same object to be accessed through different fragment
I am a newbie so any advice will be really appreciated, Thanks
Please try the following:
Create a singleton class
In the singleton class create empty or valued variables
Create getter/setter for those variables if required
Access the variables from other classes/activities using the singleton object of the data holder class
This a system from MVP/MVC architecture and a very good practice
Whenever you replace fragment from Activity or from anywhere you can pass data to fragment by setting data as Arguments.
Testfragment = new TestFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("key",<data to pass>);
fragment.setArguments(bundle);
replaceFragment(fragment);
Access same data in fragment by using
Object obj = getArguments().getSerializable("key")

Why use bundle to pass data to fragment?

I have a fragment that is always visible. I don't understand why I should use bundles to pass data to it from activity.
Most of the questions here recommend this method of passing data:
Bundle bundle=new Bundle();
bundle.putString("name", "From Activity");
Fragmentclass fragobj=new Fragmentclass();
fragobj.setArguments(bundle);
I prefer creating Fragment object in OnCreate function of activity and then use this object to display fragment(FragmentTransaction.add). As I have refence to this fragment I can create create function showName() in it and call it from activity like that:
myFragment.showName("name");
Is there anything wrong with this approach?
The Android documentation states:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().
That's why it's better to use a bundle and set the parameters of the Fragment this way, it's easier for the system to restore its values when the fragment is re-instantiated.
Now, I wouldn't use myFragment.showName("name"); because you don't know if the lifecycle of the fragment has already finished (attached to the activity and inflated the views), so instead, I would call the showName("name") in the onActivityCreated or onViewCreated callbacks.
Applications should generally not implement a constructor. The first place application code can run where the fragment is ready to be used is in onAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
There's nothing wrong with this approach for setting one off data values, you just need to be careful to make sure that the view that you want to set your name on actually exists at the point that the showName method is called.
Part of the reason that using Bundles to pass information is popular is that they can hold all types of data using keys and also they can easily be used to pass view states around during device rotation. Ultimately it's a matter of preference and exactly what your use case is.
When app is in background, the Fragment can recreated (eg: by change the theme (light/dark), language, ...).
So if you dont pass data use Bundle to Fragment, your Fragment will not have this data when it recreated

Can we pass param to fragment directly use set method

I know that we can pass data to Fragment using setArguments method.
But I also test that if I add a public field in the target fragment and set it from the other fragment, I can also correctly get the field value.
Then why should we use Bundle as the communication bridge?
Is there any performance issue that directly setting the fragment field?
public class SecondFragment extends Fragment {
//can we directly set this field rather than using setArguments(Bundle b)?
public List<String> strings;
}
I think one of the main reasons are that if Android recreate the fragment for various reasons outside of your code, it then calls an empty public constructor of your fragment, so that's why it's recommended to use Bundle. Or at least that's what I've been told.

Share Headless Fragment Between Activities

For some reason my understanding was that a headless Fragment lives for the duration of your application. With this understanding, in my attempt to persist an object between startActivityForResult() I put the object in a Headless Fragment like this
private HeadlessFragment modelFragment;
modelFragment = (HeadlessFragment)
getSupportFragmentManager().findFragmentByTag(Constants.HEADLESS_FRAGMENT_TAG);
if (modelFragment == null){
modelFragment = new HeadlessFragment();
}
modelFragment.setInvoice(invoice);
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
Does a Headless Fragment survive between two Activities life cycle? I did set setRetainInstance(true) on that Headless Fragment. I was hoping that I will not have to implement Parceable on my custom object.
For some reason my understanding was that a headless Fragment lives for the duration of your application.
No. Fragments are owned by activities and are not application-wide constructs.
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
There are at least two reasons for this:
First, at least in the code that you are showing, you never add the fragment to the FragmentManager via a FragmentTransaction. As such, the activity that created the fragment will not be able to find the fragment via findFragmentByTag(), because the FragmentManager does not know about it.
Second, each activity has its own FragmentManager, and fragments from one activity are not accessible in another activity.
I was hoping that I will not have to implement Parceable on my custom object.
Then don't pass the object. Pass the information (e.g., a key or ID) by which the other activity can retrieve the object (from a singleton POJO cache, by querying the database, etc.).
Or, do not make them separate activities, but have them as separate (regular) fragments in one activity.
Or, implement Serializable, though Parcelable executes more quickly.

Pass custom class to Fragment

I have a custom class called Data which contains all the data. The main activity creates two fragments. I have a field in the main activity like this:
private Data data = new Data();
The fragments are created with this method:
private ArrayList<Fragment> getFragments() {
ArrayList<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(new fragment_one());
fragments.add(new fragment_two());
return fragments;
}
I need to pass the data field to the fragments, so the fragments can acces the methods of Data.
I tried by creating a bundle, but I can't pass a custom class. What can I do?
Bundles can accept custom classes, if they implement either Parcelable or Serializable, Parcelable is faster but more work to implement and Serializable is easier to implement, but slower.
Then you can do this:
Bundle bundle = new Bundle();
bundle.putSerializable("MyData", data);
fragment_one.setArguments(bundle);
Now fragment_one will have access to data in it's onCreate(Bundle bundleHoldingData) method.
Another option is to have a public setter in your fragment that takes in data. Benefit of this is you don't have to wait till data is ready to add the fragment.
Data needs to either implement Parcelable or Serializable.
You can then either use bundle.putParcelable() or bundle.putSerializable() to pass the data to both fragments via the setArguments() method.
You should not pass references to fragments, all your data should be passed using setArguments (unless your fragment is retained). The reason is that android might destroy your fragment during configuration change, and recreate it during activity creation.
So you should either pass your data inside setArguments, or give access to it using singleton class, ie. application class.
[edit] - havent tried this myself but you can find online tools to make your data class parcelable, here is one: http://devk.it/proj/parcelabler/
You can set a static object depending how much data you keep and careful with the memory leaks. That way you can reach it within the fragments. But making the data parcelable and pass it with the bundle is always a better choice.
One option is to provide an accessor for the data on the Activity class. Then, in your fragment, you call getActivity(), cast it to the derived type, and get the data, as needed.
This of course creates a dependency from your fragment to the activity, but if it's not meant to be a generic, re-usable fragment, it would be very simple and straightforward to implement and means you can get a reference to the current Activity data and not a copy like the Bundle / Parcelable / Serializable strategy would.

Categories

Resources