Inject into arbitrary Logic class that is not instanciated by Hilt - android

I'm currently migrating an app from anko-sqlite to room as anko has been discontinued a long time ago. In the process of doing so I introduced a repository layer and thought I would try to introduce dependency injection to get references to the repository instances in all my classes.
Prior to this I used a singleton instance that I just shoved into my application classes companion object and accessed that from everywhere.
I managed to inject my repositories into Fragments, Workmanager and Viewmodels. But I do now struggle a bit to understand how they forsaw we should handle this with arbitrary logic classes.
For instance my workmanager worker calls a class that instantiates a list of "jobs" and those need access to the repository. The worker itself actually bearly even does need access to the repositories as all the work is abstracted away from it.
How can I make something like this work
class Job(val someExtraArgINeed: Int) {
#Inject
lateinit var someRepository: SomeRepository // <= this should be injected when the job is instanciated with the constructor that already exists
}
For Activities and so forth we have special annotations #AndroidEntryPoint that makes this work. However, the documentation makes it unclear as to how we should get our instances from any other class that isn't an Android class. I understand that constructor injection is the recommended thing to use. But I do not think it would work here for me without an even more massive refactor required than this already would be.

If I understand your question correctly you can use hilt entry points like this
class CustomClass(applicationContext: Context) {
#EntryPoint
#InstallIn([/*hilt component that provide SomeRepository*/ApplicationComponent]::class)
interface SomeRepositoryEntryPoint {
fun provideSomeRepository(): SomeRepository
}
private val someRepository by lazy {
EntryPointAccessors.fromApplication(applicationContext, SomeRepositoryEntryPoint::class.java).provideSomeRepository()
}
fun doSomething() {
someRepository.doSomething()
}
}

Related

Should I use #Singleton, object or both with Dagger2 and Kotlin?

I'm doing an app using Kotlin and Dagger2, trying to follow the MVVM pattern, but I'm in a dilemma, should I use #Singleton, object or both? And why? Let's say I have a RepositoryMovies class and I want to get the same instance every time, according to my research you can do this as follows:
#Singleton (Dagger2 way)
#Singleton
class RepositoryMovies {
TODO()
}
Object (Kotlin way)
object RepositoryMovies {
TODO()
}
Both
#Singleton
object RepositoryMovies {
TODO()
}
And don't get me started with singletons in Kotlin following the "Java-Way". I'd appreciate your help. Thanks so much.
Injecting an object doesn't make much sense, since in kotlin object is used to simulate java's utility classes, such as java's Arrays or Collections classes. One defining characteristic of such classes is that, they are not associated with any specific class in your project, they can be required and used anywhere.
On the other hand in most practical situations a repository will be associated with a specific class. for example you may only want to inject a UserRepository in a UserViewModel, because that is the only place where you need to access user's information.
As for the object and #Singleton, object is by definition a singleton, so marking it with #Singleton is redundant and doesn't accomplish anything until you make it injectable by means of a #Provides function. where you have to specify, how dagger can create instances of this class?
In your first example marking a class #Singleton doesn't do anything, unless it is injectable. as the docs state.
Singletons and Scoped Bindings
Annotate an #Provides method or injectable class with #Singleton. The graph will use a single instance of the value for
all of its clients.

Dagger 2 Inject Fields

I have a class that is dagger injected via the constructor. However I now need to add an argument to this constructor that is not provided via injection and is instead a run time argument.
In my previous job, we rolled our own DI so I am not up to speed with all the "magic" annotations that dagger offers yet. I figured it should be simple to add an argument to a constructor and still have dagger inject the remaining values (as it was very simple to do with the aforementioned "roll your own DI" solution I have implemented before).
However, it looks like this is not possible with dagger (i.e. assisted injection). So I have been reading on how to solve this issue but have become completely stumped.
Here is the class that I am currently (successfully) injecting ServiceA into:
class Foo #Inject constructor(private val serviceA: ServiceA) {
...
}
What I would like to do is add another argument to this constructor that will be provided at run time. In this case a simple flag to determine some behaviour in the class. And, given that Dagger doesn't support assisted injection, I need to remove injection from Foo and instead create a FooFactory to handle creation of Foo objects. This factory will have ServiceA injected into its constructor and will provide a method to construct an instance of Foo taking the boolean. I will then end up with a Foo class that looks like:
class Foo(private val serviceA: ServiceA, myNewArgument: Boolean) {
...
}
And a FooFactory that looks like:
#Singleton
class FooFactory #Inject constructor(private val serviceA: ServiceA) {
fun createFoo(myNewArgument: Boolean) {
return Foo(serviceA, myNewArgument)
}
}
And, although this is a complete mess to just get an extra constructor arg, it does the job.
The problem I am facing, is that my Foo class is actually an AndroidViewModel, and will need to be constructed through the ViewModelProvider.Factory contract, which has a create method which is invoked by the SDK to create the view model. You override this method to create an instance of the view model but the method has no parameters, so there is no way to propagate the flag into the view model through this method.
So the only way for me to get the flag propagated to the view models constructor is by having the factory itself take the flag as an argument to it's constructor, which, because dagger does not support assisted injection, is not possible.
So, instead I am planning to make dagger manually inject my dependencies into the FooFactory at initialization time. This is where I am stuck, I cannot figure out how on earth to get dagger to manually inject dependencies into my class.
My FooFactory now looks like:
class FooFactory(private val myNewArgument: Boolean) : ViewModelProvider.Factory {
init {
// I need to trigger injection here... how though???
}
#Inject
lateinit var serviceA: ServiceA
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return Foo(
serviceA,
myNewArgument
) as T
}
}
So, somehow in the init block of the factory, I need to ask dagger to inject the fields annotated with #Inject. But I have no idea how to do this, I have tried following the answers at the following questions and tutorials:
https://proandroiddev.com/from-dagger-components-to-manual-dependency-injection-110015abe6e0
Dagger 2 - injecting non Android classes
Dagger 2 injection in non Activity Java class
None of these seem to work for my use case and I'm starting to lose it. Could anyone point me in the right direction? Is this dagger framework massively over engineered/complicated for not much benefit (this is the conclusion I am coming to at this point, all I want to do is achieve DI for testing purposes, I don't want to have to write factories so I can add an extra argument to a constructor)...

Dagger singleton vs Kotlin object

To define a singleton, should I use Kotlin object declaration or to make an ordinary Kotlin class and inject it using dagger? In my opinion the first option is definitely easier but there may be a reason to use dagger in this situation that I'm not aware of.
Option 1 (notice object keyword):
object SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
Option 2 (notice class keyword):
class SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
#Module
class AppModule {
#Provides
#Singleton
internal fun provideTheUtil() = SomeUtil()
}
class MainActivity : BaseActivity() {
#Inject internal lateinit var util: SomeUtil
}
UPDATE 2019-07-03
#Blackbelt said in comments that we should prefer option 2 for testability. But libraries like MockK can mock objects too. So do you still think option 2 is the preferred one?
You might want to reconsider the need of NumberFormatUtil being a singleton. It might be cheaper if you use #Reusable with Dagger or even a factory without any scope directly.
If NumberFormatUtil is fairly simple and only provides a few utility methods, no state and no need for mocking in tests, you could use an object implementation, maybe using #JvmStatic for Java-inter-operability. But then you could go for global utility (extension) functions as well:
package xyz
fun formatNumber(number: Long) {
// ...
}
fun Long.format() = formatNumber(this)
You should use option 2.
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance. This is useful when exactly one object is needed to
coordinate actions across the system.
From: Singleton Pattern
So, a singleton is single instance in a scope. In case of Android, it is virtual machine instance running the app. In case you need custom scopes, you have to use option 2 only.
But, if you have only static methods inside the object you want to inject its better to keep them as global methods and even get rid of object. No need to inject anything. It is similar to a java class with only static methods (I mentioned this point as it is a usual way of creating Utility classes).
However, if the object also has some state. I would recommend going dagger way. The object way does not provide with dependency injection. It only creates a Singleton. Your purpose for using dagger is dependency injection.

Dagger 2 - Strategies for reducing number of classes which require annotations

So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.

Dagger: What if I *WANT* a new instance every time?

Interesting how difficult this answer is to find.
I've been using Dagger - Android for a while now and have my entire dependency graph set up. I'm using scopes, qualifiers, all that good stuff. I'm not a Dagger newbie anymore, but suffice to say I've been using it in a pretty standard way in my Android setup and everything has been going great.
For the first time, I'm realizing that I'd like to request new instances of a certain class in my graph myself, manually, and I want it to be a new instance every time.
What's the best way to go about doing that? I'm wondering if there's a way to leverage a non-#Singleton/non-scoped provider and call some sort of create() method myself, or if it's best to create a factory myself and make that factory a singleton/scoped instance and use my factory to get the new instance(s) when I need them? [I should mention this class will definitely not have an empty constructor so will need injected instances of other classes defined in my injection graph.]
(Also, it would probably help the most if answers were in the context of Android; i.e. I'm in, say, a ViewModel and need a new instance of some class defined in one of my Modules.)
Dagger will provide you a new instance as long as you don't scope the dependency.
To get a new instance of a dependency manually, you can inject Provider of it instead and use its get() method, it'll give you a new instance every time you call it.
Module part doesn't really change:
#Module
class AppModule {
#Provides
fun provideSomeObject(): SomeObject = SomeObject()
}
And in your class
class SomeClass {
// We don't inject the object anymore
// #Inject lateinit var myObject : SomeObject
// We'll inject it's provider
#Inject lateinit var myObject : Provider<SomeObject>
fun someMethod(){
// Here, instance1 and instance2 are NOT same objects
val instance1 = myObject.get()
val instance2 = myObject.get()
}
}
You can read more here.

Categories

Resources