带你玩转Dagger2

一.概述

  1. Dagger2是一个基于JSR-330标准的依赖注入框架,在编译期间自动生成代码,负责依赖对象的创建。
  • Java 依赖注入标准 JSR-330 简介概念
    JSR-330 是 Java 的依赖注入标准。定义了如下的术语描述依赖注入:
    A 类型依赖 B类型(或者说 B 被 A 依赖),则 A类型 称为”依赖(物) dependency”
    运行时查找依赖的过程,称为”解析 resolving“依赖
    如果找不到依赖的实例,称该依赖是”不能满足的 unsatisfied”
    在”依赖注入 dependency injection”机制中,提供依赖的工具称为”依赖注入器 dependency injector”
  1. 作用:管理对象及其生命周期
  2. 好处:统一管理对象,对象初始化一次即可,降低程序耦合。比如一个对象调用一千次,如果new的话需要一千次,现在只需要去找dagger2要即可,即使修改也就去找Dagger2修改一次即可。
  3. 使用场景:应用在比较复杂或者规模较大的App,简单App没必要大材小用。
  4. 介绍
    ①Module:提供对象的,注入到需要的地方。一个App中有很多module,比如httpObject、GlideObject等。
    ②Component:用于注入对象,从Module注入到需要的地方,比如controller等


    dagger2.png

二.具体使用

  1. 导入依赖
implementation 'com.google.dagger:dagger:2.17'
annotationProcessor 'com.google.dagger:dagger-compiler:2.17'
  1. 建包及使用(di:注入包、object:对象包)

①在object包创建对象类

public class HttpObject{
}

②在di包创建类HttpModule

/**
 * 提供Http对象
 *
 */
@Module
public class HttpModule {
    @Provides
    public HttpObject provodeHttpObject() {
        return new HttpObject();
    }
}

③在di包创建AnflyComponet接口用来注入对象

/**
 * 提供依赖注入的类
 */
@Component(modules = {HttpModule.class})
public interface AnflyComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * @param mainActivity
     */
    void injectMainactivty(MainActivity mainActivity);
}

④重新编译整个工程RebuildProject,生成DaggerAnflyComponet(实现原理是atp),在MainActivity中创建注入器并调用其方法,就可以通过@Inject注入HttpObject对象

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //生成DaggerAnflyComponet注解处理器
        DaggerAnflyComponet.create().injectMainactivty(this);

        Log.e("TAG", "httpObject=" + httpObject.hashCode());
    }
}

3.原理简介
刚才在MainActivity中使用DaggerAnflyComponet创建component,使用的使用create方式
①我们点击源码进去:

  public static AnflyComponet create() {
    return new Builder().build();
  }

②create最终使用的是构建者模式实现的,所以我们也可以直接在MainActivity中使用构建者模式创建注解处理器

AnflyComponet anflyComponet = DaggerAnflyComponet.builder()
                .httpModule(new HttpModule())
                .dataBaseModule(new DataBaseModule())
                .build();
        anflyComponet.injectMainactivty(this);

③点击.httpModule(new HttpModule())

public Builder httpModule(HttpModule httpModule) {
      this.httpModule = Preconditions.checkNotNull(httpModule);
      return this;
    }

通过构建者模式返回自身,当然这里做了非空判断:Preconditions.checkNotNull(httpModule)

ublic static <T> T checkNotNull(T reference) {
    if (reference == null) {
      throw new NullPointerException();
    }
    return reference;
  }

④点击build看看做了什么事情

 public AnflyComponet build() {
      if (httpModule == null) {
        this.httpModule = new HttpModule();
      }
      return new DaggerAnflyComponet(this);
    }

先判断httpModule 是否为空,如果为空就新建一个,最后返回DaggerAnflyComponet
至此,相当于把需要的对象在内存中生成。
⑤点击anflyComponet.injectMainactivty(this)跳转到我们自己写的接口方法


1582989016(1).png

该接口有自己的实现类,我们看看实现类

 @Override
  public void injectMainactivty(MainActivity mainActivity) {
    injectMainActivity(mainActivity);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectHttpObject(
        instance, HttpModule_ProvodeHttpModuleFactory.proxyProvodeHttpModule(httpModule));
    return instance;
  }

通过MainActivity成员注射器 MainActivity_MembersInjector注入HttpObject

⑥声明HttpObject为单例

@Module
@Singleton
public class HttpModule {
    @Singleton
    @Provides
    public HttpObject provodeHttpModule() {
        return new HttpObject();
    }
}

@Singleton
@Component(modules = {HttpModule.class, DataBaseModule.class})
public interface AnflyComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * @param mainActivity
     */
    void injectMainactivty(MainActivity mainActivity);

    void injectSecondactivty(SecondActivity secondActivity);
}

现在新建一个SecondActivity同样打印HttpObject对象的hashcode,MainActivit打印两个HttpObject对象的hashcode。
MainActivit:

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Inject
    HttpObject httpObject2;

    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //生成DaggerAnflyComponet注解处理器
        //第一中写法
        DaggerAnflyComponet.create().injectMainactivty(this);
        //第二种写法
        AnflyComponet anflyComponet = DaggerAnflyComponet.builder()
                .httpModule(new HttpModule())
                .dataBaseModule(new DataBaseModule())
                .build();
        anflyComponet.injectMainactivty(this);

        Log.e("TAG", "MainActivity httpObject=" + httpObject.hashCode());
        Log.e("TAG", "MainActivity httpObject2=" + httpObject2.hashCode());
    }

    private void initView() {
        tv = (TextView) findViewById(R.id.tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

SecondActivity:

public class SecondActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        DaggerAnflyComponet.create().injectSecondactivty(this);

        Log.e("TAG", "SecondActivity httpObject=" + httpObject.hashCode());
    }
}

结果是:

02-29 23:50:03.339 3667-3667/? E/TAG: MainActivity httpObject=745396538
02-29 23:50:03.339 3667-3667/? E/TAG: MainActivity httpObject2=745396538
02-29 23:50:05.176 3667-3667/com.anfly.dagger2 E/TAG: SecondActivity httpObject=951541497

结论:同一个activity中是同一个对象,但是不同activity中不是同一个对象,现在只达到了局部单例,但是项目中是需要全局单例的,怎么办呢??

⑦实现全局单例
方法:所有类使用同一个注射器,即在application中获取注射器,在不同类中去注入
创建一个application

public class DaggerApplication extends Application {

    private static DaggerApplication daggerApplication;
    private AnflyComponet anflyComponet;

    @Override
    public void onCreate() {
        super.onCreate();
        daggerApplication = this;

        anflyComponet = DaggerAnflyComponet.create();
    }

    public static DaggerApplication getDaggerApplication() {
        return daggerApplication;
    }

    public AnflyComponet getAnflyComponet() {
        return anflyComponet;
    }
}

在MainActivit中:

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Inject
    HttpObject httpObject2;

    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //生成DaggerAnflyComponet注解处理器
        //第一中写法
//        DaggerAnflyComponet.create().injectMainactivty(this);
        //第二种写法
//        AnflyComponet anflyComponet = DaggerAnflyComponet.builder()
//                .httpModule(new HttpModule())
//                .dataBaseModule(new DataBaseModule())
//                .build();
//        anflyComponet.injectMainactivty(this);

        DaggerApplication.getDaggerApplication().getAnflyComponet().injectMainactivty(this);

        Log.e("TAG", "MainActivity httpObject=" + httpObject.hashCode());
        Log.e("TAG", "MainActivity httpObject2=" + httpObject2.hashCode());
    }

    private void initView() {
        tv = (TextView) findViewById(R.id.tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

在SecondActivity中:

public class SecondActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
//        DaggerAnflyComponet.create().injectSecondactivty(this);

        DaggerApplication.getDaggerApplication().getAnflyComponet().injectSecondactivty(this);
        Log.e("TAG", "SecondActivity httpObject=" + httpObject.hashCode());
    }
}

结果:

03-01 00:04:34.301 3817-3817/com.anfly.dagger2 E/TAG: MainActivity httpObject=745396538
03-01 00:04:34.301 3817-3817/com.anfly.dagger2 E/TAG: MainActivity httpObject2=745396538
03-01 00:04:36.139 3817-3817/com.anfly.dagger2 E/TAG: SecondActivity httpObject=745396538

结论:使用同一个注射器实现的全局单例

4.不同Component将对象同一个activity
不同的Component不能给同一个activity注入对象,实在想注入同一个activity只能想办法组装成一个component。
怎么组装呢?创建一个主component,其他次component可以通过dependencies = PresenterComponet.class添加到主component
①对象类

public class PresenterObject {
}

②提供对象的类

@Module
public class PresenterModule {
    @Provides
    public PresenterObject providePresenter() {
        return new PresenterObject();
    }
}

③注射器

@Component(modules = {PresenterModule.class})
public interface PresenterComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * 但是这里是从component,所以该方法不能用
     *
     * @param
     */
//    void injectMainactivty(MainActivity mainActivity);
//    void injectSecondactivty(SecondActivity secondActivity);

    public PresenterObject providePresenter();
}

这里需要注意的是,不需要在写一遍injectMainactivty(MainActivity mainActivity),因为在主component已经写过一遍了,只需要提供一个方法(来自module中方法);
④主component修改

@Singleton
@Component(modules = {HttpModule.class, DataBaseModule.class},
        dependencies = {PresenterComponet.class})
public interface AnflyComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * @param mainActivity
     */
    void injectMainactivty(MainActivity mainActivity);

    void injectSecondactivty(SecondActivity secondActivity);
}

第三行多了dependencies = {PresenterComponet.class},他表示这个主component引入了次component。
⑤RebuildProject
在DaggerApplication中添加一行:创建注入器

@Override
    public void onCreate() {
        super.onCreate();
        daggerApplication = this;

        anflyComponet = DaggerAnflyComponet.builder()
                .httpModule(new HttpModule())
                .presenterComponet(DaggerPresenterComponet.create())
                .build();
    }

现在就可以直接在MainActivity中直接调用presenterObject了。

  1. 将PresenterObject设置为单例。如果按照以前的写法,module和component添加注解@Singleton就会报错:
错误: [Dagger/IncompatiblyScopedBindings] com.anfly.dagger2.di.PresenterComponet (unscoped) may not reference scoped bindings:
@Provides @Singleton com.anfly.dagger2.object.PresenterObject com.anfly.dagger2.di.PresenterModule.providePresenter()

原因:使用dependencies的时候,多个组件之间的Scope不能相同,没有Scope的不能依赖有Scope的组件。
Scope是什么呢?用来标识组件,方便进行依赖关系绑定
解决方案:我们可以使用scope替换掉所用的@Singleton就能解决问题了
①创建一个scope文件,再创建一个APPScope类,这个代码是@Singleton源码

@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope {
}

首先直接将主component相关的@Singleton替换为@AppScope

@Module
public class HttpModule {
    @AppScope
    @Provides
    public HttpObject provideHttpModule() {
        return new HttpObject();
    }
}

@AppScope
@Component(modules = {HttpModule.class, DataBaseModule.class},
        dependencies = {PresenterComponet.class})
public interface AnflyComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * @param mainActivity
     */
    void injectMainactivty(MainActivity mainActivity);

    void injectSecondactivty(SecondActivity secondActivity);
}

每一个组件或者component对应一个Scope,为presenter创建一个scope

@Scope
@Documented
@Retention(RUNTIME)
public @interface PresenterSingleScope {
}

然后

@PresenterSingleScope
@Component(modules = {PresenterModule.class})
public interface PresenterComponet {
    /**
     * 提供注入方法,注意参数不能使用多态
     *
     * 但是这里是从component,所以该方法不能用
     *
     * @param
     */
//    void injectMainactivty(MainActivity mainActivity);
//    void injectSecondactivty(SecondActivity secondActivity);

    public PresenterObject providePresenter();
}

@Module
public class PresenterModule {
    @PresenterSingleScope
    @Provides
    public PresenterObject providePresenter() {
        return new PresenterObject();
    }
}

打印结果:

03-01 04:03:18.332 5482-5482/com.anfly.dagger2 E/TAG: MainActivity httpObject=745396538
03-01 04:03:18.332 5482-5482/com.anfly.dagger2 E/TAG: MainActivity httpObject2=745396538
03-01 04:03:18.332 5482-5482/com.anfly.dagger2 E/TAG: MainActivity presenterObject=533813739
03-01 04:03:21.948 5482-5482/com.anfly.dagger2 E/TAG: SecondActivity httpObject=745396538
03-01 04:03:21.948 5482-5482/com.anfly.dagger2 E/TAG: SecondActivity presenterObject=533813739

结论:我们实现了多个组件或者component向同一个activity注入对象,并且实现全局单例。

三.高级用法
出来混迟早要还的,技术债Dagger2:Android篇
可能是东半球最好的dagger2文章
浅析Dagger2的使用

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