Injecting Interface to an App Activity - Hilt+Android - android

New to DI. Let's say there is an interface XYZ in a module ABC, which is being used by the main app project as a dependency. I want to inject that interface XYZ to the MainActivity in the main project. Please see below how I am trying.
ABC Module Contents
XYZ
interface XYZ {
fun init()
}
TestView class implementing interface
class TestView: XYZ {
override fun init(){
}
}
Main project contents
AppModule class
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
fun xyz(): XYZ = TestView()
}
MainActivity
#AndroidEntryPoint
class MainActivity : AppCompactActivity() {
#Inject lateinit var xyz : XYZ
override onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
xyz.init()
}
}
Please let me know if there is something wrong with this.
If I use the same interface for another class let's say TestView2 and use it in another activity in main project. Can I provide that view as well in the AppModule class? If yes How will I differentiate it from the first one as Both will be using the same interface?
Thanks in advance.

I'm not a senior dev, so take my words with a grain of salt ;)
Please let me know if there is something wrong with this.
Yes and no (see below)
It will work and some people prefer to provide an interface this way,
however it is better to use #Binds (it generates less code, which makes your app smaller and the build times are quicker)
you can find how to use it here
If I use the same interface for another class let's say TestView2 and use it in another activity in main project. Can I provide that view as well in the AppModule class? If yes How will I differentiate it from the first one as Both will be using the same interface?
If you create 2 provide methods which return the same type, dagger won't know which method to use to provide your dependency, that's why you can name your providers (using the #Named annotation), you can find more about it here
(also, just a comment: Using multiple activities in one application isn't really recommended anymore, and I'm personally against it)

Related

Android Hilt - Question about binding per activity

I've a question about implementing bindings per activity. Let me give you background. In application I've for ex. two activities, both of them inject same simple provider with two different values. When I create two separate modules for both activities with #ActivityRetainedComponent scope - compiler gives me error that there are duplicated bindings. The solution for that problem is to create special scope for multiple activities but then I'll need to manually point which scope should be used. I rather to make that pointing on DI level. Below some sample code:
class SampleProvider(
var text : String
)
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class ActivityOneScope
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class ActivityTwoScope
#InstallIn(ActivityRetainedComponent::class)
#Module
object ActivityOneModule {
#Provides
#ActivityOneScope
#ActivityRetainedComponent
fun provideSampleProvider() : SampleProvider = SampleProvider("ActivityOne")
}
#InstallIn(ActivityRetainedComponent::class)
#Module
object ActivityTwoModule {
#Provides
#ActivityTwoScope
#ActivityRetainedComponent
fun provideSampleProvider() : SampleProvider = SampleProvider("ActivityTwo")
}
I want to omit specifying scope during injection inside activity like this:
#ActivityOneScope
#Inject lateinit var testProvider: TestProvider
Is it possible in Hilt? In Dagger 2 it was possible using for example #ContributesAndroidInject and pointing specific module.
Thanks in advance

Why does the author define a blank interface in a project?

The following code is from the project https://github.com/skydoves/Pokedex
I can't understand why the author need define a blank interface Repository.
What are the benefit using a blank interface Repository ?
Repository.kt
/** Repository is an interface for configuring base repository classes. */
interface Repository
DetailRepository.kt
class DetailRepository #Inject constructor(
private val pokedexClient: PokedexClient,
private val pokemonInfoDao: PokemonInfoDao
) : Repository {
...
}
MainRepository.kt
class MainRepository #Inject constructor(
private val pokedexClient: PokedexClient,
private val pokemonDao: PokemonDao
) : Repository {
...
}
It is called Marker interface pattern. It is interface without any methods or constants. Usually interfaces like this are created to provide special behaviour for a marked classes. You can find a few interfaces like this in java. For example Cloneable and Serializable.
In case of Pokedex i think the reason is much simpler. It looks like this interface was created just to group all repositories. Repository abstraction is never used in the project. Author is always using specific implementations. Repository interface is redundant but can be useful when we want to find all repositories in the project :)
I believe it's because of generics. If you want to create/use an object with generics, the interface will help you a lot.
Consider the following:
interface Animal
class Dog(name:String): Animal{
fun bark(){
println("woff")
}
}
class Cat(name:String): Animal{
fun meow(){
println("meow")
}
}
class Car(name:String){
fun horn(){
println("beep")
}
}
class Farm{
private val animals = ArrayList<Animals>()
fun addAnimal(animal: Animal){
// Even if you don't need a direct function from animal
// you can still store only animals and not cars!
animals.add(animal)
}
}
class Main{
fun main(){
var farm = Farm()
farm.addAnimal(Dog("rex")) // OK
farm.addAnimal(Cat("luna")) // OK
farm.addAnimal(Car("bentley")) // ERROR
}
}
Like this, you can enforce the developer to use the tool as you thought of it, avoiding populating objects with general-purpose items.
A further thought could be if you wanted to add some functionalities and you see at some point, that your class repository actually needs a function for each implementation. If you already have an interface, you won't have to worry to search for all possible implementations (maybe the implementation is in one project extending yours), as the compiler will tell you what is missing.

Dagger bound multiple times : Trying to create a fake implementation

Hi I'm trying to create a fake presenter of my activity I have my module let's call it Activity1module where I have set all of the presenter, use-case, everything and it works perfect, but when trying to create a screen which uses that exact activity with a fake presenter it says I've bound multiple times that presenter.
What I've did is :
#Module
abstract class Activity1Module{
#Binds
abstract fun providePresenter(impl: PresenterImpl) : Activity1Contract.Presenter
.....
}
Then I have created a new module FakeActivity1Module and it's like this :
#Module(includes = [Activity1Module::class])
abstract class FakeActivity1Module {
#Binds
abstract fun bindsFakePresenter(impl: FakePresenterImpl): Activity1Contract.Presenter
.....
}
But looks like it doesn't like this way, is there any way to use the fake one instead of the production one without creating #Named or touching production code?
Dagger doesn't have any capability to have one module override another's bindings directly. FakeActivity1Module and Activity1Module should include similar bindings but neither should be in the other's includes list.
You can, however, extract common bindings to an Activity1CommonModule (named as you'd like) and have both Activity1Module and FakeActivity1Module include that module. That would allow you to avoid repeating yourself as much as possible, at the conceptual cost of some indirection between files.
You can even include that Activity1CommonModule as a nested interface (or abstract or static class) within Activity1Module; you'd still need Activity1Module's includes to contain its own Activity1Module.Activity1CommonModule.class, but you'd have the benefit of centralizing all of Activity1Module's bindings in a single file, plus the benefit of easily seeing through diffs (git diff, p4 diff, etc) when a binding moves in or out of the common set.
Same class cannot be injected 2 times and it produce bound multiple times error. So for the FakeActivity1Module instead of create new injection. Try to override Activity1Module and apply your changes.
class FakeActivity1Module: Activity1Module {
overrides fun providePresenter(): Activity1Contract.Presenter = FakePresenterImpl()
}
And you use FakeActivity1Module into your component for testing and it should work as expected.

Provide all childs of a class with dagger

im creating a highly modular application, i have a lot of clases that need to be injected, all of them are childs (not direct childs) of the same class, none of them have constructor parameters.
I want to avoid having to create a "#Provides" method for each one of them in my module.
Is there a way to tell dagger to automatically provide all the classes that implement a base interface? Or is it possible to do it myself using reflection?
Im using dagger-android with kotlin
Update: Ill post some code to illustrate
In one of the modules i have this interface
interface ExampleClass: BaseExample {
fun doSomething()
}
}
Then in the main app i implement it
class ExampleClassImpl #Inject constructor() : ExampleClass {
override fun doSomething(){
}
}
The class where i need it is a Viewmodel created with dagger so inject works on the constructor.
class ExampleViewModel #Inject constructor(val exmpl :ExampleClass) : BaseViewModel {
}
I want to inject that ExampleClassImpl, to do that i need to create a #module with a method annotated with #Provides or #Bind and return that class.
Without the provider i get an error at compile time:
error: [Dagger/MissingBinding] com.myapp.ExampleClassImpl cannot be provided without an #Provides-annotated method.
You want to inject ExampleClass, but Dagger only knows about ExampleClassImpl. How would Dagger know that you want that specific subclass?
Moreover, you say you have many subclasses that you want to inject. How would Dagger know which one to provide to a constructor expecting the base class?
If you want ExampleViewModel to get an instance of ExampleClassImpl then you can simply change the declaration to:
class ExampleViewModel #Inject constructor(val exmpl :ExampleClassImpl)
Doing so you lose the ability to swap the constructor argument with a different implementation of ExampleClass.
The alternative is to have one #Named #Provides method per subclass. So something like:
// In your module
#Provides
#Named("impl1")
fun provideExampleClassImpl1(impl: ExampleClassImpl): ExampleClass = impl
// When using the named dependency
class ExampleViewModel #Inject constructor(#Named("impl1") val exmpl :ExampleClass)

How to mock after setup dagger-android 2.15 when writing espresso tests?

If we just use plain dagger 2. In the application class, we will have a property which holds the AppComponent. Then we can swap it during espresso tests.
But when I setup my project using dagger-android 2.15. Things becomes more implicit if adopt too much Dagger magic. The code is more clean, but makes testing a little bit hard.
This is the application class:
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.create(this)
.build()
}
}
This is the HomeActivity
class HomeActivity : DaggerAppCompatActivity() {
#Inject
lateinit var userPreference: UserPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
if (!this.userPreference.memberRegistered) {
goToActivity(EntryActivity::class.java)
}
}
}
Take this code for example. How to mock that injected userPreference.memberRegistered Which could be a HTTP call underneath?
For those who is interested in this, I got a blog with step by step detail for this:
Basically, the idea is:
You still generate instances for injection in #Module
But We’ll create new #Component A only for testing
This #Component will have a method to get that #Module
During tests, we swap the #Component that the application use with our component A
Then things are easy:
Without DaggerMock
In the #Module, instead of return real instance, you just return mockito mock.
With DaggerMock
You declare the type you want to swap and mock it
You can then use the mock.
No need to change the #Module
It inspires by #AutonomousApps 's solution, but the differences are now you don't need to write the #Component, #Module for each test class.
After trying several approaches, this is the only one that worked for me.
I wrote a blog post that explains how to do this just yesterday: https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj
I don't intend to repeat the entire post for this answer (it's hundreds of words and lines of code to properly set up a test harness with Dagger), but to attempt to summarize:
Add a custom application class in the debug source set (I assume it would also work in the androidTest source set, but I have not tried this).
You also need to reference this application in a AndroidManifest.xml in the same source set.
Create a "Test component" in your androidTest class that extends from your production top-level component and build it.
Use that test component to inject your application, which means you've just replaced your entire Dagger dependency graph with a new one you've defined just for the test suite.
Profit.

Categories

Resources