Slide 1

Slide 1 text

A (K)night with Dagger Slicing problems of newbies with DI

Slide 2

Slide 2 text

Why Dagger is important nowadays ?

Slide 3

Slide 3 text

Like you love to watch Thor with his Hammer

Slide 4

Slide 4 text

Interviewers watch for Dagger in you

Slide 5

Slide 5 text

What is Dependency Injection ?

Slide 6

Slide 6 text

Please don’t react this way!

Slide 7

Slide 7 text

Ok, What is Dependency ?

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

And, What is Injection ?

Slide 11

Slide 11 text

To inject something..

Slide 12

Slide 12 text

So, what is dependency injection ?

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

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.

Slide 15

Slide 15 text

So, what does that mean ? You can... ● Write testable code ● Decouple class and its dependency ● Design maintainable and reusable structure

Slide 16

Slide 16 text

Sounds interesting! Okay then..

Slide 17

Slide 17 text

Let’s dive into DI

Slide 18

Slide 18 text

What is Reflection ?

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Example of Reflection ● Packages that provide classes for reflection: ○ java.lang ○ java.lang.reflect ● Class class: ○ MainActivity.class.getSimpleName() returns “MainActivity” using Reflection

Slide 21

Slide 21 text

Let's see, who used Reflection ?

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Sometimes, it may lead to...

Slide 24

Slide 24 text

But, thanks to Google Google undertook this project and made revolutionary enhancements in the underlying mechanism of Dagger1

Slide 25

Slide 25 text

And Dagger2 was born...

Slide 26

Slide 26 text

Let’s Get Started with Dagger 12 Dagger 1 Dagger 2

Slide 27

Slide 27 text

What is Annotation ?

Slide 28

Slide 28 text

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.

Slide 29

Slide 29 text

What do Annotation Processors do ?

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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.

Slide 32

Slide 32 text

Modes of Dependency Injection

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Let’s learn to DI using Dagger2

Slide 36

Slide 36 text

Objective Display List of Users using Random User Api

Slide 37

Slide 37 text

RandomUserApplic ation.java

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Picasso is breaking the rule of testability.

Slide 40

Slide 40 text

Two major problems to be solved.. ● Ideal way of dealing with clumsy initializations ? ● Make our code more testable ?

Slide 41

Slide 41 text

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.

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

Dependency Graph

Slide 44

Slide 44 text

Make sure to add below Dependencies dependencies { implementation 'com.google.dagger:dagger:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11' }

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Let's start with Component

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

But how will Component know from where to get the dependencies of Picasso and RandomUserService ?

Slide 51

Slide 51 text

Modules for dependencies

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

● 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…

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

But how do I pass Context into other modules ?

Slide 62

Slide 62 text

Modules with includes attribute

Slide 63

Slide 63 text

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 {}

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

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..

Slide 66

Slide 66 text

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 { ... }

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

Now, hit gradle build…

Slide 69

Slide 69 text

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.

Slide 70

Slide 70 text

Now, let’s look at our application class…

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

But there’s one problem...

Slide 73

Slide 73 text

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 ?

Slide 74

Slide 74 text

There should be something to control instance creations

Slide 75

Slide 75 text

Limit the instances by Scope

Slide 76

Slide 76 text

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.

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

Typically, we need two type of Contexts: Application and Activity. We can have ActivityModule to provide Activity context.

Slide 79

Slide 79 text

● 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 ?

Slide 80

Slide 80 text

Named Annotations comes in...

Slide 81

Slide 81 text

@Named annotation does this job We can specify @Named(“”) annotation as: In ContextModule: @Provides @RandomUserApplicationScope @Named("application_context") public Context context() { return context.getApplicationContext(); } In ActivityModule: @Provides @RandomUserApplicationScope @Named("activity_context") public Context context() { return context; }

Slide 82

Slide 82 text

Tell Dagger to use Application context as In OkHttpModule: @Provides @RandomUserApplicationScope public File file(@Named("application_context") Context context) {} In PicassoModule: @Provides @RandomUserApplicationScope public Picasso picasso(@Named("application_conte xt") Context context, OkHttp3Downloader okHttp3Downloader) {}

Slide 83

Slide 83 text

Alternative to Named Annotations

Slide 84

Slide 84 text

@Qualifier is of my type Create an interface as: @Qualifier public @interface ApplicationContext { } @Provides @RandomUserApplicationScope @ApplicationContext public Context context() { return context; } Use it in OkHttpModule and PicassoModule: @Provides @RandomUserApplicationScope public File file(@ApplicationContext Context context) {} @Provides @RandomUserApplicationScope public Picasso picasso(@ApplicationContext Context context, OkHttp3Downloader okHttp3Downloader) {}

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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.

Slide 88

Slide 88 text

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.

Slide 89

Slide 89 text

● 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

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

And we have activity-scoped MainActivityModule

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

Now, check changes in Activity and Application class

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

A lot more manageable code base has been established by now... That’s it…???!!!

Slide 97

Slide 97 text

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.

Slide 98

Slide 98 text

You might think.. “Ohh.. I don’t care. I am good at writing utility classes... ”

Slide 99

Slide 99 text

Well, This is not my style… Guess why...

Slide 100

Slide 100 text

@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.

Slide 101

Slide 101 text

Modify MainActivityComponent as ● Remove methods RandomUserAdapter and RandomUserService. ● Instead, create a method with MainActivity as parameter.

Slide 102

Slide 102 text

Add @Inject to fields

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

I can still improve it even more, with Constructor Injection...

Slide 105

Slide 105 text

MainActivity is passed as a constructor parameter...

Slide 106

Slide 106 text

Let’s make MainActivity injectable

Slide 107

Slide 107 text

● In RandomUserAdapter, pass MainActivity as constructor parameter ● Annotate it with @Inject

Slide 108

Slide 108 text

Now, hit gradle build again…

Slide 109

Slide 109 text

Mission Accomplished!!! Link to Dagger 2 Example: https://goo.gl/FsSL7n

Slide 110

Slide 110 text

That’s all with Dagger

Slide 111

Slide 111 text

Any Queries ???

Slide 112

Slide 112 text

“Never say I don’t know it, say I haven’t started yet” - Chintan Soni

Slide 113

Slide 113 text

Chintan Soni Senior Android Developer @ Simform Solutions Pvt. Ltd. Follow me on: FB: chintansoni202 Twitter: @chintansoni202