Can dagger make android activity creation slower? - android

I have property data manager in Activity A and I am instantiating it's value instance in activity A onCreate() through dagger component.
override fun onCreate(savedInstanceState: Bundle?){
datatManager = coreComponent().provideDataManager()
}
My questions(probably a silly ones) are: 1] Does dagger generate the code and instantiate the object when I call it on onCreate()? or Dagger being compile time, it already have all the classes ready behind the scene on which Data Manager has dependency? and just pas me the reference when I need it? 2] Does this make creating/starting an activity any slower?

Dagger generates code during compilation time, so the code itself is "ready", but that doesn' mean class instances are. Dagger creates instances every time you access them by default, unless you use a scoping mechanism such as #Singleton.
If an injected instance is heavy (i.e. does a lot in its constructor) then yes, it can negatively affect your activity creation time.

Related

How can I access my activity's ViewModel from within a service?

I am building an app that needs to detect certain objects in photos. I first load and keep all the photos I'd like to inspect into my room Database and connect it to my view with a ViewHolder.
When the user clicks a button, I want the detection process to begin, and I want to use a jobIntentService for that (As there might be thousands of photos there).
My problem is - how do I access the view holder from within the Service? I need it both to actually get a hold of the files and also so I can update each file's record once detection has been made.
I've tried to ask for the activity as one of the attributes, but I am getting this error
Unable to instantiate service tech.levanter.anyvision.services.DetectJobIntentService: java.lang.InstantiationException: java.lang.Class<tech.levanter.anyvision.services.DetectJobIntentService> has no zero argument constructor
Would appreciate any input, thanks
ViewModels holds particular significance for Activities and Fragments (e.g. they retain data during config changes). So a Service doesn't really need it. Hence you can resolve the issue in one of two ways.
Approach 1:
If your MyViewModel is just a wrapper for accessing LiveData from your Repository class, then you can just use your Repository class inside your Service.
Approach 2:
If your ViewModel is doing more than just wrapping calls to the Repository and you want your Service class to have access to the same logic defined in your ViewModel, then use an intermediate ViewModelContent class. Instead of putting everything in your MyViewModel class, put them in a "ViewModelContent" class. Then use your MyViewModel class as an accessor wrapper around ViewModelContent. Then your Service can instantiate ViewModelContent as you would any other class.
class MyViewModel(application: Application) : AndroidViewModel(application) {
init{
viewModelContent = ViewModelContent(...)
}
}
Approach 1 will usually be cleaner than Approach 2.

onRetainCustomNonConfigurationInstance deprecated in AndroidX

onRetainCustomNonConfigurationInstance was deprecated in AndroidX when the first version got released back in 2018! As the AndroidX Activity library says in the release notes:
onRetainCustomNonConfigurationInstance has been deprecated. Use a ViewModel for storing objects that need to survive configuration changes.
I just want a single object to survive configuration changes, this was the purpose of onRetainCustomNonConfigurationInstance!
For my use case, I want an instance of a Dagger graph to survive configuration changes in an Activity but using AAC ViewModel doesn't feel right to me for that use case. Dagger injects my ViewModels, I don't want to wrap my graph in another ViewModel just for the sake of making it survive configuration changes.
Is there any other way I can make an object survive configuration changes?
Using ViewModel is the most accurate and recommended way to make an object survive configuration changes, you should use it. You could've used onSaveInstanceState but that'd force all the objects needing to support Parcelable, that's not only reasonable, it's sometimes impossible.
To replace onRetainCustomNonConfigurationInstance, you can use the same APIs that support ViewModel in a way that handles all the complexity for you.
You can use this implementation of LongLastingElement API (code here) that uses ViewModel under the hood and removes all the boilerplate code to make an object survive configuration changes.
For your example, to make the Dagger graph survive configuration changes with this API, the code would look like this:
class LoginActivity : AppCompatActivity() {
private lateinit var loginComponent: LoginComponent
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loginComponent = LongLastingElement.getInstance<LoginComponent>(this).getElement {
(applicationContext as MyDaggerApplication).appComponent.loginComponent().create()
}
loginComponent.inject(this)
}
}
LongLastingElement has a static method called getInstance where you specify the type of the object that needs to be stored and you pass a lifecycle owner in. Then, the getElement method is called, it receives a lambda as a parameter that needs to create an instance of the object you want to store.
Since the Lifecycle owner is used to obtain the LongLastingElement instance, the lambda that getElement takes as a parameter will be only called once. It'll create an instance of the object the very first time is called and the same instance will be reused after configuration changes and subsequent calls to getElement. This works for any lifecycle owner such as Activities and Fragments.

How to perform a Dagger 2 Constructor, Method, Field Injection?

I'm practicing dagger 2 for a week now, I just want to know the difference of these injections(constructor, method, field), and where should I use them.
Constructor: whenever you've the possibility to do so (when you have access to the constructor, for example, with your presenters if you use MVP pattern).
Field: when you don't have access to the constructor, for exemple when injecting in your Activity or Fragment.
Method: an #Inject annotated method will be executed by Dagger as soon as the construction call has finished. We usually use it when we want to pass class instance itself (this reference) to injected dependencies.
Read this for more informations and examples of use cases.

Dagger2 sub-component confusion in Android

My Goal :
To understand how scope works and how to Implement a UserScope that I can use over multiple Activities and reset/create a new one as required.
Methods I am using :
This Blog: http://frogermcs.github.io/building-userscope-with-dagger2/
It apparently explains the same thing that i am trying to achieve here.
Official Docs
http://frogermcs.github.io/building-userscope-with-dagger2/
Quick brief on Blog
Obviously, There is UserModule and UserComponent. Author has wrapped the creation of UserComponent under UserManager which has ApplicationScope. So UserManager is available at time of log in. when login is successful UserComponent is initialized via UserManager. Simple logic.
Now this already initialized #UserScope is used in couple of Activities, as you can see in the picture.
What I am struggling to understand
Take a look at UserComponent.
public interface UserComponent {
#Subcomponent.Builder
interface Builder {
Builder sessionModule(UserModule userModule);
UserComponent build();
}
UserDetailsActivityComponent plus(UserDetailsActivityComponent.UserDetailsActivityModule module);
RepositoriesListActivityComponent plus(RepositoriesListActivityComponent.RepositoriesListActivityModule module);
LogoutManager logoutManager();
}
Specifically UserDetailsActivityComponent and RepositoriesListActivityComponent are created through UserComponent. Like this,
#Override
protected void onUserComponentSetup(UserComponent userComponent) {
userComponent.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this)).inject(this);
}
So they first get pre-created in UserComponent through UserManager and then it calls onUserComponentSetup which then creates the appropriate Component and injects the current Activity.
I fail to comprehend with this pattern mentioned above, as I have read in the docs that we use plus(InjectionToBeDoneOn i) when we need the injection on a particular instance of InjectionToBeDoneOn. But why inject this Activity via this Component? What does this accomplish? Wouldn't it make sense to do this the conventional way in onCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)?
Also, I am missing decent material of how UserScope is implemented in Android which has life span from log-in to log-out but not bigger than the #Singleton scope.
we use plus(InjectionToBeDoneOn i) when we need the injection on particular instance of InjectionToBeDoneOn
Not quite. A component has basically 3 kinds of methods
SomeDependency provideDependency() which just creates / provides some dependency to subcomponents, or for manual retrieval (basically a getter)
void inject(MyAndroidFrameworkClass object) that injects an object with its dependencies
SomeSubComponent plus(SubComponentModule module) that creates a subcomponent, adding additional modules
You're mixing up 2. and 3. here.
// user scoped component
userComponent
// create a subcomponent (UserDetailsActivityComponent)
.plus(new UserDetailsActivityComponent.UserDetailsActivityModule(this))
// use the UserDetailsActivityComponent that was just created and inject with it
.inject(this);
UserDetailsActivityComponent is a subcomponent of UserComponent, which is why the userComponent gets extended .plus(somemodule) to create a subcomponent. If your submcomponent does not need additional modules you can also just use .plus() because to Dagger the important thing is the return type or signature in general.
If it returns another component, then it creates a SubComponent.
If it hast one parameter and returns void or the parameters type, then it is an inject method
If it has no parameters and returns some type is is a provides method (1.) to expose some dependency
but why inject this Activity via this Component? What does this accomplish?
If you were to create UserDetailsActivityComponent from scratch, it would only see and know about what it can provide itself. If you have some #Singleton somewhere it could not access any of it, because it is not part of the object graph.
A subcomponent extends another component, adding to the object graph. If you have a #Singleton A and your UserComponentn needs A to provide B, with a subcomponent this will work, without it you will get a cannot be provided error.
Dagger is no magic. It really just builds up a directed graph and checks whether everything is fine. It will complain if some dependencies have cyclic dependencies on one another or if some part of the graph doesn't have access to dependencies it need.
Your UserComponent holds your userdata. For simplicity lets say it holds the UserName. Now UserDetailsActivity might want to display UserName, but it needs some way to get it.
By using the #Singleton AppComponent as a parent you'd have access to some Apis, but not the user scoped ones. You could move the user scoped objects into the #Singleton AppComponent, but then you'd either have to recreate the AppComponent every time the user changes (which kind of defeats the purpose of declaring it #Singleton, or you'd have to find some other means to update / change the user.
If you want to do it the Dagger way, you create a UserComponent that adds the User to the graph. That way subcomponents can access it and do their user things.
When the user changes you have to make sure to destroy any activities / fragments that used the UserComponent, and you just recreate everything—with a new user.
wont it make sense to do in conventional way in OnCreate() of the activity with DaggerXYZComponent().Builder().Build().inject(activity)
You can do that of course. The author just put the calls to app.getAppcomponent().getUserManager().getUserComponent() or something like this into their BaseActivity and BaseUserActivity so that you wouldn't have to repeat the same lines of code every time. This method will basically still be called in onCreate, it just enables you to use the components directly, without fetching them every single time.
You can obviously remove those template methods and inline everything in onCreate, leading to duplicated code, making maintenance harder in the long run.
i am missing decent material of how UserScope is implemented in android which has life span from log-in to log-out but not bigger than #SingleTon scope.
Android doesn't help and it's your job to clean up after yourself. If the user changes you purge everything the UserComponent and its SubComponents touched, and recreate it with the new user.
You will have to store the UserComponent with the current user either in the Application class, some "real" singleton, or some "Manager" in the application component with a #Singleton scope. Every approach has their own benefits I guess.
Scopes just point out to Dagger that one object should exist within a Scope only once. It doesn't matter what you name it, or how many objects are in the Scope. Dagger only needs them to ensure that there are no dependency cycles.

Ensuring only one instance per scope in Dagger2

So, what I am trying to accomplish is to ensure that I have only one instance per scope in Dagger2.
Default Singleton scope already works that way. No matter on how many places you inject same object, let's call it GlobalInstance, method GlobalInstance provideGlobalInstance() that constructs it will be called once and only once.
On the other side, if I define custom scope, for example #SessionScope and inside some SessionModule I make method User provideUser(), that method (and, consequentially, new User() constructor) will be called as many times as I am injecting User. No matter if I use the same module instance every time, User provideUser() is being called for every #Inject User mUser I have in my code, resulting with multiple instances, instead of one scope-limited "singleton".
Is there some clear way to achieve it, using regular Dagger api. One way to do it is to have lazy getters inside the module class, but it is not very clean way to do it.
Please note that #Singleton scope is functionally equivalent to any other custom scope you define.
It means that you could have two flavors of #Provides methods in SessionModule:
#Provides #SessionsScope - provides "session singletons" (more on this later)
#Provides - provides new object on each injection
Please note that the term "singleton" have some ambiguity when we talk about Dagger, therefore I prefere to use term "scoped objects". When scoped object injected with Dagger for the first time, the component caches its instance and returns it on each subsequent injections PERFORMED BY THE SAME COMPONENT.
For more information you can read this post: Android Dagger 2 Scopes Demistified

Categories

Resources