The Dagger documentation page says:
To get the most out of compile-time validation, create a module that includes all of your application's modules.
This leave some questions to be answered:
What is actually the effect of including other modules? (At least, it seem included modules do not need to be instantiated directly)
Is it legal/possible to have one module included multiple times by different modules? What would happen then?
The documentation needs improvement.
Includes is a literal inclusion - all of the #Provides methods of included modules, fully transitively, are collected together and considered as (in effect) part of the analyzed module. So:
#Module(includes = BModule.class)
class AModule {
#Provides A provideA(...) { ... }
}
#Module
class BModule {
#Provides B provideB(...) { ... }
}
is functionally identical to
#Module
class JointModule {
#Provides A provideA(...) { ... }
#Provides B provideB(...) { ... }
}
Additionally, module inclusion collapses duplicates. So if you have:
#Module(includes = {BModule.class, CModule.class})
class AModule { ... }
#Module(includes = CModule.class)
class BModule { ... }
#Module
class CModule { ... }
it will result in a collection of bindings (de-duplicated) from AModule + BModule + CModule.
Related
I've been trying to find a way to provide dependencies depending where a fragment is navigated from. I have the following in a library module:
interface FruitProvider {
fun provide(): String
}
class FruitFragment : Fragment() {
#Inject
lateinit var provider: FruitProvider
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
The idea is for other modules to consume this library and inject whatever they want as a FruitProvider.
As an example, if I have 2 providers:
class BananaProvider #Inject constructor() : FruitProvider {
override fun provide() = "banana"
}
class PearProvider #Inject constructor() : FruitProvider {
override fun provide() = "pear"
}
If I'm in the banana module I'd like to create and display an instance of FruitFragment injected with BananaProvider. Similarly for the pear module but with a PearProvider.
Background
Right now we have a method in FruitFragment to set the provider, i.e., setFruitProvider(provider: FruitProvider) that is called by the consumers of the library.
This approach is not ideal because if the activity gets recreated this field is null. At least this is what some debugging seems to suggest. Put simply, the fragment is recreated but the setFruitProvider is not being called again since it gets called from methods outside the fragment's lifecycle.
The interface FruitProvider cannot be Serializable so we cannot save it in the instance state. All that we think is left is to inject the fragment since this can be done by the fragment itself when attached.
This question already has answers here:
Why #ContributesAndroidInjector doesn't provide Android Framework type
(2 answers)
Closed 3 years ago.
I have two provision methods, one depending on the object the other method provides. Below code works properly the problem is stated under the section of Problem
Working Example
In StringProviderModule, added a method which is dependent on the object (Fragment instance) provided in the parent component.
#Module
class StringProviderModule {
#Module
companion object{
#JvmStatic
#Provides
fun provideFragmentArgument(cripledFragment: CripledFragment): String{
return cripledFragment.arguments?.getString(CripledFragment.ARG_STRING) ?: "No such argument"
}
}
}
In FragmentModule, added StringProviderModule to FragmentModule's generated subcomponent
#Module
abstract class FragmentModule {
#ContributesAndroidInjector(modules = [StringProviderModule::class])
abstract fun fragment: CripledFragment
}
In ActivityBinderModule, adding FragmentModule to the ActivityBinderModule's generated subcomponent
#Module
abstract class ActivityBinderModule {
#ContributesAndroidInjector(modules = [FragmentModule::class])
abstract fun mainActivity(): MainActivity
}
In AppComponent, adding ActivityBinderModule as a module
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBinderModule::class
])
interface AppComponent: AndroidInjector<DaggerMyApp> {
#Component.Builder
interface Builder{
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
Then #Injecting String into Presenter class and #Injecting that presenter into CripledFragment. I can access Fragment's String argument in Presenter. Implementation is not important as it's outside the scope of the question, I can add code if needed, though.
Problem
When merging StringProviderModule and FragmentModule, IDE complains with following message "CripledFragment cannot be provided without an #Inject constructor or an #Provides-annotated method". Isn't #ContributesAndroidInjector already providing an instance of CripledFragment?
#Module
abstract class FragmentModule {
#ContributesAndroidInjector
abstract fun cripledFragment(): CripledFragment
#Module
companion object{
#JvmStatic
#Provides
fun provideFragmentArgument(cripledFragment: CripledFragment): String{
return cripledFragment.arguments?.getString(CripledFragment.ARG_STRING) ?: "No such argument"
}
}
}
I have checked multiple tutorials and dagger docs, couldn't find any proper explanation to infer the cause of my problem. Simple explanation rather than suggesting a solution is much appreciated as I'm not looking for a concrete solution.
This question has been answered here. Basically, #ContributesAndroidInjector provides MainActivity instance for a subcomponent it generates not for a module (in this case ActivityBinderModule) it resides in. That means any provision method inside FragmentModule will have access to MainActivity's instance.
#Module
abstract class ActivityBinderModule {
#ContributesAndroidInjector(modules = [FragmentModule::class])
abstract fun mainActivity(): MainActivity
}
In Swift we usually use extensions as a way to organize methods in separate blocks and even files. This makes code much cleaner but it also allows us to do some tricks such as:
class API {}
extension API {
class Lists{}
}
extension Lists {
class Posts {
func latest() -> [Post] {
// get latest posts from a REST api
}
}
}
We can put any of the extension blocks in a separate file and it works perfectly in Swift.
Now one do the following to get the latest posts from the API in a clean way
let posts = API.Lists.Posts.latest()
Trying to convert that code into Kotlin I used SwiftKotlin converter tool that I thought that might work but it doesn't compile as It seems to be invalid:
class API {}
class API.Lists {}
class Lists.Posts {
companion object {
fun latest() {
// get posts
}
}
}
So I came up with the following that works fine and also compiles but it's not suitable for my case as methods can be quite long and I can't afford to have them all in one class in one file and I don't know how I can split them in multiple files.
class API {
class Lists {
class Posts {
companion object {
fun latest() {
}
}
}
}
}
Any suggestion is appreciated.
To put an extension on a companion object, you can write
fun API.Lists.Posts.Companion.latest() ...
You still need
class API {
class Lists {
class Posts {
companion object {
}
}
}
}
in a single file, but extensions can be defined elsewhere.
If you just want to mimic usage of these calls, you can use objects, like this for example:
object ApiPosts {
fun latest() {}
}
object ApiLists {
val Posts = ApiPosts
}
object API {
val Lists = ApiLists
}
API.Lists.Posts.latest()
But this is really not a Kotlin way, and in common case it's a bad practice to write in a language the way it's not supposed to.
One possible solution is to just extend the inner classes with normal functions not static ones (companion):
class ParentClass {
class InnerClass {
}
}
And in any other file you could do:
fun ParentClass.InnerClass.instanceMember() {
}
And for usage:
ParentClass.InnerClass().instanceMember()
Of course this isn't exactly like the Swift version but it's close enough.
According to Dagger documentation about injecting activity objects, it says that installing AndroidInjectionModule in your application component. However, everything is fine without it.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
For example:
Injected instance
data class Food(val name: String)
Module
#Module
class FoodModule{
#Provides
fun provideFood(): Food {
return Food("cholocate")
}
}
BindingModule
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector(modules = [FoodModule::class])
abstract fun FoodShop(): MainActivity
}
AppComponent (Without installing AndroidInjectionModule)
#Component(modules = [MainActivityModule::class])
interface AppComponent{
fun inject(app: App)
}
App
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.create().inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> {
return dispatchingActivityInjector
}
}
MainActivity
class MainActivity : AppCompatActivity() {
#Inject
lateinit var food: Food
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("test", "Get ${food.name}")
}
}
It get chocolate successfully in MainActivity.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
It actually seems like you don't need to declare it, but it might lead to compile errors if you don't.
If you have a look at AndroidInjectionModule you can see that it just lists a bunch of #Multibinds methods for framework types.
#Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
Now if you look up Declaring #Multibinds you can read that
You do not have to use #Multibinds for sets or maps that have at least one #IntoSet, #ElementsIntoSet, or #IntoMap binding, but you do have to declare them if they may be empty.
And to declare them if they may be empty is exactly what the AndroidInjectionModule module is doing for you. If the Android Injection parts would require an undefined Map of injector factories you would probably get a compile time error stating that it cannot be provided.
The reason that you don't need the module is because you're using #ContributesAndroidInjector, of which the generated code will contain a #Binds #IntoMap etc. method, that declares the bindings map. Stated above—as it is not empty anymore—you would not need the additional #Multibinds declaration that AndroidInjectionModule provides for the non-empty multibinding.
You might not need the module, but it will declare all the framework injector factories for you in case that they might be empty, possibly preventing one or two compile errors. After all the javadoc simply states that it should be installed, not that it must.
This module should be installed in the component that is used to inject the Application class.
I'm working with Ionic 2 and I have entity management page which is suitable for different data providers.
Now I have page for character management.
export class CharacterListPage{
currentItems: Character[];
constructor(public provider: Characters) {
this.currentItems = this.provider.query();
}
// other stuff
}
I need to do creature management. Creatures structure is identical to character structure.
Obvious way is just copy-paste page and make some renaming:
export class CreatureListPage{
currentItems: Character[];
constructor(public provider: Creatures) {
this.currentItems = this.provider.query();
}
// other stuff
}
Is it possible to use more efficient way?
That looks like a typical use case for an abstract class, luckily typescript brings that feature to the .js world:
You have a base class, lets call it ListPage, which holds all the common functionality of a Character and a Creature which can then be extended by other pages and reuse this existing functionality:
class ListPage {
listItems: Character[] | Creature[];
constructor(provider: Characters | Creatures) {
this.listItems = provider.query();
}
// other common methods
}
class CharacterListPage extends ListPage {
constructor(provider: Characters) {
super(provider);
}
}
class CreatureListPage extends ListPage {
constructor(provider: Creatures) {
super(provider);
}
}
For this minimal example it may not seem very efficient but when your classes get more complex and have much common functionality it will be very useful.
Here you can find the documentation on typescript classes.