Android Dagger2使用详解

文章目录

  • 1 Dagger2简介
    • 1.1 Dagger2是什么?
    • 1.2 dagger2 解决什么问题
    • 1.3 什么是依赖注入
  • 2 Dagger2详解
    • 2.1 注解详解
  • 3 Dagger2简单使用
    • 3.1 添加依赖
    • 3.2 新建Object
    • 3.3 新建Module
    • 3.4 新建Component
    • 3.5 查看结果
  • 4 分析代码
  • 5 单例
    • 5.1 使用@Singleton
    • 5.2 SecActivity
    • 5.4 局部单例
    • 5.5 创建全局对象
  • 6 两个的Component
    • 6.1 dependencies
    • 6.2 使用@Scope注解
  • 7 Subcomponent
    • 7.1 Subcomponent使用演示
    • 7.2 获取多个不同的网络请求地址
  • 8 Subcomponent和dependencies的不同
    • 8.1 dependencies
    • 8.2 Subcomponent

官网地址:https://dagger.dev/
Github地址:https://github.com/google/dagger

参考:

User’s Guide
https://dagger.dev/users-guide

Android - Dagger2使用详解:
https://www.jianshu.com/p/2cd491f0da01

秒懂依赖注入及 Dagger2 的实用技能(如何在Android中使用):
https://blog.csdn.net/ShuSheng0007/article/details/80950117

秒懂 Java注解类型(@Annotation):
https://blog.csdn.net/shusheng0007/article/details/80622035

1 Dagger2简介

1.1 Dagger2是什么?

是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫黄油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理,其目的是为了降低程序耦合。

google在dagger2 官网定义如下:

Dagger is a fully static, compile-time dependency injection >framework for both Java and Android.Dagger 是一个可以用于Java及Android平台的依赖注入框架,其注入过程完全是静态的在编译时期完成的(通过编译时产生代码的方式,区别于Spring等框架的依赖反射的方式,反射是基于运行时的)。

1.2 dagger2 解决什么问题

第一:dagger 是一个依赖注入框架,首要任务当然是解决依赖注入的问题。
第二:dagger主要想通过编译时产生代码的方式来解决那些基于反射的依赖注入框架所存在的缺点,例如性能问题,开发过程中存在的问题。

1.3 什么是依赖注入

In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state.[1] Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
依赖注入是一种给一个对象提供其依赖的对象的技术。例如A对象需要依赖B对象,那么此处关键点就是不能在A里面去new B对象,必须通过某种方式将B对象提供给(注入)A中,例如最简单的方式是通过一个 set(B b)方法

2 Dagger2详解

2.1 注解详解

  • @Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
  • @Module: Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。
  • @Provide: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
  • @Component: Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。
  • Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如 果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。
  • @Scope: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例。在scope的例子中,我们用自定义的@PerActivity注解一个类,所以这个对象存活时间就和 activity的一样。简单来说就是我们可以定义所有范围的粒度(@PerFragment, @PerUser, 等等)。
  • Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@ForApplication”和“@ForActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。

3 Dagger2简单使用

Dagger2不能将不同的组件往一个类里面注入,我们可以将多个组件打包成一个组件,然后再这个组件注入到需要的地方。
Android Dagger2使用详解_第1张图片

3.1 添加依赖

在app的build.gradle中添加依赖和注解处理器

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

3.2 新建Object

新建object包,然后在包中新建两个类

public class HttpObject {
}
public class DatabaseObject {
}

3.3 新建Module

新建package名为di,在di包下创建HttpModule和DatabaseModule,用来提供对象。

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

/**
 * 用来提供对象
 */
@AppScope
@Module
public class HttpModule {

    @AppScope
    @Provides
    public HttpObject providerHttpObject(){
        return new HttpObject();
    }

//    @Provides
//    public XXXObject providerXXXObject(){
//        return new XXXObject();
//    }
}
/**
 * 用来提供对象
 */
@Module
public class DatabaseModule {

    @Provides
    public DatabaseObject providerDatabaseObject(){
        return new DatabaseObject();
    }
}

3.4 新建Component

新建Component,并使Module和Component建立连接,并注入到Activity中

@Component(modules = {HttpModule.class, DatabaseModule.class})
public interface MyComponent {
    void injectMainActivity(MainActivity mainActivity);
}

3.5 查看结果

Make Project后,查看生成的文件,如下图:
Android Dagger2使用详解_第2张图片

贴上DaggerMyComponent代码:

public final class DaggerMyComponent implements MyComponent {
  private Provider providerHttpObjectProvider;

  private Provider providerDatabaseObjectProvider;

  private MembersInjector mainActivityMembersInjector;

  private DaggerMyComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static MyComponent create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.providerHttpObjectProvider =
        HttpModule_ProviderHttpObjectFactory.create(builder.httpModule);

    this.providerDatabaseObjectProvider =
        DatabaseModule_ProviderDatabaseObjectFactory.create(builder.databaseModule);

    this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(
            providerHttpObjectProvider, providerDatabaseObjectProvider);
  }

  @Override
  public void injectMainActivity(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  public static final class Builder {
    private HttpModule httpModule;

    private DatabaseModule databaseModule;

    private Builder() {}

    public MyComponent build() {
      if (httpModule == null) {
        this.httpModule = new HttpModule();
      }
      if (databaseModule == null) {
        this.databaseModule = new DatabaseModule();
      }
      return new DaggerMyComponent(this);
    }

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

    public Builder databaseModule(DatabaseModule databaseModule) {
      this.databaseModule = Preconditions.checkNotNull(databaseModule);
      return this;
    }
  }
}

在MainActivity中通过 @Inject创建对象

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;
    @Inject
    DatabaseObject databaseObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMyComponent.create().injectMainActivity(this);
        Log.i("MainActivity",httpObject.hashCode() + "");
        Log.i("MainActivity",databaseObject.hashCode() + "");
    }
}

打印:

MainActivity: 77306469
MainActivity: 241808186

HttpObject 和 DatabaseObject对象构建成功。

如果想修改HttpObject的构造方法,我们并不需要修改Activity中的代码,只需修改HttpModule即可,如下图:
Android Dagger2使用详解_第3张图片

4 分析代码

上面MainActivity我们使用如下代码实现注入。

DaggerMyComponent.create().injectMainActivity(this);

我们也可以通过如下代码实现:

DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);

build()方法如下图所示:
Android Dagger2使用详解_第4张图片
可以看到在build方法中实例化了DaggerMyComponent对象。

有了DaggerMyComponent对象,接下来看看书如何注入的。

在DaggerMyComponent的injectMainActivity调用了injectMembers方法,如下图:
在这里插入图片描述

injectMembers是MembersInjector接口里面的一个方法,如下图:

Android Dagger2使用详解_第5张图片

injectMembers方法具体实现是在MainActivity_MembersInjector这个类中,如下图:
Android Dagger2使用详解_第6张图片

从上图中可以看到,在MainActivity_MembersInjector类中的injectMembers方法中生成了我们需要的三个Object实例。

生成不同的Object实例是通过不同的Provider的get()方法得到的,那get()方法又是如何实现的呢?

在DaggerMyComponent的initialize方法中生成了不同的Provider,如下图:

Android Dagger2使用详解_第7张图片

Provider调用get方法最后还是调用了Module中的providerHttpObject方法,providerHttpObject方法是带有 @Provides 注解的。
Android Dagger2使用详解_第8张图片

5 单例

5.1 使用@Singleton

在providerHttpObject上添加@Singleton注解,在对应的Module和Component上都要添加@Singleton注解。

@Singleton
@Module
public class HttpModule {
    @Singleton
    @Provides
    public HttpObject providerHttpObject(){
        return new HttpObject();
    }
}
@Singleton
@Component(modules = {HttpModule.class, DatabaseModule.class})
public interface MyComponent {
    void injectMainActivity(MainActivity mainActivity);
}

MainActivity代码不做修改,如下:

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;
    @Inject
    HttpObject httpObject2;

    @Inject
    DatabaseObject databaseObject;

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

//        DaggerMyComponent.builder().httpModule(new HttpModule()).build().injectMainActivity(this);

        Log.i("MainActivity",httpObject.hashCode() + "");
        Log.i("MainActivity",httpObject2.hashCode() + "");
        Log.i("MainActivity",databaseObject.hashCode() + "");
    }
}

运行查看结果httpObject和httpObject2是同一个对象。
在这里插入图片描述

5.2 SecActivity

public class SecActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sec);
        DaggerMyComponent.create().injectSecActivity(this);
        Log.i("hongxue","sec="+httpObject.hashCode()+"");
    }
}

查看结果,发现SecActivity和MainActivity中httpObject并不是同一个对象。
Android Dagger2使用详解_第9张图片

why?

这就是局部单例。

5.4 局部单例

查看DaggerMyComponent的initialize方法和之前有所不同,如下图箭头位置所示:
Android Dagger2使用详解_第10张图片
查看DoubleCheck的代码,这里贴上完整的代码:

public final class DoubleCheck implements Provider, Lazy {
  private static final Object UNINITIALIZED = new Object();

  private volatile Provider provider;
  private volatile Object instance = UNINITIALIZED;

  private DoubleCheck(Provider provider) {
    assert provider != null;
    this.provider = provider;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the provider
  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = provider.get();
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }

  /** Returns a {@link Provider} that caches the value from the given delegate provider. */
  public static  Provider provider(Provider delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
      /* This should be a rare case, but if we have a scoped @Bind that delegates to a scoped
       * binding, we shouldn't cache the value again. */
      return delegate;
    }
    return new DoubleCheck(delegate);
  }

  /** Returns a {@link Lazy} that caches the value from the given provider. */
  public static  Lazy lazy(Provider provider) {
    if (provider instanceof Lazy) {
      @SuppressWarnings("unchecked")
      final Lazy lazy = (Lazy) provider;
      // Avoids memoizing a value that is already memoized.
      // NOTE: There is a pathological case where Provider

may implement Lazy, but P and L // are different types using covariant return on get(). Right now this is used with // DoubleCheck exclusively, which is implemented such that P and L are always // the same, so it will be fine for that case. return lazy; } return new DoubleCheck(checkNotNull(provider)); } }

5.5 创建全局对象

在MainActivity和SecActivity中我们创建了不同的DaggerMyComponent对象,所以在两个页面中通过单例获取到的实例也不同,如果想获取到相同的实例,可以在Application中创建DaggerMyComponent对象。

创建MyApplication:

public class MyApplication extends Application {

    private MyComponent myComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        myComponent = DaggerMyComponent.create();
    }

    public MyComponent getAppComponent() {
        return myComponent;
    }

}

分别在MainActivity和SecActivity中添加如下代码:

 ((MyApplication) getApplication()).getAppComponent().injectMainActivity(this);
 ((MyApplication) getApplication()).getAppComponent().injectSecActivity(this);

运行后查看结果:
Android Dagger2使用详解_第11张图片

6 两个的Component

新建名为"presenter_di" 的 package,然后在里面新建Presenter、PresenterModule和PresenterModule:

public class Presenter {
}
@Module
public class PresenterModule {
    @Provides
    public Presenter providePresenter() {
        return new Presenter();
    }
}
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
    void injectMainActivity(MainActivity mainActivity);
//    Presenter providePresenter();
}

在MainActivity中添加注入

 @Inject
 Presenter presenter;

编译后报错,说明一个Activity中不能直接使用两个不同的Component。

6.1 dependencies

可以通过依赖解决这个问题。

修改PresenterComponent:

@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
//    void injectMainActivity(MainActivity mainActivity);

    Presenter providePresenter();
}

PresenterComponent不再提供注入方法,只是提供了一个Presenter对象。

在MyComponent上将PresenterComponent添加为dependencies:

@Singleton
@Component(modules = {HttpModule.class, DatabaseModule.class},
        dependencies = {PresenterComponent.class})
public interface MyComponent {
    void injectMainActivity(MainActivity mainActivity);
    void injectSecActivity(SecActivity secActivity);
}

此时可以编译通过了。

修改MyApplication,将DaggerPresenterComponent对象添加到DaggerMyComponent中,如下:

public class MyApplication extends Application {

    private MyComponent myComponent;

    @Override
    public void onCreate() {
        super.onCreate();
//        myComponent = DaggerMyComponent.create();
        myComponent = DaggerMyComponent
                .builder()
                .httpModule(new HttpModule())
                .presenterComponent(DaggerPresenterComponent.create())
                .build();
    }

    public MyComponent getAppComponent() {
        return myComponent;
    }

}

运行后查看结果:
在这里插入图片描述


6.2 使用@Scope注解

我们如果在PresenterComponent上添加@Singleton注解,那么程序也不能编译通过。

@Singleton
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
//    void injectMainActivity(MainActivity mainActivity);
    Presenter providePresenter();
}

也就是说:一个Component使用了@Singleton注解,另一个Component也使用了@Singleton注解就会编译报错。

那怎么处理这个问题会比较好呢?

我们查看@Singleton注解:
Android Dagger2使用详解_第12张图片

可以看到@Singleton注解上使用了@Scope注解,@Scope限定了注解作用域。

我们可以自己自定义Singleton。

在di包下面新建scope包,并新建AppScope注解,如下:

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

}

将使用@Singleton地方替换为@Scope,运行后查看结果与使用@Singleton结果一样,如下:

Android Dagger2使用详解_第13张图片

@Singleton只是一个模板,我们需要scope都自定义 dependencies:组件依赖

  1. 多个组件之间的scope不能相同
  2. 没有scope的不能依赖有scope的组件

我们在定义一个UserScope注解,在Presenter上使用

@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {

}
@UserScope
@Component(modules = {PresenterModule.class})
public interface PresenterComponent {
//    void injectMainActivity(MainActivity mainActivity);
    Presenter providePresenter();
}
@UserScope
@Module
public class PresenterModule {
    @UserScope
    @Provides
    public Presenter providePresenter() {
        return new Presenter();
    }
}

编译后运行得到了相同的结果。


7 Subcomponent

在上面使用dependencies来确定两个component的依赖关系,我们还可以使用@Subcomponent注解。

7.1 Subcomponent使用演示

为了演示方便,这里重新新建一个项目。

1 在object包下面新建DatabaseObject和HttpObject:

public class DatabaseObject {
}
public class HttpObject {
}

2 在di包下新建DatabaseModule和HttpModule

/**
 * 用来提供对象
 */
@Module
public class DatabaseModule {

    @Provides
    public DatabaseObject providerDatabaseObject() {
        return new DatabaseObject();
    }
}
@Module
public class HttpModule {

    @Provides
    public HttpObject providerHttpObject() {
        return new HttpObject();
    }

}

3 在di包下新建DatabaseComponent和HttpComponent。其中DatabaseComponent使用了@Subcomponent注解

/**
 * 这就是一个子组件
 */
@Subcomponent(modules = {DatabaseModule.class})
public interface DatabaseComponent {
    void injectMainActivity(MainActivity mainActivity);
}	
@Component(modules = {HttpModule.class})
public interface HttpComponent {
    DatabaseComponent buildDatabaseComponent();
}

4 使用

public class MainActivity extends AppCompatActivity {

    @Inject
    HttpObject httpObject;
    @Inject
    HttpObject httpObject2;
    @Inject
    DatabaseObject databaseObject;

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

        DaggerHttpComponent.create().buildDatabaseComponent().injectMainActivity(this);
        Log.i("hongxue", httpObject.hashCode() + "");
        Log.i("hongxue", httpObject2.hashCode() + "");
        Log.i("hongxue", databaseObject.hashCode() + "");
    }
}

运行后正常打印结果。

7.2 获取多个不同的网络请求地址

在项目中,可能会有多个网络请求地址,我们可以通过如下设置。

1 HttpObject中添加baseUrl属性;
2 在HttpModule中定义一个List用于保存请求地址的集合。在获取HttpObject对象的方法上使用@Named注解,使用的时候就通过这个注解获取HttpObject对象。

public class HttpObject {
    public String baseUrl;
    public HttpObject(String baseUrl) {
        this.baseUrl = baseUrl;
    }
    public HttpObject() {
    }
}
@Module
public class HttpModule {

    ArrayList baseUrl;

    public HttpModule(ArrayList baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Named("base1")
    @Provides
    public HttpObject providerHttpObject1() {
        return new HttpObject(baseUrl.get(0));
    }

    @Named("base2")
    @Provides
    public HttpObject providerHttpObject2() {
        return new HttpObject(baseUrl.get(1));
    }


}
public class MainActivity extends AppCompatActivity {

    String url1 = "www.sina.com.cn";
    String url2 = "www.163.com.cn";

    @Inject
    @Named("base1")
    HttpObject httpObject1;

    @Inject
    @Named("base2")
    HttpObject httpObject2;


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

        ArrayList url = new ArrayList<>();
        url.add(url1);
        url.add(url2);

        DaggerHttpComponent.builder().httpModule(new HttpModule(url)).build()
                .buildDatabaseComponent().injectMainActivity(this);

//        DaggerHttpComponent.create().buildDatabaseComponent().injectMainActivity(this);

        Log.i("hongxue", httpObject1.baseUrl + "");
        Log.i("hongxue", httpObject2.baseUrl + "");

    }
}

运行后查看结果:
在这里插入图片描述

8 Subcomponent和dependencies的不同

8.1 dependencies

使用dependencies ,还是会生成不同的DaggerXXXComponent文件,并且DaggerPresenterComponent最为被依赖的对象,在构造方法中可以传参。
Android Dagger2使用详解_第14张图片

Android Dagger2使用详解_第15张图片

8.2 Subcomponent

使用Subcomponent,DatabaseComponentImpl只是一个内部类,我们发现DatabaseComponentImpl构造方法中不能传参。
在这里插入图片描述
Android Dagger2使用详解_第16张图片

所以我们尽量使用dependencies保持可扩展性。

代码Github下载:
https://github.com/345166018/AndroidIOC/tree/master/HxDagger2
https://github.com/345166018/AndroidIOC/tree/master/HxDagger2Sub

你可能感兴趣的:(android,dagger2,使用详解,Android架构设计)