Dagger2基本使用与原理

上一篇介绍了Dagger2的一些注解点击打开链接

一、Dagger2的一些简单配置

1.在build.gradle(project..)中添加apt插件

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

2.在build.gradle(Module:app)中添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
    provided 'org.glassfish:javax.annotation:10.0-b28'
    compile 'com.google.dagger:dagger:2.5'
    compile 'com.google.dagger:dagger-compiler:2.5'
}
dragger2引入是会出现一些问题,配置这些即可

dexOptions {
    javaMaxHeapSize "4g"
}
defaultConfig {
        ...
        multiDexEnabled true
    }

3.关于dagger2与Retrofit2之间分包的问题
multiDexEnabled true设置为true会导致找不到Retrofit
如果关闭,又会报 64K什么的 方法错误
解决办法
compile 'com.android.support:multidex:1.0.1'
如果继承了Application
还需重写
/**
 * 分割 Dex 支持
* @param base
*/
@Override
protected void attachBaseContext(Context base) {
         super.attachBaseContext(base);
          MultiDex.install(this);
}

二、注解的使用

1.使用@Inject来提供依赖

创建两个实体类,Food构造方法不需要传参数,但是我们也用@Inject标注了,这是因为在Person构造方法中需要传入Food
public class Food {
    @Inject
    public Food() {
    }
    public String eated(){
        return "吃东西!!!";
    }
}
public class Person {
    public Food food;
    @Inject
    public Person(Food food) {
        this.food = food;
    }
    public String eat(){
        return food.eated();
    }
}

Component作为桥梁,将MainActivity与Food连接
@Component
public interface FoodComponent {
    void inject(MainActivity mainActivity);
}
创建Component后,build项目,会生成一个DaggerComponent。这个实现类提供了将实例化好的对象注入到所需的MainActivity中
MainACtivity中
public class MainActivity extends AppCompatActivity {
    @Bind(R.id.info)
    TextView info;
    @Inject
    Person person;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        DaggerFoodComponent.builder()
                .build().inject(this);
        info.setText(person.eat());
    }
}
DaggerFoodComponent.builder().build().inject(this);实现依赖注入。上源码吧,不然说不清了 - -。。。
public final class DaggerFoodComponent implements FoodComponent {
  private Provider<Person> personProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private DaggerFoodComponent(Builder builder) {
    assert builder != null;
    initialize(builder);//3---->build方法会调用initialize方法
  }

  public static Builder builder() { //1-------->先在这里被调用
    return new Builder();
  }

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {//4------>这个方法会实例化内部属性,通过简单工厂就生成了一个对象

    this.personProvider = Person_Factory.create(Food_Factory.create());//这里你会发现personProvider对象被实例化了,但是里面还有一个FoodProvider对象,再看看我们的实体类,里面确实有一个FoodProvider,所以我们有需要通过Food工厂获得foodProvider对象,Factory继承自Provider


    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(personProvider);//这里就是初始化MembersInjector这个对象


  }

  @Override
  public void inject(MainActivity mainActivity) {//-5----->最后将实例化好的对象注入到Activity/class/Fragment

    mainActivityMembersInjector.injectMembers(mainActivity);//6---->而这个方法(针对我们当前示例)就是把activity中的person对象实例化了

  }

  public static final class Builder {
    private Builder() {}

    public FoodComponent build() {//2--------->调用build方法
      return new DaggerFoodComponent(this);
    }
  }
}
Food工厂和Person工厂源码
public enum Food_Factory implements Factory<Food> {
  INSTANCE;

  @Override
  public Food get() {
    return new Food();
  }

  public static Factory<Food> create() {
    return INSTANCE;
  }
}

public final class Person_Factory implements Factory<Person> {
  private final Provider<Food> foodProvider;

  public Person_Factory(Provider<Food> foodProvider) {
    assert foodProvider != null;
    this.foodProvider = foodProvider;
  }

  @Override
  public Person get() {
    return new Person(foodProvider.get());
  }

  public static Factory<Person> create(Provider<Food> foodProvider) {
    return new Person_Factory(foodProvider);
  }
}
MainActivity_MembersInjector源码;
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
    private final Provider<Person> personProvider;

    public MainActivity_MembersInjector(Provider<Person> personProvider) {
        assert personProvider != null;

        this.personProvider = personProvider;
    }

    public static MembersInjector<MainActivity> create(Provider<Person> personProvider) {
        return new MainActivity_MembersInjector(personProvider);
    }

    public void injectMembers(MainActivity instance) {//7----->通过Factory的get方法获得实例,赋值给当前activity中的person。
        if(instance == null) {
            throw new NullPointerException("Cannot inject members into a null reference");
        } else {
            instance.person = (Person)this.personProvider.get();
        }
    }

    public static void injectPerson(MainActivity instance, Provider<Person> personProvider) {
        instance.person = (Person)personProvider.get();
    }
}

实例化DaggerFoodComponent,并且是怎么提供依赖注入的过程在上述源码中说的清清楚楚。
总的来说就是 Component作为桥梁,将被@Inject标记的所需要的变量,在对应的DaggerXXX中实例化好,再注入到所被@Inject标记的地方(这个地方可以是class,activity,fragment)。

2.@Module

使用Module来提供注入,先把原先实体类中构造方法上的@Inject去掉;
接着创建Module类。因为在Activity中我们需要Person对象,故我们在这里提供一个Person的方法,但是person创建的时候又需要一个Food,故我们还要提供一个Food的方法。(在这里说明一点,关于Food对象的实例化,我可以删除Module中提供的方法,然后在构造方法中继续用@Inject标注,哈哈哈哈是不是很神奇!!!)其实呢,无论是@Inject标注的构造方法还是@Provides标注的方法都会产生对应的一个工厂。而如果方法中的在Module中与构造方法中都是互通的,因为他们最终还是生成了对应的工厂。
@Module
public class FoodModule {
    @Provides
    public Food provideFood(){
        return new Food();
    }
    @Provides
    public Person providePerson(Food food){
        return new Person(food);
    }
}
这里的Component接口中就需要声明是那个或哪些modules为Activity提供依赖
@Component(modules = {FoodModule.class})
public interface FoodComponent {
    void inject(MainActivity mainActivity);
}

3.@Qualifier

用来区分提供依赖的方法和需要依赖的变量
新建Person1标注。
在Module的Person2方法上添加标注。
@Module
public class FoodModule {
    @Provides
    public Person providePerson(Food food){
        return new Person(food);
    }
    @Person1
    @Provides Person providePerson2(Food food){
        return new Person(food);
    }
}

Activity中
在person1上添加标注。
@Inject
Person person;
@Person1
@Inject
Person person1;
这样就能避免依赖方法不知给谁提供依赖了。

4.关于Singleton与自定义的Scope,用以下这张图表示(copy来的和下面的类名虽然对不上,但是没有影响)。


具体的实现。
举个例子,用过Retrofit的都知道,网络请求的时候我们需要通过Retrofit来实现。而且确保它是单一并且能供全局使用的。
那么我们在APPComponent中提供OkHttpCilent方法和Retrofit方法;这里就用到了@Singleton;
首先创建APPModule
@Module
public class AppModule {
    private Context context;

    public AppModule(App app) {
        this.context = app;
    }
    @Singleton
    @Provides
    public Context providerApplicationContext() {
        return this.context;
    }

    @Singleton
    @Provides
    public OkHttpClient provideClient() {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .readTimeout(5, TimeUnit.SECONDS)
                .build();
        return client;
    }
    @Singleton
    @Provides
    public Retrofit provideRetrofit(OkHttpClient client){
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://127.0.0.1:8080/")
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(client)
                .build();
        return retrofit;
    }
}
用Singleton标注视情况而定,如果你要保证它是唯一的,那么就用Singleton标注。
创建AppComponent
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
    OkHttpClient getClient();
    Retrofit getRetrofit();
}
用Singleton标注,说明它是全局的单例
然后FoodComponent,添加AppComponent作为它的依赖,并且还需要给Component添加作用域,不然会编译报错。原因是因为,依赖的AppComponent是有作用域的(无论什么作用域),而作为被依赖的Component时也需要一个Scope
@ActivityScope
@Component(modules = {FoodModule.class},dependencies = {AppComponent.class})
public interface FoodComponent {
    void inject(MainActivity mainActivity);
}
Activity中的代码
@Bind(R.id.info)
TextView info;

@Named("1")
@Inject
Person person;
@Inject
Retrofit retrofit;
@Inject
OkHttpClient client;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    DaggerFoodComponent.builder().appComponent(App.get(this).getAppComponent())
            .foodModule(new FoodModule())
            .build()
            .inject(this);
    info.setText(person.eat()+"\nretrofit="+retrofit.toString());
}
关于引用的问题,其实就是将一个AppComponent的引用传递到了DaggerFoodComponent中。
除了用dependencies还可以用SubComponent,两者实现的效果是一样的。

当然改成SubComponent也不难,只需要改Component即可。

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
    FoodComponent addSub(FoodModule foodModule);
    OkHttpClient getClient();
    Retrofit getRetrofit();
}
@ActivityScope
//@Component(modules = {FoodModule.class},dependencies = {AppComponent.class})
@Subcomponent(modules = {FoodModule.class})
public interface FoodComponent {
    void inject(MainActivity mainActivity);
}
App.get(this).getAppComponent().addSub(new FoodModule()).inject(this);
显示结果:

结果表明,我们即实例化了Person,也实例化了Retrofit。

5.其他的一些用法

Dagger2还支持Lazy模式,通过Lazy模拟提供的实例,在@Inject的时候并不初始化,而是等到你要使用的时候,主动调用其.get方法来获取实例。
@Named("1")
@Inject
Lazy<Person> person;

Person p = person.get();
info.setText(p.eat()+"\nretrofit="+retrofit.toString());

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试