Dagger2使用详解

Dagger2使用详解


什么是依赖注入?

依赖注入就是将调用者依赖的对象实例通过一定的方式从外部传入,解决了各个类之间的耦合问题。

这个外部,正是dagger2容器。需要什么对象从容器中取就行了,调用和被调用方被隔离开,通过一个容器将他们联系起来,从而实现了解耦。

Dagger2是Google出的依赖注入框架。Dagger2的原理是在编译期生成相应的依赖注入代码。其他框架是在运行时期反射获取注解内容,影响运行效率。


引入Dagger2

在app的build.grade中引入android-apt,并添加dagger2依赖

apply plugin: 'com.neenbedankt.android-apt'
dependencies {
    apt "com.google.dagger:dagger-compiler:2.11-rc1"
    provided 'org.glassfish:javax.annotation:10.0-b28'
    compile "com.google.dagger:dagger:2.11-rc1"

在project级别的build.grade中添加android-apt

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:2.2.2'
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

Dagger2基本用法

@Inject
通常在需要依赖的地方使用这个注解,换句话说,你用它告诉dagger2这个类或这个字断需要依赖注入,这样dagger2就会构造一个这个类的实例去满足他们的依赖
@Module
Module类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样dagger2在构造类实例的时候就知道去哪里找这些依赖。
@Provide
在Module中,我们定义的方法使用这个注解,告诉dagger2我们想要构造对象并提供这些依赖
@Component
Component是一个注入器,也可以说是连接Inject和Module的桥梁

这里写图片描述

下面看个例子,通过例子来看下dagger2的基本用法:

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

    DaggerLoginModuleComponent.create().inject(this);
    loginManager.login();
  }
}

调用LoginManager的login接口,因此需要将LoginManager对象注入到MainActivity中,注入的操作由DaggerLoginModuleComponent调用inject方法完成的,LoginModuleComponent是怎么和LoginManager关联上的呢?

// LoginModuleComponent.java
@Component(modules = {LoginModule.class})
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

事实上,LoginModuleComponent只是LoginModule和MainActivity的连接器,可以想象成注射器,把LoginModule提供的对象注射到MainActivity中,完成依赖注入的过程。再看下LoginModule中提供了哪些对象?

// LoginModule.java
@Module
public class LoginModule {
  @Provides
  public LoginManager providerLoginManager() {
    return new LoginManager();
  }
}
// LoginManager.java
public class LoginManager {
  public void login() {
    Log.i("dagger2", "--- login ---");
  }
}

可以看到是通过Provides注解来提供LoginManager对象的,用起来还是很简单的,运行一下:

04-27 18:38:05.893 2343-2343/com.testdes.des I/dagger2: --- login ---

Dagger2模块化

LoginManager中执行login操作,需要OkHttpClient对象,我们从构造方法中传入,如下:

// LoginManager.java
public class LoginManager {
  OkHttpClient client;
  public LoginManager(OkHttpClient client) {
    this.client = client;
  }
  public void login() {
    Log.i("dagger2", "--- login:" + client);
  }
}
// LoginModule.java
@Module
public class LoginModule {

  @Provides
  public LoginManager providerLoginMgr(OkHttpClient client) {
    Log.i("dagger2", "--providerLoginMgr");
    return new LoginManager(client);
  }

  @Provides
  public OkHttpClient okHttpClient() {
    return new OkHttpClient();
  }
}

考虑到OkHttpClient不仅仅在登录的时候使用,其它网络操作也需要这个对象,因此把OkHttpClient做成公共模块,通过HttpModule提供出来。

// HttpModule.java
@Module
public class HttpModule {
  @Provides
  public OkHttpClient providerOKHTTP() {
    return new OkHttpClient();
  }
}
// LoginModule.java
@Module(includes = {HttpModule.class})
public class LoginModule {

  @Provides
  public LoginManager providerLoginMgr(OkHttpClient client) {
    Log.i("dagger2", "--providerLoginMgr");
    return new LoginManager(client);
  }
}

在LoginModule中通过includes属性把HttpModule引入进来就可以了,实现了简单的HttpModule模块化,当然还有其他的方式,比如在LoginModuleComponent中引入HttpModule

@Component(modules = {LoginModule.class, HttpModule.class})
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

或者创建HttpComponent,通过dependencies依赖,如下:

@Component(modules = {LoginModule.class}, dependencies = HttpComponent.class)
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

Dagger2单例

单例只需要使用@Singleton注解来声明,当HttpModule的providerOKHTTP使用了Singleton,LoginModuleComponent也需要使用Singleton来标注,如下:

// HttpModule.java
@Module
public class HttpModule {
  @Provides
  public OkHttpClient providerOKHTTP() {
    return new OkHttpClient();
  }
}
// LoginModuleComponent.java
@Component(modules = {HttpModule.class})
@Singleton
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

// MainActivity.java
public class MainActivity extends AppCompatActivity {
  @Inject OkHttpClient okHttpClient;
  @Inject OkHttpClient okHttpClient2;
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DaggerLoginModuleComponent.create().inject(this);
    Log.i("dagger2", ""+okHttpClient);
    Log.i("dagger2", ""+okHttpClient2);
  }
}

运行一下,确实是单例

04-28 14:47:03.553 8497-8497/com.testdes.des I/dagger2: com.testdes.des.dagger2.OkHttpClient@3ef1ff
04-28 14:47:03.553 8497-8497/com.testdes.des I/dagger2: com.testdes.des.dagger2.OkHttpClient@3ef1ff

我们在增加一个页面SettingActivity,假设有个登出的功能,也需要用到OkHttpClient,代码不贴了,运行一下:

04-28 15:29:47.999 9781-9781/com.testdes.des I/dagger2: MainActivity:com.testdes.des.dagger2.OkHttpClient@3ef1ff
04-28 15:29:48.068 9781-9781/com.testdes.des I/dagger2: SettingActivity:com.testdes.des.dagger2.OkHttpClient@ba0d601

哇擦,又发现不单例了!!这是什么鬼?
这里解释下,dagger2的单例和传统java单例不一样,我们通常说的单例是在静态域里初始化的,只要程序不退出就一直保持单例,而dagger2单例是依附于component,即使声明了singleton,不同的component也不是单例,参考文章末尾的注意事项第7条。

所以我们如果要实现单例,需要怎么做呢?
思路是定义一个HttpComponent,作为其他component的dependencies component,具体如下:

// HttpComponent.java
@Component(modules = {HttpModule.class})
@Singleton
public interface HttpComponent {
}

// LoginModuleComponent.java
@Component(dependencies = {HttpComponent.class})
@Singleton
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

运行一下,发现报错了

Error:(11, 1) error: This @Singleton component cannot depend on scoped components:
@Singleton com.testdes.des.dagger2.HttpComponent

错误原因参考文章末尾注意事项第4条,Singleton是一个scope, LoginModuleComponent和HttpComponent的scope不能相同,那我们只好给LoginModuleComponent自定义一个scope了,怎么定义可以参考Singleton

// ActivityScope.java
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {
}
// LoginModuleComponent.java
@Component(dependencies = {HttpComponent.class})
@ActivityScope
public interface LoginModuleComponent {
  void inject(MainActivity activity);
}

运行一下,终于还是报错了

Error:(13, 8) error: com.testdes.des.dagger2.OkHttpClient cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
com.testdes.des.dagger2.OkHttpClient is injected at
com.testdes.des.MainActivity.okHttpClient
com.testdes.des.MainActivity is injected at
com.testdes.des.dagger2.LoginModuleComponent.inject(activity)

LoginModuleComponent没有办法得到HttpComponent提供的OkHttpClient对象,这是需要做一个桥接,增加一个get方法(名字任意取),返回一个OkHttpClient对象,如下

@Component(modules = {HttpModule.class})
@Singleton
public interface HttpComponent {
  OkHttpClient get();
}

BUILD SUCCESSFUL

在MainActivity中注入的方法修改下:

// MainActivity.java
DaggerLoginModuleComponent.builder()
        .httpComponent(DaggerHttpComponent.create()).build().inject(this);

需要构建一个httpComponent对象,按照上面的写法,每次用到都要调用DaggerHttpComponent.create()显然不合理,因此我们把httpComponent构建操作放到Application初始化的时候执行一次,如下:

// TestApplication.java
public class TestApplication extends Application{

  HttpComponent httpComponent;
  @Override public void onCreate() {
    super.onCreate();
    System.out.println("start TestApplication");

    httpComponent = DaggerHttpComponent.create();
  }

  public HttpComponent getHttpComponent() {
    return httpComponent;
  }

}

// MainActivity.java
DaggerLoginModuleComponent.builder()
.httpComponent(((TestApplication)getApplication()).getHttpComponent()).build().inject(this);

SettingActivity中的写法和MainActivity保持一致,然后运行一下:

04-28 16:25:50.715 11228-11228/com.testdes.des I/dagger2: MainActivity:com.testdes.des.dagger2.OkHttpClient@39f1d88
04-28 16:25:50.769 11228-11228/com.testdes.des I/dagger2: SettingActivity:com.testdes.des.dagger2.OkHttpClient@39f1d88

终于单例了,以上是dagger2的基本用法和单例的实现,谢谢~


注意事项

  1. component的inject方法接收父类型参数,而传入的是子类型的对象则无法注入
  2. component关联的modules中不能有重复的provide
  3. module中的provide方法使用了scope,则component需要使用同一个注解
  4. component的dependencies与component自身不能使用同一个scope,即组件之间的scope不能相同
  5. singleton的组件不能依赖其他scope组件,只能其他scope组件依赖singleton
  6. 没有scope的组件不能依赖有scope的组件
  7. @singleton的生命周期依附于component,同一个module provide singleton,不同的component也不是单例
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengyuan9160

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值