A K Night With Dagger

Download as pdf or txt
Download as pdf or txt
You are on page 1of 113

A (K)night

with Dagger
Slicing problems of
newbies with DI
Why Dagger is important nowadays ?
Like you love to watch Thor
with his Hammer
Interviewers watch for Dagger in
you
What is Dependency Injection ?
Please don’t react this way!
Ok, What is Dependency ?
Dependency
● Class Black needs object from class Red,

Green and Blue, to fulfil its purpose.

● It implies that class Black is dependent on

Red, Green and Blue.

● So, Dependency is an object that can be

used.
And, What is Injection ?
To inject something..
So, what is dependency injection ?
Dependency Injection
● Injection: Passing of one or more objects
to the depending class
● Dependency Injection: A form of
Inversion of Control, a design principle,
which say that any class should be getting
its dependencies from outside.
● In other words, class should not be
instantiating other class within it, but rather
there should be another configuration
class which can provide with those
instances.
So, what does that mean ? You can...

● Write testable code

● Decouple class and its dependency

● Design maintainable and reusable structure


Sounds interesting! Okay then..
Let’s dive into DI
What is Reflection ?
Reflection...
● Enables the code with ability to examine or modify the Runtime behavior of

class in runtime.

● Java Reflection Api was introduced in Java 1.5

● Drawbacks:

○ Performance Overhead

○ Exposure of Internals

○ Unexpected and Untraceable Crashes due to dependency resolution at runtime


Example of Reflection

● Packages that provide classes for reflection:

○ java.lang

○ java.lang.reflect

● Class class:

○ MainActivity.class.getSimpleName() returns “MainActivity” using Reflection


Let's see, who used Reflection ?
History of Dagger1

● Square Inc.’s Dagger1 is a DI Framework which was purely dependent on

Java Reflection Api

● Typically, you are not gonna use DI for injecting just one dependency

● So, more injections implies more performance overhead

● Also, error tracing was extremely difficult


Sometimes, it may lead to...
But, thanks to Google

Google undertook this project and made revolutionary


enhancements in the underlying mechanism of Dagger1
And Dagger2 was born...
Let’s Get Started with Dagger 12

Dagger 1 Dagger 2
What is Annotation ?
Annotation is...

● A class of metadata, introduced in Java 1.5, that can be associated with

class, methods, fields, and even other annotations.

● Those meta data can be accessed at runtime via Reflection.

● But we were not known to the same capability of reading metadata at compile

time via Annotation Processors.


What do Annotation Processors do ?
Annotation Processors...

● Are code generators that eliminate boilerplate code by generating code for

you at Compile Time. Really..!!

● No performance Overheads at all (Compile Time nature).

● Even you can create your own Annotations and a Processor for it using this

tutorial: The 10 step guide to annotation processing in android studio


How Dagger 2 differs from Dagger 1

● Works purely on Annotation Processor

● “Which client is dependent on what objects, that is resolved at compilation

stage.”

● Hence, Dagger2 is free from Performance Overhead. Also, Dagger2 errors are

highly traceable.
Modes of Dependency Injection
Modes of DI
● Java Specification Request 330 (JSR 330) has defined standard Java

Annotations for describing dependencies of a class.

● Modes of DI:

○ Constructor Injection: Injecting the constructor parameters

○ Field Injection: Injecting the member variable (Required that it must not be private)

○ Method Injection: Injecting the method parameter


By the way… Some Annotation Processors..
Jake Wharton’s ButterKnife is an Android Architecture Component’s Room
Persistence Library is an Annotation Processor
Annotation Processor.
@Entity
@Bind(R.id.textView) public class User {...}
TextView mTextView; @Dao
public interface UserDao {...}

@Database(entities = {User.class}, version = 1)


public abstract class AppDatabase extends
RoomDatabase {...}
Let’s learn to DI using Dagger2
Objective
Display List of Users
using Random User Api
RandomUserApplic
ation.java
But I am really not happy with it.

● Firstly, code is ought to be in serialized in some way. Every objects need to be

initialized in some order.

● Secondly, Code looks very clumsy

● And lastly, Code is not test friendly


Picasso is breaking the rule of testability.
Two major problems to be solved..

● Ideal way of dealing with clumsy initializations ?

● Make our code more testable ?


Initial Setup Towards Dagger 2 implementation

● Identify the classes who secretly depend on objects.

● Provide dependency of such objects through constructor arguments

● Do necessary object creations at one level, let’s say, at Application Level.

● Example: We are gonna provide single point of accessing Picasso

instance. So, class dependent on Picasso, should be having Picasso as

constructor arguments.
Dependency
Graph
Make sure to add below Dependencies

dependencies {

implementation 'com.google.dagger:dagger:2.11'

annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

}
Annotations with Dagger2

● @Module and @Provides: define classes and methods which provide

dependencies

● @Inject: request dependencies. Can be used on a constructor, a field, or a

method

● @Component: enable selected modules and used for performing dependency

injection
Let's start with Component
Component is...

● A public interface to your dependency graph

● Best Practise: Exposes only your top level dependency. Internal dependencies

should remain under the hood.

● Go ahead creating a component RandomUserComponent


@Component: Tells Dagger that this interface is a Dagger Component and not an

ordinary interface. Dagger will generate a subclass of this component with

implementation of getPicasso() and getRandomUserService()


But how will Component know from
where to get the dependencies of
Picasso and RandomUserService ?
Modules for dependencies
Modules...
● provide those under the hood dependencies to the outermost dependency,

like Picasso and RandomUserService.

● Go ahead with moving my code from RandomUserApplication class to these

modules as OkHttpClientModule, PicassoModule,

RandomUserServiceModule.

● @Provides: If any method in module provides dependency, it should be

annotated with @Provides


We are all set with our modules,
But,
PicassoModule and OkHttpClientModule
are incomplete without Context…
Manage External Dependencies

● External Dependencies: Dependencies on which our graph itself is dependent

on.

● Our modules, now, are just lacking with the dependency of Context.

● Go ahead with Creating a ContextModule


● External Dependencies to our graph or module can be of Context, Activity,
Service etc.
● They are external because WE NEVER CREATE CONTEXT / ACTIVITY / SERVICE
INSTANCE BY NEW KEYWORD…
● Remember this…
But how do I pass Context into other
modules ?
Modules with includes attribute
Modules with includes attribute
Includes attribute: includes other module dependency to the current module

So, my module’s @Module annotation got updated with includes attribute as:

@Module(includes = OkHttpClientModule.class)
public class RandomUserServiceModule {}

@Module(includes = OkHttpClientModule.class)
public class PicassoModule {}

@Module(includes = ContextModule.class)
public class OkHttpClientModule {}
By now,
our modules are connected and can
communicate to each other.
We are simply remaining to tell our
Component that
which Modules you need to depend on..
Component annotation has modules attribute

● Modules attribute: provides a way to tell component about its dependent


modules.
● So, my component’s @Component annotation got updated with modules
attribute as:

@Component(modules = {RandomUserServiceModule.class,
PicassoModule.class})
public interface RandomUserApplicationComponent {
...
}
Now, hit gradle build…
And if you are lucky...

● Dagger generates a class with Builder Design Pattern

DaggerRandomUserApplicationComponent which now provides a way to

initialize any component with modules associated with it.

● Magic of Dagger: For a particular component, you only need to pass

modules that depend on external dependencies, you can skip providing a

component with internal dependent modules.


Now, let’s look at our application
class…
But there’s one problem...
Every time you call Component#build()

● Dagger by default, creates new instances of every dependency to be

injected.

● Why Dagger doesn’t understand that I just need a single instance to be

created for Picasso ?

● How do we tell Dagger to create just a single instance for any dependency ?
There should be something to control instance
creations
Limit the instances by Scope
Scope...

● Annotation informs Dagger to share the same instance every time

Component.build() will be called.

● It will make dependencies work as a Singleton for that particular Component.


This is how we will use our customly created Scope:
This is how we use ● Firstly, we have to put it at component level:
@Scope @RandomUserApplicationScope
@Component(modules = {RandomUserServiceModule.class,
@Scope PicassoModule.class})
@Retention(RetentionPolicy.CLA public interface RandomUserApplicationComponent {}
SS)
public @interface ● Then, put it on every method with @Provides, that needs to act
as Singleton. Usually, we need just single instance for
RandomUserApplicationScope {
OkhttpClient, Picasso, etc:
}
@Provides
@RandomUserApplicationScope
public Picasso picasso(Context context, OkHttp3Downloader
okHttp3Downloader) {}

@Provides
@RandomUserApplicationScope
public Retrofit retrofit(OkHttpClient okHttpClient,
GsonConverterFactory gsonConverterFactory) {}
Typically, we need two type of Contexts:
Application and Activity. We can have
ActivityModule to provide Activity
context.
● But Dagger gets confused which Context to use because it has got two modules
that has methods that provide Context and it will throw error on build.
● How can we tell Dagger that this dependency should use Application Context or
Activity Context ?
Named Annotations comes in...
@Named annotation does this job

We can specify @Named(“”) annotation as:

In ContextModule: In ActivityModule:

@Provides @Provides
@RandomUserApplicationScope @RandomUserApplicationScope
@Named("application_context") @Named("activity_context")
public Context context() { public Context context() {
return context.getApplicationContext(); return context;
} }
Tell Dagger to use Application context as

In OkHttpModule: In PicassoModule:

@Provides @Provides
@RandomUserApplicationScope @RandomUserApplicationScope
public File public Picasso
file(@Named("application_context") picasso(@Named("application_conte
Context context) {} xt") Context context,
OkHttp3Downloader
okHttp3Downloader) {}
Alternative to Named Annotations
@Qualifier is of my type
Create an interface as: Use it in OkHttpModule and PicassoModule:

@Qualifier @Provides
public @interface ApplicationContext { @RandomUserApplicationScope
} public File file(@ApplicationContext Context
context) {}
@Provides
@RandomUserApplicationScope @Provides
@ApplicationContext @RandomUserApplicationScope
public Context context() { public Picasso picasso(@ApplicationContext
return context; Context context, OkHttp3Downloader
} okHttp3Downloader) {}
Till now...
● We have created Application Level Dependencies

● But what about some dependencies that we need just on Activity Level ?

● Activity Life Cycle runs separate from Application Life Cycle

● So, dependencies created within Activity should be destroyed with Activity

itself.

● Let’s understand Multiple Components and communication between them


Creating Multiple Components and make them talk

● Best practises: When you are injecting dependencies into clients who have

life cycle different from where dependencies are coming, it's better to

create a separate component and modules for that client.


Firstly, Let’s have activity-level scope
@Scope
public @interface MainActivityScope {
}

● We will apply this scope to component and modules we will create next

● So, let’s have a MainActivityComponent and make it talk to

RandomUserApplicationComponent for dependencies they are missing.


● Dependencies attribute: It tells Dagger that while creating component, if you are

looking for any other dependencies, you can look into components specified in

the dependencies attribute.

● So, now MainActivityComponent can look for dependencies into

RandomUserApplicationComponent
And we have activity-scoped MainActivityModule
Now, check changes in Activity and Application class
A lot more manageable code base has
been established by now...
That’s it…???!!!
What if I have 50 dependencies in your component ?

Would write 50 different lines ?

randomUserService = mainActivityComponent.randomUserService();

randomUserAdapter = mainActivityComponent.randomUserAdapter();

...

Well, I am lazy at this and we are already learning lazy loading of objects.
You might think..

“Ohh..
I don’t care.
I am good at writing utility classes... ”
Well, This is not my style… Guess why...
@Inject is waiting for you...

● @Inject : JSR 330 (Java Specification Requests) provides this annotation

● Instead of telling Dagger, that I want RandomUserAdapter and

RandomUserService instances, let Dagger react upon fields with @Inject

annotations.

● But to use the magic of @Inject, we will need to modify our code a little.
Modify MainActivityComponent as

● Remove methods RandomUserAdapter and RandomUserService.

● Instead, create a method with MainActivity as parameter.


Add @Inject to fields
How will this work ???

● Now, when Dagger finds a method in MainActivityComponent with void as

return type, “Omg… there’s no return type…!!! There must be something I need

to look into this class. Ohh yes, I can see fields annotated with @Inject, let me

initialize them”

● And you are done.. You can run your code


I can still improve it even more, with Constructor
Injection...
MainActivity is passed as a constructor parameter...
Let’s make MainActivity injectable
● In RandomUserAdapter, pass MainActivity as constructor parameter

● Annotate it with @Inject


Now, hit gradle build again…
Mission
Accomplished!!!
Link to Dagger 2 Example:
https://goo.gl/FsSL7n
That’s all with
Dagger
Any Queries ???
“Never say
I don’t know it,
say
I haven’t started yet”

- Chintan Soni
Chintan Soni
Senior Android Developer @
Simform Solutions Pvt. Ltd.

Follow me on:
FB: chintansoni202
Twitter: @chintansoni202

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy