I have a non-UI persistant fragment to keep track of bluetooth devices in my app (so all pairings aren't lost as soon as the user turns the phone).
I now need to access the data in this fragment from several activites and other fragments. Suffice to say it's not an option to create a new instance of the fragment for each activity, it must be the same instance in all of them. Is this allowed or even possible? What would happen with the fragments' onDetach function?
From google developers: "A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments."
Meaning it's not possible..
Related
Currently, I am using an interface to communicate from fragment to activity. I want to pass updated model back to the activity on onDestroyView to update UI but that is causing crash when activity is destroyed by system or we enable "don't keep activity" then move activity to background. This causes crash as we use this context to fetch images and get exception You cannot start a load for a destroyed activity
I know we can use shared viewModel with liveData but that would seem overkill if we have single activity and multiple fragments.
we can also use setFragmentResult with supportFragmentManager.setFragmentResultListener but using this result would be available to all fragments attached to through this fragmentManager
can we use some thing else to make communication lifecycle aware?
I have a MainActivity that just hosts the Navigation Component, all the functionality resides in other fragments and I need to use TTS in all of them.
Do I declare a single TextToSpeech instance and pass it around to each fragment (how and where?), or is it better for each of them to have its own instance? I'm already more inclined on choosing the second one but you never know.
Edit: this indecision comes from the need of managing the TTS handle according to the activity or fragment lifecycle.
If I initialize a different TTS field in onCreate() or onCreateView() for each fragment, then I would also need to call TextToSpeech#shutdown() in the fragments' onDestroyView(). It might be the easiest way but I'm concerned about the constant creation and destruction of TTS instances every time the user goes to another fragment.
The alternative would be to initialize the TTS field in my activity's onCreate() and to shut it down in the activity's onDestroy(), but then how would I pass the handle to the fragments while using the Navigation Component? By using Safe Args or a ViewModel?
According to Android Developer site, the correct way of communicating an activity with their fragment is through listeners.
https://developer.android.com/training/basics/fragments/communicating
My question is, this fragment is holding a reference to the activity... when the activity is destroyed, will the fragment manager release the fragment and thus the fragment will be collected and so the activity? or do they hold a strong reference that needs to be nullified too in the Fragment's onDestroy?
The Fragments Lifecycle is bound to the one of the Activity. Imagine an Activity as the Universe and Fragments as Planets / Stars. If the Universe dies, so do the Stars / Planets inside of it. Similarly, if an Activity gets destroyed so do all of it's Fragments.
The official documentation (which you should definitely check out) explains it very well:
A fragment must always be hosted in an activity and the fragment's
lifecycle is directly affected by the host activity's lifecycle. For
example, when the activity is paused, so are all fragments in it, and
when the activity is destroyed, so are all fragments. However, while
an activity is running (it is in the resumed lifecycle state), you can
manipulate each fragment independently, such as add or remove them.
When the activity containing the fragment is destroyed, so is the fragment automatically. Check this out
Fragment transaction has method add(Fragment fragment, String tag), which does not place fragment to container, so it cannot have view. For what it can be used?
From the Android Documentation:
However, a fragment is not required to be a part of the activity layout; you may also use a fragment without its own UI as an invisible worker for the activity.
How about this purpose ?
Simple example: an Activity starts an AsyncTask, but when device rotated activity restarts, causing AsyncTask to lose connection with the UI Thread. But this Activity can hold a Fragment (invisible, with no UI at all) that can handle all the AsyncTask work. When Activity recreated the Android OS takes care reattaching the Fragment, thus no data loss will occur.
For Dialogs you don't have any container on normal app layer. It is directly added on Window with WindowManager(See WindowManager.LayoutParams for various types of layers).
DialogFragment has an API like DialogFragment.html#show(android.app.FragmentManager, java.lang.String) which corresponds to this.
You can use fragments without UI (container) as a background worker (one benefit is that you can retain it during rotations etc) and for retaining data during rotations and other changes.
Reading http://developer.android.com/guide/components/fragments.html is strongly recommended.
Example of instance retaining: https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.java
Also, here are similar questions (so this questions seems to be a duplicated but cannot be flagged due to bounty):
What is the use case for a Fragment with no UI?
Android non-UI Fragment usage
As #Lucius Hipan mentions, it can be used to prevent data loss.
Almost always this king of fragments are used as Retained container ( setRetainInstance(true) called in onCreate method), then after device configuration changes (e.g. orientation changing) fragment will not be recreated but remembers previous state.
It's recommended way to use asynctask.
Here is an example:
There is login activity. The user enters their credentials and presses the Login button. After that configuration change occurs (user rotates phone). So, network task was completed, but your handlers was not listening for it now. If you show any login animation, it can be stored via savedInstance, but listeners not. And instead of creating service you can simply create new retained fragment with persistant asynctask and interface to communicate with activity.
This method is a good compromise for small projects where using bus libraries is overstatement.
By calling the method add(Fragment fragment, String tag) internally calls add(int containerId, Fragment fragment, String tag) with a 0 containerId.That will be add(0, fragment, tag).
If 0 is supplied as containerId, it will not be placed the fragment in a container.
When starting a new application for a client, I am asking myself again the same question about who should be responsible for loading data: activities or fragments. I have taken both options for various apps and I was wondering which pattern is best according to you in terms of:
limiting the code complexity.
handling edge cases (like screen rotation, screen going power save, loss of connectivity, etc.)
Option 1 - Activity loads data & fragment only displays it
This allows to have fragments that are just fed a bunch of objects to display. They know nothing about loading data and how we load that.
On the other side, the activity loads data using whichever method is required (for instance initially the latest 50 entries and on a search, loads the search result). It then passes it to the fragment which displays it. Method to load the data could be anything (from service, from DB, ... fragments only know about POJOs)
It's kind of a MVC architecture where the activity is the controller and fragments are the view.
Option 2 - Activity arranges fragments & fragments are responsible to fetch the data
In this pattern, fragments are autonomous pieces of application. They know how to load the data they are displaying and how to show it to the user.
Activities are simply a way to arrange fragments on screen and to coordinate transitions between application activities.
In theory you can do whatever you want, if it works.
Actually, the fragments and activities display data and deal with their own life cycles.
Since fragments belongs to activity so you have to use both in conjunction to better handle all the data but mostly it depends on your needs.
If you keep in mind the idea that the Fragment should provide the UI and the Activity should provide the processing then you have a good division of concerns and code which should allow the Fragment or the Activity to be reused.
If you know about the MVC - Model View Controller - design pattern then you can think of the Fragment as the View and the Activity as the Model.
Things get much more interesting when you build an application with multiple Fragments.
Some key points as a decide factor -
The idea of a Fragment is that it is a wrapped up chunk of UI that
can be used by any Activity that needs it. On this basis you have to
ask yourself if the event that has to be handled is the same for
every Activity or unique to each Activity. If it is the same then the
event handler is better written within the Fragment.
The Fragment doesn't have a UI of its own - it is displayed by an
Activity that the Fragment is associated with. The events are
generated by objects in the View hierarchy, which is owned by the
Activity. If you try to use Android Studio to add an event handler,
for example, it will add it to the Activity and not to the Fragment.
You can define the EventListener that you want to handle the event
in the Fragment and then hook it up to the View object in the
Activity in which you want to generate the event.
A Fragment is a class that implements the onCreateView method to
supply a View hierarchy that can be displayed by an Activity.
To use a Fragment in an Activity you have to add it using a
FragmentManager and a FragmentTransaction. You can add the Fragment
using the add method but nothing happens until you call the commit
method.
After the method that used the commit, usually the Activity's
onCreate, terminates the CreateView event runs the Fragment's
onCreateView and the Fragments View hierarchy is added to the
Activity's content.
You have to write code to save and restore any additional state the
Fragment may have.
If a task is common to all instances of the Fragment then its code
should live in the Fragment.
In particular the code to handle events can be defined within the
Fragment.
The Activity should be used to host code that processes the data
provided by the UI.
Attaching Activity event handlers to the Fragment's UI or is
difficult to do correctly.
From scenarios make decision what your app will be. Is it service,
activity, widget , even a content provider or a complex system,
including some different components. Test your decision against
scenarios.
All of these have to work after the Fragment has been destroyed and
recreated.
(1) Initialization of the Fragment, (2) Saving and restoring the Fragment's
state and (3) Implementing something like an event mechanism so the Fragment
can get the Activity's attention
The hardest part is implementing something like an event mechanism.
In the case of the complex system, distribute functionalities and
data entities among application components. Make a list of components
and what they are (activities or smth else).
Make the list of UI components with description what they do (not HOW
yet) These will be widgets and activities or fragments or layouts
later.
Often you will want one Fragment to communicate with another, for example
to change the content based on a user event. All Fragment-to-Fragment
communication is done through the associated Activity. Two Fragments
should never communicate directly.
When your app is perfectly modular, fragments don't know about each
other. You can add a fragment, remove a fragment, replace a fragment,
and they should all work fine, because they are all independent, and
the activity has full control over the configuration.
You can't do anything with a Fragment unless you start a transaction.
Within the transaction you can set up what you want to happen,
usually add the Fragment to the current layout, but nothing happens
until you use the commit method.
Efficient handling of data with Screen Orientation -
When screen orientation changes, Android restarts the running Activity (onDestroy() is called, followed by onCreate()).
To properly handle a restart, it is important that your activity restores its previous state through the normal Activity lifecycle, in which Android calls onSaveInstanceState() before it destroys your activity so that you can save data about the application state. You can then restore the state during onCreate() or onRestoreInstanceState().
However, you might encounter a situation in which restarting your application and restoring significant amounts of data can be costly and create a poor user experience. In such a situation, you have two other options:
1) Retain an object during a configuration change
Allow your activity to restart when a configuration changes, but carry a stateful Object to the new instance of your activity.
2) Handle the configuration change yourself
Prevent the system from restarting your activity during certain configuration changes, but receive a callback when the configurations do change, so that you can manually update your activity as necessary.
What I would do is manage all data flow (bluetooth, database storage, etc)
in the Activity and use Fragments only for UI display or handling user input.
This way is easier to handle configuration changes/ screen rotations.
Also, if data flow things are heavy to be on UI thread, consider using a Service with a background thread.
If it is a "one shot" thing, you can use an IntentService,
otherwise you can implement a Bind Service and request a bind from anywhere you have Context.
For more read - fragment-and-activity-working-together.
Ideally neither Activity nor Fragment with UI should contain any "model" logic - these classes should be lightweight and responsible only for UI logic. But when you decide to make a separate model object you have a dilemma to choose where to initialise and store this object and how to deal with configuration changes. And here comes some handy trick:
You can create a model Fragment without UI, make it retain instance to deal with configuration changes (it's AFAIK the simplest way to save data across config. changes without troubles) and retrieve it anywhere you need via findFragmentById(). You make all expensive operations inside it once (using background thread, of course), store your data and you're done.
For more info, see Adding a fragment without a UI section.
UPD: There's now a better way to deal with configuration changes: ViewModel from Google's Architecture Components. Here's a good example.
I prefer and always implemented Option-2 over Option-1.
Reasons for not selecting Option-1:
We should have listeners for events triggered in Fragments and pass it back to activity to load data, process it and push it back to fragment, which makes work more complex.
An Activty can load any number of Fragments, Typically you end up questioning these questions to yourself in a scenario where your app is highly scalable and is already huge. Writing all the events in an activity and passing it over to fragment will be an complex altogether.
As #Ved Prakash mentioned, Handling screen orientation becomes complex if orientation is handled by Activty.
I have an example:
your application have 2 features A and B. the 2 features are independent each other. and each feature has a lot of screen.
you should create 2 activities A and B because when Activity A is used, Activity B should be released to reduce memory of app. And the same when B is used, A should be released. The memory of Context A and B are independent, if you want to send data from Activity A to B you must use intent or use global variable in Application Context. intent is managed by OS, not by application. When A send intent to B, if A is destroy is no problem with intent send to B. Activity is App module, it is can call by other applications (fragment is impossible).
for example: feature A has a lot of screen (ex: Fragment1, Fragment2). they use the same data and depend on each other. each screen should be a fragment. and when process with data you can get reference to data by calling function getActivity() and then get reference to variable of Activity context (or Activity memory) to write or read it. Fragment1 and Fragment2 are belong to Activity A Context.it means that Fragment 1 and Fragment 2 can transfer data with each other via variable of Activity context, it is easy to do . noticed that Intent is managed by OS,it is so expensive when send data via Intent.