Dagger2 入门详解(一)

转载请标明出处:http://blog.csdn.net/xx326664162/article/details/53695558 文章出自:薛瑄的博客

Dagger2 系列:

Dagger2 入门详解(一)

@Scope 看这一篇就够了——Dagger2 (二)

一、Dagger2介绍

1. Dagger2是什么?

Dagger2在Github主页上的自我介绍是:“A fast dependency injector for Android and Java“(一个提供给Android和Java使用的快速依赖注射器。)
Dagger2是由谷歌接手开发,最早的版本Dagger1 是由Square公司开发的。

2. Dagger2相较于Dagger1的优势是什么?

更好的性能:相较于Dagger1,它使用的预编译期间生成代码来完成依赖注入,而不是用的反射。大家知道反射对手机应用开发影响是比较大的,因为反射是在程序运行时加载类来进行处理所以会比较耗时,而手机硬件资源有限,所以相对来说会对性能产生一定的影响。
容易跟踪调试:因为dagger2是使用生成代码来实现完整依赖注入,所以完全可以在相关代码处下断点进行运行调试。

3. 使用依赖注入的最大好处是什么?

没错,就是模块间解耦! 就拿当前Android非常流行的开发模式MVP来说,使用Dagger2可以将MVP中的V 层与P层进一步解耦,这样便可以提高代码的健壮性和可维护性。

如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。下面所说的依赖都是指类之间的依赖关系
如果不用Dagger2的情况下我们应该这么写:

public class A {
    ...
    B b;
    ...
    public A() {
        b = new B();
    }
}

上面例子面临着一个问题,一旦某一天B的创建方式(如构造参数)发生改变,那么你不但需要修改A中创建B的代码,还要修改其他所有地方创建B的代码。
如果我们使用了Dagger2的话,就不需要管这些了,只需要在需要B对象的地方写下:

public class A {
   @Inject
   B b;
}

当然在使用@Inject注解的时候,需要其它一些代码共同配合完成这个任务,下面将一一介绍Dagger2的功能。

二、Dagger2工作原理

我们先简单的从整体上看一下Dagger2工作原理,在针对每个功能逐一讲解。

这里写图片描述

上图中,

1、提供依赖部分,就是new对象。有两种方式,使用@Inject注解构造函数或者提供一个Module类

2、@Component 它本质是一个使用@Component注解的接口,这个接口与Module类建立联系,并提供inject()函数。

3、在原来需要使用new创建对象的地方,现在实现Component,然后调用它的inject()函数。

4、使用@Inject注解变量,就能生成它的对象。

三、Dagger2 中的注解

理解 Dagger2 中的几个注解的含义和用法,是掌握 Dagger2 的关键,下面一个一个的分析。

@Inject

@Inject 构造函数

使用@Inject注解构造函数。表示当创建类的实例时,Dagger会获取类的构造函数的参数,并调用此构造函数创建实例。

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  ...
}

@Inject 变量

注解变量,就生成这个变量的实例。在这个例子中,它创建Heater实例和Pump实例。

class CoffeeMaker {
  @Inject Heater heater;
  @Inject Pump pump;
  ...
}

如果你的类中有@Inject 注解变量,但没有@Inject注解构造函数,Dagger将根据请求注入这些字段,但不会创建新的实例。 可以使用@Inject注解一个无参构造函数,以指示Dagger可以使用该无参构造函数创建实例。

@Provides

默认情况下,Dagger通过创建所请求类型的实例来满足每个依赖关系,如上所述。 当你请求CoffeeMaker对象时,将通过调用new CoffeeMaker()创建实例,并将实例赋给CoffeeMaker变量。

但是@Inject不能在下面的地方工作:

  • 无法创建实现了接口的类。
  • 需要创建一个第三方类,无法使用@Inject注解这个类的构造函数,也就无法使用@Inject来创建实例。
  • 必须配置可配置对象!

对于@Inject不能满足要求的情况下,使用@ Provide 注解一个函数来满足依赖。 函数返回的实例,就是需要的实例。

当需要创建Heater对象时,provideHeater()就会被调用

@Provides static Heater provideHeater() {
  return new ElectricHeater();
}

所有@Provides方法必须属于一个模块。 这里说的模块是有@Module注解的类。如下所示:

@Module
class DripCoffeeModule {
  @Provides static Heater provideHeater() {
    return new ElectricHeater();
  }

  @Provides static Pump providePump(Thermosiphon pump) {
    return pump;
  }
}

@Module

使用@Module注解的类,用来包含@Provides注解的一些方法

@Module
public class Module{
    //A是第三方类库中的一个类
    @Provides
    A provideA(){
          return A();
    }
}

@Component

  • @Module 提供依赖, 就是提供生产类的实例
  • @Inject注解函数表示提供依赖,@Inject注解变量表示请求依赖,
  • @Component 就是联系提供和请求这两者的纽带。

使用起来很简单,只需要创建Component对象,然后调用Component的inject(A a)方法。就可以在类A中使用@Inject注解变量来创建实例。

创建Component对象时,需要创建他依赖的module和Component实例。见下面第2、3行代码

mMainComponent = DaggerMainComponent.builder()
                .appComponent(getAppComponent())
                .mainModule(new MainModule())
                .activityModule(new ActivityModule(this)).build();

Component的功能有哪些?或者说为什么要使用Component

1、可以表示哪个module来提供依赖
2、可以表示依赖哪些 Component
3、可以表示为谁提供依赖
4、可以表示提供哪些依赖

Component会首先从Module中查找可以创建实例的函数,若找到就用Module创建类实例,并停止查找Inject维度。否则才查找使用@Inject注解的构造函数。所以创建类实例级别Module维度要高于Inject维度。

下面通过几行代码,来对@Component主要的功能进行介绍:

@Component 注解的是一个接口

比如下面这个接口,表示它可以提供一个 生产MyApplication 实例的依赖

@Singleton  //这行先不去理会,下面会讲到
@Component(modules = {AppModule.class},dependencies = {xxxComponent.class})
public interface AppComponent{
  void inject(MyActivity myActivity);
  public MyApplication getApplication();
}

对应上面说的四点:

1、modules = {AppModule.class}, 表示AppModule提供依赖,具体的函数实现是在AppModule里面,可以有多个。(用 @Module 注解的类)

2、dependencies = {xxxComponent.class},AppComponent中部分对象,需要xxxComponent.class来提供依赖。

3、有参数无返回值的函数,表示这个类提供依赖。比如上面代码中调用 inject(MyActivity myActivity),把AppComponent注入到MyActivity,那么在 MyActivity就可以通过@Inject 注解变量来生成实例了。

4、有返回值无参数的函数表示AppComponent 向子Component(继承AppComponent 的Component)暴露哪些依赖。这里可能会有疑问,Component 可提供的依赖不就是 Module 里的那些吗?确实没错,这里的函数是父Component 对子Component暴露函数,并不一定会暴露 Module 中所有的方法,只有暴露的方法才能在子Component中使用

四、一个很简单的例子

这个例子的完整代码https://github.com/JantHsueh/Dagger2

1、关于Module和Component的使用

创建AppModule

@Module
public class AppModule {

    Context context;

    public AppModule(Context context){
        this.context = context;
    }

    @Provides @Singleton
    public Context provideContext(){
        return context;
    }

    @Provides @Singleton
    public ToastUtil provideToastUtil(){
        return new ToastUtil(context);
    }
}

创建AppComponent

@Singleton
@Component (modules={AppModule.class})// 由AppModule提供依赖
public interface AppComponent {
// 必须定义为接口,Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××,这里对应的实现类是DaggerAppComponent 
    Context getContext();
    ToastUtil getToastUtil();
}

通常约定AppComponent是一个全局的Component,负责管理整个App的全局类实例。当然你想怎么用都行,这只是一个主观层面的规定。

Context getContext();和ToastUtils getToastUtils();是将Context和ToastUtils暴露给子Component。不明白这句话什么意思??等会下面会进行解释

在Application中创建AppComponent实例

注意下面的只是生成mAppComponent实例,并没有调用inject();


public class App extends Application {

    AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mAppComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

    }

    public AppComponent getAppComponent(){
        return mAppComponent;
    }
}


2、关于父Component中的方法暴露给子Component部分

现在来解释一下Context getContext();和ToastUtils getToastUtils();是将Context和ToastUtils暴露给子Component。先看下面这段代码

@PerActivity
//
@Component(dependencies = AppComponent.class,modules = {MainModule.class, ActivityModule.class})

public interface MainComponent extends ActivityComponent{
    //对MainActivity进行依赖注入
    void inject(MainActivity mainActivity);
}

下面是一个MainComponent的简单使用

public class MainActivity extends BaseActivity implements MainFragment.OnFragmentInteractionListener{

    private MainComponent mMainComponent;

    @Inject
    ToastUtil toastUtil;//注意这里的注解使用的是MainComponent依赖的AppComponent中暴露的ToastUtil getToastUtil();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Dagger2框架将自动生成MainComponent的实现类DaggerMainComponent
        mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
                .mainModule(new MainModule())
                .activityModule(new ActivityModule(this)).build();
        mMainComponent.inject(this);//一定不要忘记调用inject();
    }
}

注意一定要调用inject()这个方法,我大概解释一下,如果没有调用inject(),那么toastUtil就是一个空值。

上面代码中的第14行

    mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
                .mainModule(new MainModule())
                .activityModule(new ActivityModule(this)).build();

这句代码会生成了 使用@Inject 注解的变量的实例(也就是ToastUtil实例),会在mMainComponent实例中生成一个ToastUtil实例。

这里使用@Inject 注解的变量有什么要求呢??是不是随便一个变量都能这样去创建??

当然不是,需要MainComponent中的module 有提供这个依赖,或者依赖的AppComponent提供这个依赖(这里具体指的是 在AppComponent 中暴露出ToastUtil getToastUtil())

这个ToastUtil实例是有了 ,但是要和MainActivity实例中的toastUtil实例建立联系就需要下面这行代码

mMainComponent.inject(this);

这句代码的意思是,将this实例与mMainComponent实例中的对象关联起来,这样this实例中的toastUtil就不是空值了。

五、Dagger2在android studio中的配置

在module下的build.gradle中添加以下依赖

apply plugin: 'com.neenbedankt.android-apt'

...

dependencies {
    apt "com.google.dagger:dagger-compiler:2.2"
    provided 'org.glassfish:javax.annotation:10.0-b28'
    compile "com.google.dagger:dagger:2.2"
}

在项目的根 build.gradle 里添加:
apt是一个Gradle插件,协助Android Studio 处理Annotation Processors,所以在Gradle中还必须添加以下内容

dependencies {
     classpath 'com.android.tools.build:gradle:2.2.3'
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

一个简单的例子

如果看了上面的讲解还是不知所云,结合这个demo再去看看文章,上面用到的代码大多都在这个例子中。也可以去看原作者demo,不过需要修改一些配置。

参考:
Dagger2 这次入门就不用放弃了
Dagger2 菜鸟入门
dagger2入门指南
噢~这就是Dagger2!
Dagger2 官方网站
Android:dagger2让你爱不释手-基础依赖注入框架篇
Android:dagger2让你爱不释手-重点概念讲解、融合篇
Android:dagger2让你爱不释手-终结篇

关注我的公众号,轻松了解和学习更多技术
这里写图片描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

薛瑄

文章不错,请博主吃包辣条

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

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

打赏作者

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

抵扣说明:

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

余额充值