Dagger2 and Android

I'm trying to inject Injection Dependency Injection into my application, but it's hard for me to understand how this works, especially from Spring, where the DI was much simpler and much more declarative.

What I want to do is have a bunch of injection-ready objects that can be used throughout my application, i.e. SharedPreferences, Network objects (OkHttp, Retrofit, Picasso ...) and EventBus and a SchedulerProvider object for RxJava.

This example seems to offer everything I need, but I am having trouble understanding some of the concepts.

In this other example , indicated on the previous page, they create a GithubService that uses the Retrofit object provided in NetModule. To do this, they create a GithubComponent as follows:

@UserScope @Component(dependencies = NetComponent.class, modules = GitHubModule.class) public interface GitHubComponent { void inject(MainActivity activity); } 

They use the UserScope annotation, which defines its own area. Since @Singleton cannot be used, does this mean that the object will not be Singleton? How do areas really affect DI? It seems they only declare a named volume with no effect, but I'm not sure.

In addition, my application is built using Fragment Actions. Should I create a component for each fragment in my application? those. I need to use my REST api services throughout the application, do I need to declare a component for each screen using them? This increases the amount of template code required and therefore does not sound very clean.

+4
android dagger-2
Nov 11 '16 at 9:40
source share
2 answers

Components should be a "large DI provider" that provides everything for a specific area.

For example, you might have a SingletonComponent area with an @Singleton tag, in which each individual module that has at least one @Singleton provider method has been @Singleton .

 @Singleton @Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class}) public interface SingletonComponent { // provision methods OkHttpClient okHttpClient(); RealmHolder realmHolder(); // etc. } 

In the module, submission methods can be defined.

 public interface DatabaseComponent { RealmHolder realmHolder(); } public interface NetworkingComponent{ OkHttpClient okHttpClient(); } 

In this case you will have

 @Singleton @Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class}) public interface SingletonComponent extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent { // provision methods inherited } 






In the module, you can specify a factory method (the "provider method") that indicates how to create a specific type of dependency.

For example,

 @Module public class NetworkingModule { @Provides @Singleton OkHttpClient okHttpClient() { return new OkHttpClient.Builder()./*...*/.build(); } @Provides @Singleton Retrofit retrofit(OkHttpClient okHttpClient) { // ... } } 

You can think of the @Singleton as a large DI container that Spring would provide you with.




You can also provide class instances using the annotated @Inject . This can get any class from a component that can instantiate it from provider methods within these modules with a scope (and, of course, regardless of conditions).

 @Singleton public class MyMapper { @Inject public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo } } 

or

 @Singleton public class MyMapper { @Inject RealmHolder realmHolder; @Inject OkHttpClient okHttpClient; @Inject public MyMapper() { } } 

Then it will be available in the component, you can even make a provisioning method for it so that it is inherited in the component dependencies:

 @Singleton @Component(modules={...}) public interface SingletonComponent { MyMapper myMapper(); } 






With Dagger2, you can also create “copied components” that inherit all the dependencies provided by a component in a given area.

For example, you can inherit all @Singleton components, but still you still have new dependent scopes for the new scope, for example @ActivityScope .

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } 

You can then create sub-copied components using either subcomponents or component dependencies.




  • Subcomponent:

.

 @ActivityScope @Subcomponent(modules={MainActivityModule.class}) public interface MainActivityComponent { MainPresenter mainPresenter(); } 

This can then be created in a component with parent scope:

 @Singleton @Component(modules={...}) public interface SingletonComponent { MainActivityComponent mainActivityComponent(MainActivityModule module); } 

Then you can use the singleton component to create it:

 SingletonComponent singletonComponent = DaggerSingletonComponent.create(); MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder)); 



  • Component Dependency:

.

 @ActivityScope @Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class}) public interface MainActivityComponent extends SingletonComponent { MainPresenter mainPresenter(); } 

For this to work, you must specify the rendering methods in the super-copied component.

Then you can create an instance like this:

 SingletonComponent singletonComponent = DaggerSingletonComponent.create(); MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder() .singletonComponent(singletonComponent) .mainActivityModule(new MainActivityModule(mainActivityHolder)) .build(); 






In Dagger2, you can get dependencies either through:

  • @Inject annotated constructor options
  • @Inject annotated fields in classes with @Inject annotated constructor
  • from @Component provisioning methods
  • using the manual input method in the field defined in the component (for classes that cannot be created using the constructor with @Inject annotation)

Manually enabling classes, such as MainActivity , can be done manually.

Manual entry only introduces the specific class that you enter. Base classes are not automatically entered; they must call .inject(this) on the component.

It works as follows:

 @ActivityScope @Subcomponent(modules={MainActivityModule.class}) public interface MainActivityComponent { void inject(MainActivity mainActivity); } 

Then you can do:

 public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder() .singletonComponent(getSingletonComponent()) .mainActivityModule(new MainActivityModule(this)) .build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance` mainActivityComponent.inject(this); } } 
+6
Nov 11 '16 at 10:36
source share

one)

Since @Singleton cannot be used, does this mean that the object will not be a singleton?

The GitHubComponent component has the @UserScope scope, it must be @Singleton if you want to declare Singletons in this scope.

2)

How do regions really affect DI? It seems they only declare named-scope without effect, but I'm not sure.

From javax docs

Area annotations apply to a class that contains an injection constructor and defines how the injector reuses type instances. By default, if there is no annotation, the injector creates an instance (by entering a type constructor), uses the instance for one injection, and then forgets about it. If there is an annotation for the scope, the injector can save the instance for possible reuse in a later injection.

3)

Do I need to create a component for each fragment in my application?

You can create it once, create an instance and save it in your application, and then request it every time you need to inject something, if your fragment or activity.

Check this example.

0
Nov 11 '16 at 10:26
source share



All Articles