I'm using data binding in my android project, also i'm using dagger 2 for DI.
basically for setting content view with data binding i need to do something like this :
LayoutClass layoutClass = DataBindingUtil.setContentView(Activity, Layout);
I'm providing that layoutClass in dagger module and injecting it to my activity. the question is, is this a good practice ?
Technically you're defining a circle-reference with this. You're just not warned, because setting up the graph requires you to be pro-active about this.
The dependencies would look like activity -> layout -> activity while you provide the module with an activity explicitly. Additionally you're modifying the activity with DataBindingUtil.setContentView() and therefore provide a dependency to the activity, which actually is a property of the activity itself.
So, never provide any UI with Dagger. Especially not to an activity.
Related
I have an app with the following architecture:
Navigator is a custom class that holds the NavController
Cooridnator holds the Navigator
Cooridnator tells the Navigator to "start" the framgent and passes the ViewModel to it
Navigator asks NavController to navigateTo a NavDirections and provides the required arguments (using Safe-Args)
Now the issue here is that if I want to send the ViewModel as argument, it needs to be Parcelable and all of its underlying classes as well (which would make most of my code Parcelable, and that's not really needed).
So is there a way to do this without making everything Parcelable or using Dagger ? (Don't like Dagger as it adds too much complexity to the code...)
I would be okay with having a lateinit field in the Fragment and setting it manually but can't seem to access the Fragment from NavDirections
Any idea on how I could do this ?
First of all: what you are passing in safe args is "data" while your viewmodel is logic. Which means your data can change over the time (one of examples would be to become outdated) but as long as viewmodel is unchanged, it's logic would stay. Thus passing viewmodel itself does not make sense to me - best you can is to pass its snapshot of state, but I doubt that's what you want.
So yes, you should be using DI and there are alternatives to dagger complexity. You can experiment with koin (because I see kotlin in your tags list), some basic outline of what it can is here https://shorturl.at/bflFL (medium). You can also experiment with Hilt as what appears to be simplified alternative to Dagger, for android world.
I'm using this library in my app. The way I've written is that I have a MainActivity with the layout as provided by the library viz. something like this:
<SlidingPanel>
<MainContentLayout>
<SlidingPanelContentLayout>
</SlidingPanel>
The MainContentLayout is basically a FrameLayout where I load a fragment into. Now here's where it gets tricky--the fragment has code that accesses the slidingupPanel's layout components. When I type the component in Android Studio it automatically imports the correct synthetic property class file as import kotlinx.android.synthetic.main.layout_sliding_panel.* and even autocomplete works to show the correct fields as properties. However, when I run the app is crashes saying that field is a null. To fix this, I have to add (activity as MainActivity). as a prefix to all the fields. And when I run this, it fixes the issue.
Is there a cleaner way to do this because writing (activity as MainActivity). in so many locations seems annoying. Is there like a kotlin directive for this and why doesn't KotlinX view binding library auto-detect that the sliding panel layout is on a different layout and therefore write to correct convenience class to access this properly without crashing the app?
I understood your question like this: you are trying to access one of activity's views from fragment it hosts.
Such approach is not good as it couples activity and fragment (thus making fragment non reusable in other activities). If really needed, interaction between activity and fragment can be done using interface activity implements. However you need to keep in mind fragment's lifecycle (in other words activity reference is not always accessible inside fragment). Last but not least, nothing wrong with kotlin extensions.
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.
I'm creating an Android app and want to comply to clean architecture.
For example, I have an activity which has a presenter which creates use cases. In that inner layer, I have a repository interface (which is known by the use cases) which is implemented by a concrete repository, lets call it repsoitoryImpl (which is not known by the uses cases). What I did in previous projects was to create the presenter and the repositoryImpl in the activity, and pass the repositoryImpl as a repository to the presenter. The presenter can then, whenever there is an action coming from the activity (e.g. a button press) create a new use case and pass the repository to it.
This works, but a) the constructors of the use cases can become very long and b) the UI has knowledge of all other "outer" things, e.g. the repositoryImpl. So I thought DI to the rescue! And started to try out Dagger 2. However, my current solution does not seem to be "correct". What I would have liked is that I can just have an #inject annotated repository in a usecase and a repositoryImpl gets injected. However I found that, at the beginning of the "injection chain" I have to call inject() on the dagger component. In most of the examples, this is done in the activity. But then I would have to inject the presenter in the activity and the usecase into the presenter to be able to inject things into the use case. Is this correct? The problem is that I want to create the use cases dynamically with different parameters and not inject them.
So my current solution is to have the dagger "AppComponent" as a static field in the Android Application class and then in my use cases I call
Application.component.inject(this)
which allows me to inject things in the use case. But then the use cases have a dependency to dagger which doesn't comply to clean architecture. Because framework dependencies should only appear in the outer layer.
Is there a common solution to this problem? Am I understanding something wrong?
As u already pointed out in clean architecture use cases must not know about DI frameworks - even decorating use cases with framework specific attributes would be a smell.
As discussed here: How to handle UseCase Interactor constructors that have too many dependency parameters in DDD w/ Clean Architecture? having too many constructor parameters is usually an indicator that the use case is "doing too much". U should consider splitting them.
Furthermore the interfaces used by a use case to access "the details" (repository, external services and systems) should be designed in a way that they are most convenient for the use case. That means instead of having multiple repository interfaces and multiple service interfaces passed to a use case u could consider using façade pattern and design one or two interfaces which are more convenient for the use cases which then "aggregate" the work with the different repositories/services. This will also reduce the number of parameters passed to the constructor.
According to clean architecture the "composition" of ur application happens in the "main component" - a class living in the frameworks circle. There are objects are created and injected. If u want to create use cases dynamically u could have a factory pattern.
Note : This is a question that I asked straight on the LightCycle project github. It's a great tool from SongKick to build a clean MVP architecture over your android app .
There is a thing that I miss thought, I have an activity with many fragments and many fragmentsPresenters.
Sometimes I do computation on my activity presenter and I want to send it to one or many of the fragment presenters (for example my table of content is displayes in the activity menu, and in a fragment that is shown full screen at the beginning).
How to I add a keep a reference of fragment presenters in my activity presenters (maybe it's not how I'm supposed to design it).
Second question. I have MyActivityPresenter that has two children : MyOnlineActivityPresenter and MyOfflineActivityPresenter.
MyActivityPresenter.newPresenter(Network.isNetworkAvailable(contexte), few other args) decides whearas an online or offline presenter is instanciated. So I should do something like :
#LightCycle
PlayerPresenter presenter = PlayerPresenter.get(NetworkUtils.isNetworkAvailable(this));
But I've been told that I should never use context that way as It could be null at the class instanciation moment. Is it indeed a problem ?
also should I pass the few others arguments that I have in the onCreate Bundle ?
And I don't use dependency injection at the moment.
I hope that I'm clear,
thanks again for this very useful lib
This is the answer they gave me
How to I add a keep a reference of fragment presenters in my activity presenters (maybe it's not how I'm supposed to design it).
It is not something in the scope of this library. I can see 2 solutions for you :
inject the same instance
provide an accessor to the presenter from the fragment. (which seems to be better for you).
But I've been told that I should never use context that way as It could be null at the class instantiation moment. Is it indeed a problem ?
also should I pass the few others arguments that I have in the onCreate Bundle ?
Same here.
You can use the app context which should be available and enough in your case
You can init this guy in the constructor because binding happens on create