I am trying to find a solution in how to define Hilt in a certain fragment related scenario. I have the following setup:
Activity
Parent Fragment 1
Child Fragment 1
Child Fragment 2
...
Child Fragment n-1
Parent Fragment 2
Child Fragment 1
Child Fragment 2
...
Child Fragment n-1
Parent Fragment 1 is using the dependency A. The instance of that dependency is something I want to share only between that parent fragment and all of its child fragments. The parent fragment 2 + its child fragments should use a different instance than the parent fragment 1 + children. Generally his structure should have only two instances of any given dependency - one for the first flow and one for the second.
I can see that a custom scope might work here but I am uncertain about how to use that in regards to Hilt.
You can do this using dagger-hilt by adding #AndroidEntryPoint in each parent Fragment and their child's view. Then your Parent Fragment 1 and Parent Fragment 2 will have different fragment scope with different instances as per the dagger-hilt explanation.
See below note from dagger-hilt:
A common misconception is that all fragment instances will share the
same instance of a binding scoped with #FragmentScoped. However, this
is not true. Each fragment instance gets a new instance of the
fragment component, and thus a new instance of all its scoped
bindings.
Also, see below dagger graph and the scopes hierarchy:
More details in their documentation.
Also, Don't use custom scopes until it's really necessary since it has performance and overhead issues.
Related
I tried to link the activity with the fragments but I had problems when making the observers.
(labels are completed )
My goal is to be able to have all fragments linked without the need to create a fragment for the activity_main.
Example
I have an app with a RootFragment containing a ViewPager2, presenting multiple ChildFragment.
The RootFragment has a RootViewModel, and each ChildFragment also has a ChildViewModel.
Running the app, I can see that the root fragment's view model is being created once and preserved through rotations etc - this is exactly what I expected to happen.
However, when I scroll through the child fragments and back again it looks like the ChildVieWModels are being recreated each time the ViewPager2 destroys/recreates the fragments to display. This isn't what I need at all!
How can I get the view models of the child fragment to remain even though the child fragments themselves are being destroyed and recreated by the ViewPager2?
My child fragment is requesting a view model like this:
private val viewModel: ChildViewModel by viewModels()
I have also tried scoping the child view model to the parent fragment but that just means each child fragment gets the same instance of ChildViewModel, which isn't quite right either:
private val viewModel: ChildViewModel by viewModels({ requireParentFragment() })
I've looked at the samples for ViewPager2 but none of them seem to have a view model in their child fragments.
What have I misunderstood here?
How can I get the view models of the child fragment to remain even though the child fragments themselves are being destroyed and recreated by the ViewPager2?
You could set the offscreen page limit. See doc https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2#setOffscreenPageLimit(int)
If you set the limit to 2, then the fragments that will be kept alive are the current tab, the next 2 tabs and the previous 2 tabs in the viewpager. You can use this to keep all fragments and viewmodels in the viewpager alive if the number of tabs/childfragments are static and small, for example 5 or less.
If the child fragments have some shared state among them or some expensive queries in child fragments, then you can put that in the RootViewModel and use that in your child fragments by doing requireParentFragment as you have pointed out.
There really isn't a better way than to set the offscreen limit or sharing the viewmodel with the parent fragment.
I am trying to do some UI testing in isolation on some fragments that uses a shared Bottom navigation view from MainActivity, basically used to navigate and to scroll up and down, but my tests fails with nullpointerException basically because the buttons inside the bottom navigation view are not found in the fragment layout.
My question is how to pass these layouts belonging to mainactivity to the fragmentTest class.
FragmentScenario is the wrong approach, because a Fragment should not even depend on the parent Activity (and if it does, always check with instanceof or is, which one Activity it actually had been attached to). Better instrument the parent Activity with an ActivityTestRule instead, because a FragmentScenario uses it's own mock Activity and so you'll never get a handle to the expected one parent Activity (that's intentional, in order to rule out rigid dependencies to the parent Activity). Just set a break-point inside your current test's code, in order to see that there is no BottomNavigationView present, because it hasn't been inflated.
There's also a new ActivityScenario (which is currently still in beta stage).
I'm refactoring an android component to be adopting MVP architectural pattern. The problem I faced is that I have a fragment nesting other fragments in it.
1- The parent fragment hides/shows one of the nested fragments based on some conditions.
2- The child fragment passes data to the parent fragment which is observing it as here inspired by callback mechanism between fragment and activity.
I've 2 questions:
1- If I consider the fragment as the view of MVP, should I use distinct presenters for the parent fragment & the child fragment (1-to-1 mapping between presenters & views) or only one presenter for both and why?
2- If I'm supposed to use distinct presenters, how should I handle passing data from the child fragment to the parent fragment as I barely know the Cons. and Pros. of:
Using an EventBus framework like Otto
Allow a presenter to have a direct reference on another presenter
Keep the communication in the view layer, away from the presenters as here, by having the nested view delegates calls it receives from its presenter to the parent view.
As with most architectur questions, I honestly think there is no right or wrong way. So please treat this just a suggestion (how I would implement this)
Each MVP unit should contain it's own presenter, which means there is one parent presenter (for the parent fragment) and several child presenters (one for each child fragment).
The child presenters all contain a parentPresenter field, which acts as a way to pass data / messages from the child to the parent. This parentPresenter is NOT the real presenter object, but an interface that includes only the needed calls.
If you need to pass data / messages the other way around (from the parent to the children), this is implemented via interface methodes in the view:
the parentPresenter calls its view
the parentView finds it's childFragment
the childFragment calls the appropriate interface call on the childPresenter
This way the whole communication is hidden behind clean interfaces and is also nicely testable. Hope this helps and let me know if you have any questions...
What I do in my application is using a callback mechanism between parent and child fragments for passing data between.
I also made a different presenters for each child fragment, because if one day I would like to use only one of the child fragments I would only override it's presenter methods.
I have multiple instances of a fragment that are inflated from an xml file, with an id assigned there that I had hoped to use as a container root. If I want to attach a sub-fragment to one of these fragments as my container, how do I know which one is used, or better yet how do I select a particular container fragment? Specifically, I want to be able to attach a fragment to one of a set of pages in a ViewPager, each of which is an instance of a common fragment definition. Any one of these pages could serve as the root for the sub-fragment. However, they'll all end up with the same id since it's specified in the XML, and you can't call FragmentTransaction#add using a fragment tag name...
If I want to attach a sub-fragment to one of these fragments as my container, how do I know which one is used, or better yet how do I select a particular container fragment?
Android does not support nested fragments, so you simply do not do it at all.
Now you can use nested fragments - also included in support library
here is my solution
ViewPager and fragments — design nested fragments or ..?