Dagger2使用指北

Dagger2是正方形公司推出的一款依赖注入的框架,第二代由谷歌接手,在Android届可是大名鼎鼎的难学,我也是反复看了一遍又一遍才稍微有点头绪,本文就是Dagger2的使用指北,包括后面如何集成到项目中,也尽量写下来。

1.引入

    implementation 'com.google.dagger:dagger:2.16'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

2.Dagger2的作用是什么呢?

我们来看这样一个例子,假如此时有一个Apple类,要在MainActivity中使用该类,传统的方法,是在MainActivity类中new出这个对象,如下:

public class MainActivity extends AppCompatActivity {
    //使用
    Apple apple;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     
        apple = new Apple(20);
        Log.i("我是一个苹果","");
    }
}

但是使用Dagger2,就不再需要new出Apple类,直接注入即可。如下:

public class MainActivity extends AppCompatActivity {
    //使用
    @Inject
    Apple apple;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注入
        DaggerGangJingComponent.builder().appleModule(new AppleModule(90))
                .build().inject(this);
              Log.i("我是一个苹果","");
    }
}

那么我们现在就是需要在项目中使用这个Apple类,怎么来使用Dagger2来创建呢?有哪些创建方式呢?后文会详细解释清楚。

1.最简单不带Module的inject方式

由我们自己定义的类,我们可以自由修改的前提下使用这种方式,分为两种:带参和不带参。

构造函数不带参的类

  1. 由类提供:
//第一,将我们需要注入的对象的类的构造参数使用@Inject标注,告诉dagger2它可以实例化这个类;
public class Apple {
    @Inject
    public Apple() {
    }
}

2.由component连接

//第二,编写Component接口使用@Component进行标注,里面的void inject()的参数表示要将依赖注入到的目标位置;
@Component
public interface AppleComponent {
    void inject(MainActivity activity);
}

3.使用Apple

//
public class MainActivity extends AppCompatActivity {
    @Inject
    public Apple apple;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//第三,使用android studio的Build菜单编译一下项目,使它自动生成我们编写的Component所对应的类,生成的类的名字的格式为 "Dagger+我们所定义的Component的名字";

//第四,在需要注入的类中使用@Inject标注要注入的变量;然后调用自动生成的Component类的方法create()或builder().build(),然后inject到当前类;在这之后就可以使用这个@Inject标注的变量了。
        DaggerAppleComponent.create().inject(this);

        Log.i("苹果",apple+"");

    }
}

构造函数带参数的类,参数为引用类型
如果参数是基本类型呢?

  1. 假如Apple中带有一个Kinfe类的参数如下:
public class Apple {
    
    Knife knife;
//Apple的构造方法使用@Inject标注,告诉Dagger2可以实例化这个类
    @Inject
    public Apple(Knife knife) {
      this.knife = knife;
    }
}

2.参数Knife的构造方法也需要提供注入,由@Inject标记,告诉Dagger2可以实例化这个类。

public class Knife {

    @Inject
    public Knife() {

    }
}

3.使用component连接二者:类和要注入该类的目标位置。inject的参数就是要把该类注入到的目标位置。

@Component
public interface AppleComponent {
    void inject(MainActivity activity);
}

4.Bulid一下项目,生成一个DaggerAppleComponent,通过该类使用注入的对象。

public class MainActivity extends AppCompatActivity {
    @Inject
    public Apple apple;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//注入Apple
        DaggerAppleComponent.create().inject(this);
//使用Apple
        Log.i("苹果",apple+","+apple.knife);

    }

2.带Module的Inject方式

在不能更改构造函数的情况下(比如引入第三方类OkHttpClient等),我们无法在构造函数上添加@Inject标记,就只能使用Module来实现类的提供。

构造方法不带参数

1.假如我们的Apple类是由第三方提供的,我们不可以更改其代码。

public class Apple {
    Knife knife;
    public Apple() {
        
    }

    public Knife getKnife() {
        return knife;
    }

    public void setKnife(Knife knife) {
        this.knife = knife;
    }
}

2.编写Module类,并且使用@Module标注,在该类中提供我们需要inject的对象,提供该对象的方法使用@Provides标注

@Module
public class AppleModule {

    @Provides
    public Apple providesApple(){
        return new Apple();
    }
}

3.在component接口中,使用modules = xxxModule.class链接上一步编写的Module类。

@Component(modules = AppleModule.class)
public interface AppleComponent {
    void inject(MainActivity activity);
}

4.依然是在目标位置注入该对象,并使用

public class MainActivity extends AppCompatActivity {
    @Inject
    public Apple apple;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerAppleComponent.create().inject(this);
        Log.i("苹果",apple+"");
    }
}

构造函数带参数

1.编写Module类,使用@Module标记,并且在Module的构造方法中传入参数,参数和对象一样,都需要写个方法提供出来。

@Module
public class AppleModule {

    String name;

    public AppleModule(String name) {
        this.name = name;
    }

    @Provides
    public Apple providesApple(){
        return new Apple(name);
    }

    @Provides
    public String providesName(){
        return name;
    }
}

2.component接口写法同上

@Component(modules = AppleModule.class)
public interface AppleComponent {
    void inject(MainActivity activity);
}

3.build一下,生成DaggerXXXModule类,使用该类的build方法并传入参数来注入对象。

public class MainActivity extends AppCompatActivity {
    @Inject
    public Apple apple;

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

        DaggerAppleComponent.builder().appleModule(new AppleModule("蓝色的苹果"))
                .build().inject(this);

        Log.i("苹果",apple+"");
    }
}

@Qualifier 自定义注解

现在有这样的情况,如果一个类,有两个构造方法,一个带参,一个不带参,如果要同时使用带参和不带参的对象,那么怎么来区分呢?

1. 使用@Named注解进行标记。

使用该类型注解(@Named或者@Qualifier),不能直接在构造函数上标记,否则会报错误:

@Qualifier annotations are not allowed on @Inject constructors.

在Module类中,使用@Named注解标记:

@Module
public class AppleModule {

    String name;

    public AppleModule(String name) {
        this.name = name;
    }

    @Provides
    @Named("normal")
    public Apple providesApple(){
        return new Apple();
    }
    @Provides
    @Named("named")
    public Apple providesNamedApple(){
        return new Apple(name);
    }

    @Provides
    public String providesName(){
        return name;
    }
}

2.使用时需要使用@Named进行区分。

public class MainActivity extends AppCompatActivity {
    @Inject
    @Named("normal")
    public Apple apple;
    @Inject
    @Named("named")
    public Apple apple1;

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

        DaggerAppleComponent.builder().appleModule(new AppleModule("红色")).build()
                .inject(this);
        Log.i("苹果",apple+"");
        Log.i("苹果1",apple1+""+apple1.name);
    }
}

3.其它创建Component接口的代码均相同。

2.使用@Qualifier自定义注解

1.创建一个注解类 命名为Type,并用@Qualifier注解标记,需要指明是运行时注解。

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
    String value() default "";
}

2.把上一步中的Named全部换成自己定义的Type即可。
在Module中:

@Module
public class AppleModule {

    String name;

    public AppleModule(String name) {
        this.name = name;
    }

    @Provides
    @Type("normal")
    public Apple providesApple(){
        return new Apple();
    }
    @Provides
    @Type("named")
    public Apple providesNamedApple(){
        return new Apple(name);
    }

    @Provides
    public String providesName(){
        return name;
    }
}

使用时:

public class MainActivity extends AppCompatActivity {
   @Inject
   @Type("normal")
   public Apple apple;
   @Inject
   @Type("named")
   public Apple apple1;

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

       DaggerAppleComponent.builder().appleModule(new AppleModule("红色")).build()
               .inject(this);
       Log.i("苹果",apple+"");
       Log.i("苹果1",apple1+""+apple1.name);
   }
}

Singleton 单例

怎样使用Dagger2创建单例对象呢?

无Module方式

1.在对象类上使用@Singleton标记

@Singleton
public class Apple {
    @Inject
    public Apple() {

    }
}

2.使用@Singleton标记component接口

@Singleton
@Component
public interface AppleComponent {
    void inject(MainActivity activity);
}

3.注入并使用Apple对象

public class MainActivity extends AppCompatActivity {
    @Inject
    public Apple apple;
    @Inject
    public Apple apple1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerAppleComponent.create().inject(this);
        Log.i("苹果",apple+"");
        Log.i("苹果1",apple1+"");
    }
}

打印结果如下:


image.png

有Module方式

出现Module类的初衷是对象类不可更改,因此,有Module类时,不需要对对象类进行任何的更改,使用单例也只需要在Module类中进行更改即可。
1.Module类中提供对象的方法上,使用@Singleton标记。

@Module
public class AppleModule {

    String name;

    public AppleModule(String name) {
        this.name = name;
    }
    @Singleton
    @Provides
    public Apple providesApple(){
        return new Apple();
    }

    @Provides
    public String providesName(){
        return name;
    }
}

2.其他代码相同:包括使用@Singleton标记component接口,注入并使用对象。

@Scope 作用域

在作用域内单例。比如有三个Activity,只在前两个Activity中实现单例。
1.创建自定义注解,使用@Scope注解标记,并标明是运行时注解。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {

}

2.Module 和 component都是用自定义的@PerActivity进行标记。

Module:
@Module
public class AppleModule {
    @Provides
    @PerActivity
    public Apple providesApple(){
        return new Apple();
    }
}

Component:
@PerActivity
@Component(modules = AppleModule.class)
public interface AppleComponent {
    void inject(MainActivity activity);
    void inject(Main2Activity activity);
}

3.光使用注解划定作用域还是不够的,还需要创建一个Application,在其中获取获取AppleComponent对象。

public class AppleApplication extends Application {
   
    private AppleComponent appleComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        // 获取AppleComponent对象
        appleComponent = DaggerAppleComponent.builder().appleModule(new AppleModule())
                .build();
    }
    //提供外界获取AppleComponent的方法
    public AppleComponent getAppleComponent() {
        return appleComponent;
    }
}

4.在需要使用单例Apple的位置获取AppleComponent对象,并且注入
MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Inject
    public Apple apple;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ((AppleApplication)getApplication()).getAppleComponent().inject(this);

        Log.i("Main 苹果", "onCreate: "+apple);

        findViewById(R.id.bn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this,Main2Activity.class));
            }
        });
    }
}

Main2Activity.java

public class Main2Activity extends AppCompatActivity {
    
    @Inject
    public Apple apple;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        ((AppleApplication)getApplication()).getAppleComponent().inject(this);
        Log.i("Main2 苹果",apple+"");

        findViewById(R.id.bn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(Main2Activity.this,Main3Activity.class));
            }
        });
    }
}

打印出的结果如下:

Main 苹果: onCreate: com.kimliu.daggerdemo3.Apple@5ef1d24
Main2 苹果: com.kimliu.daggerdemo3.Apple@5ef1d24

可以看到,两个对象的地址相同。
5.不使用单例的目标位置(Main3Activity),需要使用Apple,重新编写Module,Component注入使用即可。

Module:
@Module
public class Main3Module {

    @Provides
    public Apple providesApple(){
        return new Apple();
    }
}

Component:
@Component(modules = Main3Module.class)
public interface Main3Component {
    void inject(Main3Activity activity);
}

//使用:

public class Main3Activity extends AppCompatActivity {
    
    @Inject
    public Apple apple;

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

        DaggerMain3Component.create().inject(this);

        Log.i("Main3Activity 苹果", "onCreate: "+apple);

    }
}

本文参考:https://www.jianshu.com/p/22c397354997/
https://www.jianshu.com/p/ea4b89352e9a 感谢🙏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容