Dagger2從入門到補胎(二)

使用

繼續學習在代碼中會看到的標註:@Named、@Qualifier、@Singleton、@Scope。這四個標註包括以前學的@Inject其實不是在dagger的包中,而是javax-inject包中:java

通過上篇的學習,結合@Inject、@Component、@Module、@Provides的使用,先看一個例子:android

City.javagit

public class City {
    private String name;

    public City() {}

    public String show() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}複製代碼

BeanModule.javagithub

@Module
public class BeanModule {
    @Provides
    City providerCityCD() {
        City city = new City();
        city.setName("成都");
        return city;
    }

    @Provides
    City providerCityZG(){
        City city = new City();
        city.setName("自貢");
        return city;
    }
}複製代碼

MainActivityComponent.java緩存

@Component(modules = BeanModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}複製代碼

MainActivity.javabash

public class MainActivity extends AppCompatActivity {
    public static String TAG = "hcy";
    @Inject
    City city;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //in Android Studio, select Build > Rebuild Project
        DaggerMainActivityComponent.create().inject(this);
        Log.d(TAG, "city name: " + city.show());
    }
}複製代碼

一跑起來就GG了ide

心路歷程是這樣的,MainActivity.java做爲依賴需求方,編譯生成MainActivity_MembersInjector,BeanModule.java做爲依賴提供方,編譯生成兩個Xx_Factory,那麼問題來了,有兩個「備胎」供選擇,你選誰?Component一臉懵逼,愣是搞不懂把哪兩個"綁定"起來,直接罷工了,所以能夠看到上篇中的DaggerMainActivityComponent在這裏沒有生成。函數

@Named

那麼告訴Component咱們要的是哪一個不就行啦?可使用@Named標註,改下BeanModule.java學習

@Module
public class BeanModule {
    @Named("CD")
    @Provides
    City providerCityCD() {
        City city = new City();
        city.setName("成都");
        return city;
    }

    @Named("ZG")
    @Provides
    City providerCityZG() {
        City city = new City();
        city.setName("自貢");
        return city;
    }
}複製代碼

MainActivity.javaui

public class MainActivity extends AppCompatActivity {
    public static String TAG = "hcy";
    @Named("CD")
    @Inject
    City city;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //in Android Studio, select Build > Rebuild Project
        DaggerMainActivityComponent.create().inject(this);
        Log.d(TAG, "city name: " + city.show());複製代碼

@Named("參數")和被引用的地方填的一致就能夠了,編譯運行就不會報錯了。此時編譯生成的文件:

熟悉的DaggerMainActivityComponent又回來了,並且兩個依賴提供者也都在,經過打印咱們知道選的是CD,那它是怎麼實現的呢?看下DaggerMainActivityComponent的initialize():

private void initialize(final Builder builder) {

    this.providerCityCDProvider = BeanModule_ProviderCityCDFactory.create(builder.beanModule);

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(providerCityCDProvider);
  }複製代碼

能夠看到,依賴提供方和需求方經過@Named加了相同的標註CD後,在建立工廠的時候只會把CD這個提供方初始化,ZG根本沒有露臉的機會,後面的流程就跟上篇同樣了。

@Qualifier

使用@Qualifier能夠實現@Named同樣的功能,Qualifier翻譯過來是修飾符的意思,先看下用法,再分析@Qualifier和@Named之間的「苟且」。添加一個接口CD.java和ZG.java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface CD {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ZG {}複製代碼

修改BeanModule.java以下:

@Module
public class BeanModule {
    @CD
    @Provides
    City providerCityCD() {
        City city = new City();
        city.setName("成都");
        return city;
    }

    @ZG
    @Provides
    City providerCityZG() {
        City city = new City();
        city.setName("自貢");
        return city;
    }
}複製代碼

MainActivity.java

public class MainActivity extends AppCompatActivity {
    public static String TAG = "hcy";
    @CD
    @Inject
    City city;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //in Android Studio, select Build > Rebuild Project
        DaggerMainActivityComponent.create().inject(this);
        Log.d(TAG, "city name: " + city.show());
    }
}複製代碼

修改後和輸出結果、編譯生成的類以及源碼都和@Named一毛同樣。@Qualifier可讓咱們本身建立限定符,其實@Named內部也是經過@Qualifier實現,源碼以下:

/**
 * String-based {@linkplain Qualifier qualifier}.
 *
 * <p>Example usage:
 *
 * <pre>
 *   public class Car {
 *     &#064;Inject <b>@Named("driver")</b> Seat driverSeat;
 *     &#064;Inject <b>@Named("passenger")</b> Seat passengerSeat;
 *     ...
 *   }</pre>
 */
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}複製代碼

從註釋可知@Named是@Qualifier的String型實現;看下github上一張介紹圖:

順便提一下這裏的@Retention,翻譯過來是保留的意思,它是用來指定咱們自定義的限定符能保留的多久,有三種可選:

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.
 */
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}複製代碼

@Singleton

@Singleton從名字就知道是單例,怎麼用這個?先看一個例子:

City.java

public class City {
    public City() {}

}複製代碼

BeanModule.java

@Module
public class BeanModule {
    @Named("CD")
    @Provides
    City providerCityCD() {
        City city = new City();
        return city;
    }

    @Named("ZG")
    @Provides
    City providerCityZG() {
        City city = new City();
        return city;
    }
}複製代碼

MainActivityComponent.java

@Component(modules = BeanModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}複製代碼

MainActivity.java

public class MainActivity extends AppCompatActivity {
    public static String TAG = "hcy";
    @Named("CD")
    @Inject
    City cityCD1;
    @Named("CD")
    @Inject
    City cityCD2;

    @Named("ZG")
    @Inject
    City cityZG1;
    @Named("ZG")
    @Inject
    City cityZG2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //in Android Studio, select Build > Rebuild Project
        DaggerMainActivityComponent.create().inject(this);
        Log.d(TAG, "cityCD1 is: " + cityCD1);
        Log.d(TAG, "cityCD2 is: " + cityCD2);
        Log.d(TAG, "cityZG1 is: " + cityZG1);
        Log.d(TAG, "cityZG2 is: " + cityZG2);
    }
}複製代碼

例子很簡單,打印city以下:

D/hcy     (13129): cityCD1 is: com.hcy.huchengyang.dagger2.bean.City@35051924
D/hcy     (13129): cityCD2 is: com.hcy.huchengyang.dagger2.bean.City@36acdc8d
D/hcy     (13129): cityZG1 is: com.hcy.huchengyang.dagger2.bean.City@14db0e42
D/hcy     (13129): cityZG2 is: com.hcy.huchengyang.dagger2.bean.City@28a95e53複製代碼

能夠看到這裏獲取到的對應4個不一樣的city,按照上一篇的分析套路能夠很快定位到緣由,查看編譯生成的MainActivity_MembersInjector.java中的injectMembers()方法

@Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.cityCD1 = cityCD1AndCityCD2Provider.get();
    instance.cityCD2 = cityCD1AndCityCD2Provider.get();
    instance.cityZG1 = cityZG1AndCityZG2Provider.get();
    instance.cityZG2 = cityZG1AndCityZG2Provider.get();
  }複製代碼

這裏會調用目標工廠的get方法,最後調用到BeanModule的方法建立對象;若是要確保單例,@Singleton就能夠派上用場了。

使用

1.在提供依賴的構造函數上加@Singleton

@Module
public class BeanModule {
    @Named("CD")
    @Provides
    @Singleton
    City providerCityCD() {
        City city = new City();
        return city;
    }

    @Named("ZG")
    @Provides
    City providerCityZG() {
        City city = new City();
        return city;
    }
}複製代碼

2.@Component標註的接口加上@Singleton

@Singleton
@Component(modules = BeanModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}複製代碼

修改後打印:

D/hcy     (13519): cityCD1 is: com.hcy.huchengyang.dagger2.bean.City@35051924
D/hcy     (13519): cityCD2 is: com.hcy.huchengyang.dagger2.bean.City@35051924
D/hcy     (13519): cityZG1 is: com.hcy.huchengyang.dagger2.bean.City@36acdc8d
D/hcy     (13519): cityZG2 is: com.hcy.huchengyang.dagger2.bean.City@14db0e42複製代碼

那麼它是怎麼保證單例的呢?查看源碼會發現MainActivity_MembersInjector.java中的injectMembers()方法和以前是同樣的,可是DaggerMainActivityComponent.java的initialize()方法發生了改變:

private void initialize(final Builder builder) {

    this.providerCityCDProvider =
        DoubleCheck.provider(BeanModule_ProviderCityCDFactory.create(builder.beanModule));

    this.providerCityZGProvider = BeanModule_ProviderCityZGFactory.create(builder.beanModule);

    this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(providerCityCDProvider, providerCityZGProvider);
  }複製代碼

這裏多了DoubleCheck的操做。看下DoubleCheck.java

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

  private volatile Provider<T> provider;
  private volatile Object instance = UNINITIALIZED;

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

  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {//第一次取值後下次再來判斷不知足直接返回以前的實例
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException();
          }
          instance = result;

          provider = null;
        }
      }
    }
    return (T) result;
  }

  public static <T> Provider<T> provider(Provider<T> delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
      return delegate;
    }
    return new DoubleCheck<T>(delegate);
  }
}複製代碼

整個類都沒多少代碼,省略了註釋和異常以及部分代碼,DoubleCheck和咱們的Factory都實現了共有接口Provider,保證單例的操做就是這裏的get()方法,註釋已解釋,能夠跟着流程走一波。這裏看下github上的一張圖,下圖左邊的對應沒有標註@Singleton,每次取時都會從新new;右邊有標註的在第一次建立時會緩存一份,下次再獲取直接就返回緩存的。

@Scope

使用@Scope能夠實現@Singleton同樣的功能,Scope翻譯過來是做用域的意思,其實@Singleton就是@Scope的實現(有點像上面@Named和@Qualifier的關係),源碼以下:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}複製代碼

咱們能夠徹底能夠本身定義一個接口如:MainActivityScope.java

@Scope
@Documented
@Retention(value= RetentionPolicy.RUNTIME)
public @interface MainActivityScope{}複製代碼

修改MainActivityComponent.java

@MainActivityScope
@Component(modules = BeanModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}複製代碼

修改BeanModule.java

@Module
public class BeanModule {
    @Named("CD")
    @Provides
    @MainActivityScope
    City providerCityCD() {
        City city = new City();
        return city;
    }

    @Named("ZG")
    @Provides
    City providerCityZG() {
        City city = new City();
        return city;
    }
}複製代碼

效果是同樣的,@Scope是成對使用的,在@Module的@Provides方法上使用@Scope標註,那麼對應的@Component也須要@Scope標準,此時@Provides方法提供的依賴在@Component中爲單例。

未完待續...

相關文章
相關標籤/搜索