Referencing the Activity inside its module - android

How do I use the new AndroidInjector.inject and still be able to provide an Activity instance inside an Activity Module? The Dagger docs don`t make it clear how to archive this.
The use case is the following: I have an Activity Module which provides a Presenter to my Activity, but the Presenter needs a reference to the Activity.
I used to have something like
#Inject Presenter presenter;
public onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((CustomApplication) getApplicationContext())
.getAppComponent()
.plus(new ActivityModule(this));
}
Can someone can point me to a sample that uses AndroidInjector.inject(this) instead and allow the reference of the Activity inside the Dagger 2 module?

Check Dagger 2 Github issue 615
The instance of your Activity is automatically provided, just pass it as a parameter in your module methods.
Example:
#Provides
#ActivityScope
public providePresenter(ActivityA activity) {
return new PresenterA(activity);
}
You'll now be able to abstract simple modules. Your presenter can be constructor injected too.
This actually cutout a lot of code from all my modules.

Related

Is Dagger component bound to the object that instantiate it?

I have been using Dagger 2 in my project. I understand that the lifetime of scoped object is the same as the lifetime of component (with the same scope). What about the lifetime of component then?
For example, I have a component:
#MyApp
#Component(modules = {
ApplicationModule.class})
public interface ApplicationComponent {
// Injection methods
void inject(MyApplication mainApplication);
}
I build component by:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
buildApplicationComponent();
mApplicationComponent.inject(this);
}
private void buildApplicationComponent() {
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
}
Currently, the code to build component is in Application class. But
is it so that if I build component in Fragment the ApplicationComponent would have the same lifetime as the fragment & if I execute it in Application class the component would have the same lifetime as the whole application? Or how the lifetime of component is defined?
It is plain Java. You have object no matter where. The object provides you dependencies. If you use the same instance you will have the same dependencies or basically the same "Scope/lifetime". If you create somewhere new object it means new other object\dependencies it can provide so "another Scope/lifetime". If you share the object between different objects (You create it in the Application class, but reuse it in another fragment) you are in the "same Scope/lifetime".
But in general I see only "old way" of using Dagger 2 here. This is how your classes should end up everywhere. No need to find reference to any Components and etc or try to instantiate them on your own or clear them on your own in Fragments or Actvities. You have some code for Dagger 2 in one place and then some Annotations in you classes "which do the real work of your project". No need for complicated "connection" between the two parts and I see here a lot of cases like this which are part of tutorials that date back to 2016-2017...
class Repostory #Inject constructor(
private val dependency1: Dependency1
) {}
class Activity or Fragment {
#Inject lateinit var dependency2: Dependency2
}
This is a great example for right usage of Dagger2.
https://github.com/google/iosched
Here is an article about the app:
https://medium.com/#JoseAlcerreca
Jose Alcerreca is one of the Google Lead Devs responsible for creating the guidelines to write Android apps.
#Module
abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
}
Just try to compile something like this. There is the exaxt same class in the app I gave you. Check the generated code and also the docs. If you see ContributesAndroidInjector there is explained:
Generates an {#link AndroidInjector} for the return type of this method. The injector is
implemented with a {#link dagger.Subcomponent} and will be a child of the {#link dagger.Module}'s component.
If you check the AndroidInjector docs you will see that it is the one that is injectin the Activity. And there it will be the implementation:
#Subcomponent(modules = {MainActivityModule.class})
#ActivityScoped
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
So in general these annotations do the magic for you when a new activity starts. They create the Subcomponent, they use it and when the Activity Lifecycle ends the Subcomponent is also dead. Dagger clears the reference. Dagger knows how Activities work or Fragments and etc. There was the old way when you craete the component in the Activity, use it, call inject on your own. Or put some component in the AppClass and then clear it on your onw. But now there is a bigger amount of annotations.

#ContributesAndroidInjector on a activity that extends CordovaActivity

According to Dagger's doc I can use ContributesAndroidInjector on a concrete Android framework type. I will not be able to extend DaggerActivity as I have a hybrid app and in my case and I am using my activity by extending CordovaActivity. What should be the approach in these scenarios?
Should I fall back to pre 2.10 way of registering the Activity to be injected by Dagger as below on an activity's onCreate().
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication())
.getAppComponent()
.inject(this);
}
By doing this I have to rewrite my Application class to return appComponent which again defeats the purpose of DI.
Can you please guide me in the right path providing some inputs to handle this scenario.

Multiple injection across same activity lifecycle results in multiple instances

There is this injector for the MainActivity defined as below in a module that is referenced by the ApplicationComponent:
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity mainActivityInjector();
and the MainActivityModule referenced by the contributor looks like this:
#Module
public class MainActivityModule {
#Provides
#PerActivity
public MyActivityDependency myActivityDependency() {
return new MyActivityDependency();
}
}
and the MainActivity itself is:
public class MainActivity extends AppCompatActivity {
#Inject
MyActivityDependency myActivityDependency;
#Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
Log.d(myActivityDependency.hashCode());
AndroidInjection.inject(this);
Log.d(myActivityDependency.hashCode());
...
}
The #PerActivity Scope is supposed to preserve Activity’s dependency instances throughout its lifecycle.
This basically means that if I perform injection (AndroidInjection.inject(this)) multiple times, I am entitled to get the same injected instance (at least that's the goal).
In that case, why different instances of the MyDependency is injected each time the “.inject()” method is called?
The #PerActivity Scope is supposed to preserve Activity’s dependency instances throughout its lifecycle.
And it does. It creates an annotated dependency in a single component only once.
AndroidInjection is just a helper class that knows how to build the component for your Activity / Fragment. It does not store or persist it. Hence...
AndroidInjection.inject(this);
will create a new component every time it is called and then inject the dependencies. It is not supposed to be called multiple times, and why would you anyways? Just call it once in onCreate and everything will work fine.
In the case that you want to inject twice, you can inject the Activities component itself, and then use the component to inject again. Doing this, using the same component, you should get the same objects every time.
#Inject
DoubleInjectActivityComponent component;
Just inject it like you would any other dependency.

Dagger 2 - how to avoid code repetition for injecting dependencies that require activity context

The project I'm working on has a number of utility classes that require activity context.
I don't want to have to declare a new #Provides method for each activity that uses the dependency. i.e. I don't want this:
#Provides
static Navigator providesNavigator(ActivityOne activity) {
returns new Navigator(activity);
}
// ...and in another module
#Provides
static Navigator providesNavigator(ActivityTwo activity) {
returns new Navigator(activity);
}
So instead I declare these utilities in a single ActivityUtilitiesModule and pass our BaseActivity which all other activities extend. Now i don't have to declare my Navigator dependency x number of times.
#Provides
static Navigator(BaseActivity activity) {
return new Navigator(activity);
}
However, Dagger does not know how to satisfy the dependency for BaseActivity. This means for every Activity i need to create a provides method that will satisfy the BaseActivity dependency with the specific Activity being used. e.g.:
#Provides
static BaseActivity providesBaseActivity(ActivityOne activity) {
return activity;
}
This is better - I only need to repeat this one provider per activity, rather than repeating a provider for every utility class per activity, but it still feels like an unwelcome additional step in Dagger set up, and another thing that makes the code harder to understand.
Is there a pattern which allows me to avoid having to supply this BaseActivity provider per activity?
Please use Constructor Injection. Having provide* methods that only call the constructor are only noise and code to maintain. Add #Inject to the class constructor and possible scopes onto the class.
#SomePossibleScope
class Navigator {
#Inject
Navigator(Activity activity) { }
}
If you do this, you probably don't need your ActivityUtilitiesModule at all.
If your class depends on an Activity or BaseActivity then you need to provide it. And yes, you will have to tell Dagger about it in some way.
If you were to use an abstract class or interface you should make use of #Binds instead.
#Binds BaseActivity bindsBaseActivity(ActivityOne activity);
Compared to #Provides Dagger might optimize this code further, reducing the number of method calls and object creations, as well as a few less lines of code.
I don't know why your Utils depend on the Activity, but if they would only need a Context then you could also just provide the application context to them without a need to bind or provide your actual Activity.
I personally just bind the current Activity to the types it implements using the syntax above. And if you're using Constructor Injection properly that's more often than not the only lines of code that you'll find in my modules, making them very readable.

dagger2 activityscope providing same object of injected dependencies with different activities

Following code creates & shares same object of injected dependencies(ViewsApiEnd ) with different activities rather than creating different per activity.
#ActivityScope
#Component(dependencies = HttpComponent.class, modules = ViewsApiModule.class)
public interface ViewsApiComponent {
void inject(MainActivity activity);
void inject(SecondActivity activity2);
}
module:
#Module
public class ViewsApiModule {
#Provides
#ActivityScope
public ViewsApiEnd providesGitHubInterface(Retrofit retrofit) {
return retrofit.create(ViewsApiEnd.class);
}
}
Scope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope{
}
Full Source: Github(If anyone could fork & correct scope issue it'll be helpful. Also checke void logInstances() for verifying object creation )
How should I declare ViewsApiComponent if I want dagger2 to create different objects of injected deps( ViewsApiEnd) for different activities(MainActivity , SecondActivity )?
I'm assuming by different object you mean different instances and not different implementations.
The key to this is scopes and how you manage your components. In particular ViewsApiComponent.
Let's start with scopes. A scope in dagger is nothing but an annotation that tells dagger that during the life of a given component, as long as an object annotated with a given scope is required, the instance provided will always be the same. In other words, within the same component scoped dependencies will be singleton.
In your code, this ActivityScope is the scope tied to ViewsApiEnd. Now what people usually don't get about scopes is the first part of - "... as long as the component is alive scoped dependencies are singleton". Basically this says, if your component's instance remains the same, then all your scoped instances will be the same.
In your code this is the case because you pin mViewsApiComponent to the application class and you never recreate it. So in your activities when you do this:
// ...
((MyApp) getApplication()).getGitHubComponent().inject(this);
// ...
You're always using the same component, hence same scopes, hence same instance of ViewsApiEnd.
You need to make sure the scoped dependencies have a proper scoped handling. If you want a dependency that is different per activity, then you want to manage the component that takes care of this dependency on a per activity basis.
There's different ways of doing this. I think the simplest one in your case is to move the creation of ViewsApiComponent to your activities. Make sure you nullify the component in onDestroy. Something like this:
private ViewsApiComponent mViewsApiComponent;
#Override
public void onCreate() {
super.onCreate();
getComponent().inject(this);
}
#Overrides
public void onDestroy() {
super.onDestroy();
mViewsApiComponent = null;
}
private ViewsApiComponent getComponent() {
if (mViewsApiComponent == null) {
mViewsApiComponent = DaggerViewsApiComponent.builder()
.httpComponent(((MyApp) getApplication()).getNetComponent())
.build();
}
return mViewsApiComponent;
}
You can even put this code in a base activity and inherit from it in each activity.
Important thing to notice is that the component is now managed by the activity, or better put it's tied to its life-cycle. This ensures the component lives during the life time of the activity and all scoped dependencies are the same within that activity. For another activity a new component will be created and the new object instances will be used, but within the activity they will remain the same.
Now I've explained all of this just for the sake of trying to help you out with components and scopes, but truth is that what you're currently providing in the component's modules are all objects that should indeed be singleton throughout the app's life. So in fact, the way you have it now is the most correct approach at least in my opinion.
Hope this helps

Categories

Resources