[JAVA] Anyway, Dagger2 summary

〇Introduction

Dagger2 It's difficult. Since the introduction was difficult, I briefly summarized the introduction part for the time being, using various sites as clues. This blog uses @Inject, @Module, @Provide, @Component, I'd like to write as clearly as possible, up to the point of injecting dependencies and switching functions. For some reason, I chose Java this time.

〇What is Dagger2?

"Compilation framework for dependency injection" developed by google To put it simply, if you create an instance of another class in a class, Since it becomes difficult to test each class, Switch to dependency injection, which is a method of creating an instance at compile time and injecting it. To make it easier to maintain and test.

〇Reference URL

・ [Official] GitHub https://github.com/google/dagger ・ [Official] User's Guide https://google.github.io/dagger/users-guide.html ・ Introduction to dagger2 (2.11) that is extremely easy to understand ――It's incredibly easy to understand! https://qiita.com/m-dove/items/767c4bfaeee53caefc4d ・ I understand this (I think I understand)! Dagger 2 --The annotations are summarized in an easy-to-understand manner. https://qiita.com/kikuchy/items/a96809d621845dead8d2 ・ In short, what is DI? ――It wasn't Java, but I used this example as a reference. https://nekogata.hatenablog.com/entry/2014/02/13/073043

〇 Program

・ List of GitHub

[\ 1 ] Sample before using Dagger2 [Sample dependent injection using \ 2 ] @Inject, @Component Sample to switch injection contents using @Module, @Provide

[1] Sample before using Dagger2

This is a simple sample. Create an instance of Fortune Machine with MainActivity. Then, the constructor of FortuneMachine creates an instance of TwiterClient. When you call checkFortune of FortuneMachine, you will get a random fortune. Post to Twitter with postData of TwitterClient. (The Twitter post part is broken, just spit out the log) Just in case, the string is also returned to MainActivity.

MainActivity.java


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FortuneMachine fortuneMachine = new FortuneMachine();

        Log.w( "DEBUG_DATA", "result = " + fortuneMachine.checkFortune());
    }
}

FortuneMachine.java


public class FortuneMachine {
    TwiterClient twiterClient;

    String[] fortunes = {"Daikichi","Nakayoshi","Kokichi","Bad","大Bad"};

    public FortuneMachine(){
        //Create an instance of TwiterClient
        twiterClient = new TwiterClient();
    }

    public String checkFortune(){
        int no = getRandomNo();
        twiterClient.postData(fortunes[no]);
        return fortunes[no];
    }

    public int getRandomNo(){
        Random r = new Random();
        int n = r.nextInt(fortunes.length);

        return n;
    }
}

TwitterClient.java


public class TwiterClient {

    public TwiterClient(){
    }

    public boolean postData(String fortune){

        //Communication processing to Twitter
        Log.w("DEBUG_DATA","postData " + fortune);

        return true;
    }
}

With this program, every time you test Fortune Machine, data will be posted by Twitter Client, and you will be careful when expanding. So let's use Dagger2 to inject the Twitter Client into the Fortune Machine.

[2] Dependency injection sample using @Inject, @Component

[app]build.gradle


    implementation "com.google.dagger:dagger:2.11"
    implementation "com.google.dagger:dagger-android-support:2.11"
    annotationProcessor "com.google.dagger:dagger-android-processor:2.11"
    annotationProcessor "com.google.dagger:dagger-compiler:2.11"

MainActivity.java


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //FortuneMachine fortuneMachine = new FortuneMachine();
        Machine fortuneMachine = DaggerMainActivity_Machine.create();
        String result = fortuneMachine.maker().checkFortune();

        Log.w( "DEBUG_DATA", "result = " + result);
    }

    //Describe the dependent objects in Dagger2
    @Component
    interface Machine{
        FortuneMachine maker();
    }
}

FortuneMachine.java


public class FortuneMachine {

    //This is the same treatment as New
    @Inject
    TwiterClient twiterClient;

    String[] fortunes = {"Daikichi","Nakayoshi","Kokichi","Bad","大Bad"};

    //Also required for the constructor
    @Inject
    public FortuneMachine(){
        // twiterClient = new TwiterClient();
    }

    public String checkFortune(){
        int no = getRandomNo();
        twiterClient.postData(fortunes[no]);
        return fortunes[no];
    }

    public int getRandomNo(){
        Random r = new Random();
        int n = r.nextInt(fortunes.length);

        return n;
    }
}

TwitterClient.java


public class TwitterClient {

    //Also required for the constructor
    @Inject
    public TwitterClient(){
    }

    public boolean postData(String fortune){

        //Communication processing to Twitter
        Log.w("DEBUG_DATA","postData " + fortune);

        return true;
    }
}

・ Change 1

Added settings to build.gradle to use Dagger2.

・ Change 2

The part that creates an instance of TwiterClient in FortuneMachine is ------------------------------- @Inject TwiterClient twitter ------------------------------- It has become. The FortuneMachine and TwitterClient constructors also have @Inject.

It is a rule attached to the variable of the instance you want to create by dependency injection and the constructor placed under control. (Complicated rules at this point)

・ Change 3

In Main Activity ------------------------------- @Component interface Machine{     FortuneMachine maker(); } ------------------------------- Has been added Instance creation ------------------------------- Machine fortuneMachine = DaggerMainActivity_Machine.create(); ------------------------------- It has changed to.

@Component is a place to put all the dependencies under Dagger2 management. (Comprehensive management?) You can change maker () freely, but you have to remember the format. Once you write this and compile it, DaggerMainActivity_Machine implements MainActivity.Machine A file with the class written will appear. (Details are described in \ [Introduction to dagger2 (2.11) which is very easy to understand] in the reference URL.) You have to remember the rule named Dagger [class name] _ [interface name] that is automatically generated ...

To get that instance, add .create ().

Now, the Twitter Client is not new in Fortune Machine, The externally instantiated one is supposed to be injected into the Fortune Machine.

As you can see by doing so far, there is almost no merit in spite of the troublesomeness! !! However, let's study it because it will be convenient if we develop it from now on: frog:

[3] Sample to switch injection contents using @Module, @Provide

What I want to do is that you can control what you inject and set whether to upload to Twitter or Facebook without having to mess with Fortune Machine.

To put it briefly at the beginning, I created some Interfaces and implemented them. Let's manage the contents in a class called FortuneModuel and teach it to @Component, who is in general management.

[app]build.gradle


Same as above

MainActivity.java


public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //FortuneMachine fortuneMachine = new FortuneMachine();
        Machine fortuneMachine;
        // fortuneMachine = DaggerMainActivity_Machine.create();
        fortuneMachine = DaggerMainActivity_Machine.builder()
                .fortuneModule(new FortuneModule())
                .build();
        String result = fortuneMachine.maker().checkFortune();

        Log.w( "DEBUG_DATA", "result = " + result);
    }

    //Describe the dependent objects in Dagger2
    //Describe the module to be used
    @Component(modules = FortuneModule.class)
    interface Machine{
        FortuneMachine maker();
    }
}

FortuneMachine.java


public class FortuneMachine {

    //This is the same treatment as New
    @Inject
    Client clinet;

    String[] fortunes = {"Daikichi","Nakayoshi","Kokichi","Bad","大Bad"};

    //Also required for the constructor
    @Inject
    public FortuneMachine(){
    }

    public String checkFortune(){
        int no = getRandomNo();
        clinet.postData(fortunes[no]);
        return fortunes[no];
    }

    public int getRandomNo(){
        Random r = new Random();
        int n = r.nextInt(fortunes.length);

        return n;
    }
}

Client.java


public interface Client {
    boolean postData(String fortune);
}

TwitterClient.java


public class TwitterClient implements Client{

    //Also required for the constructor
    @Inject
    public TwitterClient(){
    }
    public boolean postData(String fortune){

        //Communication processing to Twitter
        Log.w("DEBUG_DATA","postTwitter " + fortune);

        return true;
    }

}

FacebookClient.java


public class FacebookClient implements Client {
    
    //Also required for the constructor
    @Inject
    public FacebookClient(){
    }

    public boolean postData(String fortune){

        //Communication processing to Facebook
        Log.w("DEBUG_DATA","postFacebook" + fortune);

        return true;
    }
}

FortuneModule.java


//Class that provides what is injected
// ...Name it Module
@Module
public class FortuneModule {

    //Provide what is injected
    // provide...Name it
    @Provides
    static Client provideClient(){
        return new FacebookClient();
    }
}

・ Change 1

Create a public interface Client. TwitterClient and FacebookClient inherited this, You can define @Inject of FortuneMachine in Clinet and receive the value.

・ Change 2

Create a public class Fortune Module. @Provides is attached to the function that manages what is provided to @Inject. @Module is attached to the class that puts @Provides together. In the reference URL \ [I understand this (I think I understand)! Dagger 2 ] ---------------------------------------- If @Inject is a car refueling port, @Module is a gas station and @Provides is a refueling machine installed on the stand. ---------------------------------------- The analogy is excellent.

・ Change 3

Module settings have been added to @Component of MainActivity. ---------------------------------------- @Component(modules = FortuneModule.class) ---------------------------------------- Also, the method of creating an instance of DaggerMainActivity_Machine has been changed to the module specified version. ---------------------------------------- fortuneMachine = DaggerMainActivity_Machine.builder() .fortuneModule(new FortuneModule()) .build(); ---------------------------------------- There is a function called fortuneModule casually, but I wonder if it is automatically generated.

Now, just change the value of FoturneModule You can set which Client to use, set Moc, and test.

〇Summary

To be clear, there are too many manners and mystery rules. There is almost no advantage in using this kind of source. You should switch with an if statement as much as switching instances! ?? However, Google's sample source comes with Dagger, so I can't help but understand it ... It seems convenient to use it with ViewModel, so this time.

Recommended Posts

Anyway, Dagger2 summary
Object-oriented summary
ransack summary