Fragment-scoped ObjectGraph - android

I learned how to create Application-scoped ObjectGraph, and #Inject into my Views from it.
Then I learned how to create Activity-scoped ObjectGraph, and #Inject into my View's from an Activity's Context.
Now I need to learn how to create a Fragment-scoped ObjectGraph, since Fragment is not a Context and is not injected into my Views by the LayoutInflater.
As I see the problem: I need a way to reference a Fragment from inside a View, but I think this is essentially wrong, and I need another solution

I ended up using Mortar for that https://github.com/square/mortar

Related

Why is viewModel() used in a composabe and viewModels() in an activity or fragment?

In this link it is instructed to use viewModel() in any composable and in an activity, we will get the same object while calling viewModel(). Although it is instructed to use viewModel() inside a composable, I was able to use it in setContent{} (outside of any composable) also.
In this link it is instructed to use viewModels() in an activity or a fragment to get the object of a class that extends ViewModel.
In both of the cases, we are getting an object of a class that extends ViewModel. So, why do we need to use two different approaches (viewModel() and viewModels())?
If you ask if you can use only viewModel without viewModels in Compose, the answer is yes. But in some cases it is more convenient to use both of them.
viewModels belongs to the androidx.activity package, is an extension to ComponentActivity, and has nothing to do with Compose. It was used in view-based Android and can still be used with Compose when you need to initialize or update your view model with some activity-specific callbacks.
In turn, viewModel is part of Compose and allows you to easily create/access a view model from any Composable.
You can call it directly inside setContent since it already belongs to the composable scope, but you would not be as comfortable calling it anywhere else in the activity, such as in onActivityResult(I know it's deprecated, it's just an example). You can still do it as shown in this answer, but in some cases viewModels may be easier to use.

Fragment Inheritance with ViewModel Inheritance

I have two Fragments and two ViewModels with a similar implementation. So, I decided to apply inheritance. So I decided to create a FragmentParent and a ViewModelParent, and then ended up with:
FragmentA and FragmentB both inherit from FragmentParent.
ViewModel(A) and ViewModelB both inherit from ViewModelParent.
On one side, both parents will be abstract because they have methods to be implemented in different ways. On the other side, both children ViewModels have the common parent's viewmodel methods and also their personal custom methods.
So, viewmodel object has to call some common methods from the FragmentParent but, also, the problem is that each Fragment will call their correspondent viewmodels' custom methods. So, if I declare the viewModel object in the FragmentParent, once I use it in the children to call the custom methos of each correspondent viewmodel, it says error because the viewModel object's type corresponds to the ViewModelParent.
As you can see in the image, the methods in colour cannot be called because vM is instance of ViewModelParent and they belong to the custom ViewModels.
A solution could be casting the viewmodel object in each child fragment once I need to call the custom methods, BUT, I guess this is dirty. Any good idea for this approach? Thanks.
Casting the parent ViewModel to the specific child ViewModel seems to be the best option. It's a common use of casting, I don't see any problem with it. If you're accessing methods of the child ViewModel multiple times inside the child Fragments, you can store the casted ViewModel in a property or variable in each child Fragment. For example, if you're using Kotlin you could do something like this:
//FragmentA
val viewModel = vM as ViewModelA
viewModel.customAmethod()
//FragmentB
val viewModel = vM as ViewModelB
viewModel.customBmethod()

MVVM architecture with custom view

I want to make a custom view in android with MVVM architecture. First of all, I want to ask, is ViewModel working perfectly with a custom view as it works in case of activity or fragment? Can we get ViewModel from ViewModel provider in a custom view?
If I need to make a separate custom view what will the correct approach?
A better alternative would be to use the new API view.findViewTreeViewModelStoreOwner() which gives you the viewModelStoreOwner(Fragment if view is attached to the fragment o/w activity)
You can create ViewModelProvider and then get the ViewModel.
Below is an example of code in Kotlin
private val viewModel by lazy(LazyThreadSafetyMode.NONE) {
ViewModelProvider(viewModelStoreOwner).get(ViewModel::class.java)
}
Similarly, there are other similar APIs like view.findViewTreeLifecycleOwner() and view.findViewTreeSavedStateRegistryOwner()
It is a much cleaner approach as you don't have to typecast your context into Activity or Fragment and will scale to other implementations of ViewModelStoreOwner as well.
One thing to note here is that view may have a shorter life span compared to Activity/Fragment and so you might have to make a custom view Lifecycle(so that your LiveData subscription gets managed properly) using LifecycleRegistry based on onAttachedToWindow and onDetachedFromWindow callbacks
Q: Can we get ViewModel from ViewModel provider in a custom view?
Ans: Simple answer would be yes you can !
But How? (Further explanation) ViewModelProviders required either context as Activity or Fragment. So you can retrieve context from your CustomView class using getContext() which would be Activity/Fragment where you're using it.
Cast that context to either of type & provide it to ViewModelProviders which will give you object of that Activity/Fragment container.
Hence using like this, you can share ViewModel between your CustomView and Activity/Fragment.
Side Note: You can also make your CustomView implement LifeCycleObserver, in such way you can also make your view respect lifecycle of Activity/Fragment for initialization/destruction stuffs.

How to create custom scope and share same instances using Dagger Android

So here are the things I know from the doc
Dagger Android under the hood is creating subcomponent for each Activity annotated with ContributesAndroidInjector
You can apply custom scope to the method where ContributesAndroidInjector is annotated to
If two sibling subcomponents have the same scope, they will still have different scope instances
If an Activity is in a subcomponent, it can have its own subcomponent which can contain Fragments. Those Fragments will share the scoped instances the Activity has.
Now my question is:
How to have one Activity be a subcomponent of another activity using Dagger Android?
I want to do this because I want to achieve things like #UserScope/#SessionScope.
From this I know that I can do it with just Dagger not Dagger Android. But with Dagger Android, you can only have the Application (which is the AndroidInjector) to inject Activity. You can not have an Activity used as a holder or host of the parent subcomponent to inject another Activity.
Am I understanding it correctly?
05/14/2018 Update:
I ended up getting rid of Dagger Android. So no more ContributesAndroidInjector, just pure Dagger. And to inject Activity/Fragment, I use the way that's recommended here. It will be something like this:
class MyActivity : AppCompatActivity() {
private val factory: ViewModelProvider.Factory = Injector.myCustomScope().factory()
}
And we are trying to make sure the factory is the only thing that Activity/Fragment needs.
So far it's been great.
How to have one Activity be a subcomponent of another activity using Dagger Android?
tl;dr You can't. Dagger Android follows a strict AppComponent > ActivityComponent > FragmentComponent scheme and there is no way to add custom scopes in-between.
I suggest you have a look at the Dagger Android source code, it's really not that much. It's basicalle a HashMap for each layer where you look up the component builder and build the subcomponent. A fragment looks at its parent Activity, an Activity looks at the Application. There is no feature where you can add custom components between layers.
What you can do is create your own variant of "Dagger Android" where you can implement your own interfaces and mix/match components as you need them. But that's quite a bit of extra work. I created a #PerScreen scope that survives configuration changes as a proof of concept if you are interested to see how you could do such a thing.
You can create a custom Scope called for example #PerScreen, also you will have #PerActvity scope. The difference between these scopes is that the #PerActivity scope will maintain shared dependencies between all activities like Context, Layout Inflater, etc. And all activity specific dependencies will be scoped as #PerScreen.
#PerApplication -> #PerActivity -> #PerScreen
This could structured like that.
I have explained scopes under the hood in my blog post, you can refer to it to get better understanding of this matter.

How to preserve Mosby's Presenter when using Conductor framework in Android?

Probably the answer is "you can't", because in Mosby 2.0 you need to use fragment and set it to setRetainInstance(true) to preserve Presenter. And the aim of Conductor is to remove the need of using Fragments, so there is no way to use setRetainInstance(true) anywhere in your app.
But maybe there IS another way..
Here is the official Mosby plugin for Conductor:
https://github.com/sockeqwe/mosby-conductor
Sorry i cannot comment because i don't have enough reputation but Conductor is using Conductor.attachRouter in order to take instance inside an activity and attachRouter is using the LifecycleHandler in order to take a "saved" (aka retained instance) because LifecycleHandler is a headless fragment. So in order for you to have a presenter which is not being destroyed on configuration changes all you have to do it to create your presenter inside the constructor of the controller. If i have understand right, the constructor of the controller has the life time of a headless fragment. So problem solved or i might have made a mistake somewhere...

Categories

Resources