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

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.

Related

MVVM - How to share a single repository class across multiple ViewModels

I have multiple viewmodels that access a single repository (one activity and the rest fragments).
AdminActivityViewModel
AdminListUsersViewModel
AdminUserTransactionsViewModel
... and a few more
My AdminRepo class has multiple constructors so that I can pass callback methods from the ViewModel
public AdminRepo(Application application, AdminActivityCallback callback) {
this.callback = callback;
BaseApplication baseApplication = (BaseApplication) application;
RetrofitClient client = baseApplication.getRetrofitClient();
adminService = client.getRetrofit().create(AdminService.class);
SharedPrefManager sharedPref = SharedPrefManager.getInstance(application);
AuthHeader authHeader = new AuthHeader(
sharedPref.getIdToken(),
sharedPref.getIdClient(),
sharedPref.getUserEmail()
);
client.setAuthHeader(authHeader);
}
public AdminRepo(Application application, AdminListUsersCallback callback) {
//Exact same code in constructor as above ^
}
public AdminRepo(Application application, AdminUserTransactionsCallback callback) {
//Exact same code in constructor as above ^
}
And in each of the ViewModels I am creating an instance of the AdminRepo (which is probably a bad practice) but I don't know how I can improve this.
public class AdminActivityViewModel extends AndroidViewModel implements AdminActivityCallback
public AdminActivityViewModel(#NonNull Application application) {
super(application);
repo = new AdminRepo(application, this);
}
How can I design my AdminRepo and ViewModels so that they share exactly one repository without creating an expensive AdminRepo class everytime?
I have considered making my AdminRepo class a singleton with a .getInstance() method, but I'm getting SO MANY contradicting posts on how Repository classes SHOULD NOT be static or a singleton, which makes me extremely confused on what I SHOULD do.
https://stackoverflow.com/a/7464235/11110509
If all view models live at the same time then and repository does not keep any state you could share the same instance of repository and provide that instance for each view model. However there's no place for magic here - if you create an instance of repository, you have to keep a reference to that, so you can pass the same object to other view models.
At first you need to make your view model accepting external dependencies (I\d recommend seeing dependency injection pattern)
YourViewModel( /* other dependencies ..., */ AdminRepo adminRepo)
Once you have that, you need to create a view model factory. This post describes it nicely, but long story short: implement ViewModelProvider.Factory that will keep your repository instance and use it in ViewModelProvider to obtain an instance of your view model.
With this setup you will be in control what instance you create and how you create other view models. This could be automated though with the use of dependency injection framework (Koin, dagger, hilt). If you use Dagger or Hilt (recommended by Google), then you can leave your object lifecycle in hands of the framework by providing a proper annotation. Let's try this exaple:
#Singleton
class MyRepository { ...
#HiltViewModel
class MyViewModel {
MyRepository repo;
#Inject MyViewModel(MyRepository repo) { ...
...
This code will make your repository a singleton tied to your application lifecycle. Usually you don't want to do that, because the object will live in the memory even if you move to the screen that deosn't need that. But you could use #ActivityScoped so your repository lives as long the activity.
Now if those view models have different lifecycles and they don't overlap, it would be completely fine to create a new insance for each one of them. You wouldn't want to keep unnecessary objects in the memory while you don't need them anymore.

Android Hilt : How I can inject any object in Object class or singleton class

I want to inject application string inside object class SingletonObject. I am new in Hilt and not getting any way to inject this
object SinglentonObject{
#AppQualifier
#Inject
lateinit var applicationString: String
}
The #Inject annotation is available only for EntryPoints like #AndroidEntryPoint, #HiltAndroidApp. As of now, there is no option to inject into a non-entry point classes in Hilt.
You can't use the #Inject annotation but, if you still want to have a single source of truth and use Hilt from anywhere, you can create a custom EntryPoint and then use the ApplicationContext to grab an instance of whatever you are providing with Hilt.
First, you have to declare the EntryPoint (it can be added anywhere you want, but the best practice is to keep it closer to where it's used):
#EntryPoint
#InstallIn(SingletonComponent::class)
interface ApplicationStringInterface {
#AppQualifier fun getApplicationString(): String
}
then you can grab an instance of the ApplicationString like this:
var applicationString = EntryPoints.get(applicationContext, ApplicationStringInterface::class.java).getApplicationString();
If you need help to get an instance of the applicationContext from anywhere, look here: https://stackoverflow.com/a/54076015/293878

Inject into arbitrary Logic class that is not instanciated by Hilt

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()
}
}

ViewModelFactory need

I'm doing some codelabs of kotlin fundamentals and I don't really get in android with the ViewModel why sometimes there seems to be a need of creating it through a ViewModelFactory. Here you can see the codelab which talks about this.
They just say to perform the initialization using the factory method pattern but I don't understand the why. Why do we need to use factory pattern? Is it because we need to pass some parameter to the ViewModel? Or is it for some other reason? Is every time we need to create a ViewModelFactory just to pass parameters to the ViewModel?
I've been looking for the answer, trying to confirm whether is just to pass extra parameters or is because of any other reason but still I'm not sure and I haven't found the answer.
There are a few things that need to consider before using ViewModel and ViewModelFactory
ViewModel is LifecycleAware Components.
ViewModel survive configuration changes.
ViewModelProvider' can only instantiateViewModel` with no-args contructor.
Why do we need to use factory pattern?
To instantiate ViewModel with arguments need to use ViewModelFactory. ViewModelProviders Utility can not create an instance of a ViewModel with argument constructor because it does not know how and what objects to pass in the constructor.
Also, you should follow the Dependency Injection principle. A class should not create dependencies it needs. It should be provided rather than creating.
For Example -
public class LogInViewModel extends ViewModel {
private final LogInRepo repo;
public LogInViewModel (LogInRepo repo) {
/* this.repo = new LogInRepo(); Not recommended, It violates DI principle*/
this.repo = repo;
}
}

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.

Categories

Resources