美文网首页Android技术知识Android开发Android开发
Dagger 2 在 Android 上的使用(三)

Dagger 2 在 Android 上的使用(三)

作者: 于卫国 | 来源:发表于2019-01-05 15:48 被阅读3次

本文介绍了Dagger 2 中@Qualifier和@Named注解以及Lazy和Provider的使用。

本文首发:http://yuweiguocn.github.io/

《卜算子·咏梅》
风雨送春归,飞雪迎春到。已是悬崖百丈冰,犹有花枝俏。
俏也不争春,只把春来报。待到山花烂漫时,她在丛中笑。
-近代,毛泽东

注解@Qualifier和注解@Named

书接上文,在上文中我们使用注解@Binds完成了对People抽象类的其中一个子类的绑定,如果我们想同时注入两个子类该怎样处理?

public abstract class People {
    abstract String doWhat();
}

public class Student extends People {
    @Inject
    public Student() {
    }

    @Override
    String doWhat() {
        return "study";
    }
}


public class Worker extends People {

    @Inject
    public Worker() {
    }

    @Override
    String doWhat() {
        return "work";
    }
}

在Module类中添加对另一个子类Worker的绑定方法,这里我们用到了限定符注解@Named,由于我们提供了多个依赖导致Dagger2无法识别要注入哪一个依赖,使用限定符注解@Named很好地解决了这种问题。

@Module
public abstract class PeopleModule {

    @Binds
    @Named("study")
    abstract People bindStudent(Student student);

    @Binds
    @Named("work")
    abstract People bindWorker(Worker worker);

}


@Component(modules = PeopleModule.class)
public interface PeopleComponent {

    void inject(PeopleActivity activity);
}

在提供的实例添加注解@Named,在使用依赖注入的地方添加同样的注解:

public class PeopleActivity extends Activity {

    @Inject
    @Named("study")
    People student;

    @Inject
    @Named("work")
    People worker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleComponent.create().inject(this);
        Log.d("debug", student.doWhat());
        Log.d("debug", worker.doWhat());
    }
}

同样我们也可以使用限定符注解@Qualifier通过自定义注解完成相同的功能。

使用注解@Qualifier自定义注解:

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

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

然后将自定义的注解替换上面注解@Named

@Module
public abstract class PeopleModule {

    @Binds
    @Study
    abstract People bindStudent(Student student);

    @Binds
    @Work
    abstract People bindWorker(Worker worker);

}

public class PeopleActivity extends Activity {

    @Inject
    @Study
    People student;

    @Inject
    @Work
    People worker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleComponent.create().inject(this);
        setContentView(R.layout.activity_main);
        Log.d("debug", student.doWhat());
        Log.d("debug", worker.doWhat());
    }
}

由于使用注解@Named需要指定字符串,可能会由于人为疏忽造成错误,所以这里推荐使用限定符注解@Qualifier自定义注解的方法

Lazy和Provider

我们可以使用Lazy在注入时不进行初始化,在调用get方法时再初始化实例:

public class PeopleActivity extends Activity {

    @Inject
    @Work
    Lazy<People> worker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleComponent.create().inject(this);
        setContentView(R.layout.activity_main);
        Log.d("debug", worker.get().doWhat());
    }
}

然后来看一下生成的代码:

public final class DaggerPeopleComponent implements PeopleComponent {
  ...
  private PeopleActivity injectPeopleActivity(PeopleActivity instance) {
    PeopleActivity_MembersInjector.injectWorker(
        instance, DoubleCheck.lazy((Provider) Worker_Factory.create()));
    return instance;
  }
  ...
}

这里终于用到了生成的工厂类:

public final class Worker_Factory implements Factory<Worker> {
  private static final Worker_Factory INSTANCE = new Worker_Factory();

  @Override
  public Worker get() {
    return provideInstance();
  }

  public static Worker provideInstance() {
    return new Worker();
  }

  public static Worker_Factory create() {
    return INSTANCE;
  }

  public static Worker newWorker() {
    return new Worker();
  }
}

这里使用双重检查锁定为我们提供了唯一的实例,分析具体可参见:Java实现单例模式的正确姿势

/**
 * A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a
 * delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>.
 */
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;
  }

  @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) {
          result = provider.get();
          instance = reentrantCheck(instance, result);
          /* 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;
  }

  /**
   * Checks to see if creating the new instance has resulted in a recursive call. If it has, and the
   * new instance is the same as the current instance, return the instance. However, if the new
   * instance differs from the current instance, an {@link IllegalStateException} is thrown.
   */
  public static Object reentrantCheck(Object currentInstance, Object newInstance) {
    boolean isReentrant = !(currentInstance == UNINITIALIZED
        // This check is needed for fastInit's implementation, which uses MemoizedSentinel types.
        || currentInstance instanceof MemoizedSentinel);

    if (isReentrant && currentInstance != newInstance) {
      throw new IllegalStateException("Scoped provider was invoked recursively returning "
          + "different results: " + currentInstance + " & " + newInstance + ". This is likely "
          + "due to a circular dependency.");
    }
    return newInstance;
  }

  ...

  /** Returns a {@link Lazy} that caches the value from the given provider. */
  // This method is declared this way instead of "<T> Lazy<T> lazy(Provider<T> delegate)"
  // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
  public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) {
    if (provider instanceof Lazy) {
      @SuppressWarnings("unchecked")
      final Lazy<T> lazy = (Lazy<T>) provider;
      // Avoids memoizing a value that is already memoized.
      // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
      // are different types using covariant return on get(). Right now this is used with
      // DoubleCheck<T> 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<T>(checkNotNull(provider));
  }
}

相对于Lazy注入,Provider注入每次调用get时都会提供一个新的实例,Provider是由Java提供的接口:

public class PeopleActivity extends Activity {

    @Inject
    @Study
    Provider<People> student;

    @Inject
    @Work
    Lazy<People> worker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerPeopleComponent.create().inject(this);
        setContentView(R.layout.activity_main);
        Log.d("debug", "1"+student.get().toString());
        Log.d("debug", "2"+student.get().toString());
        Log.d("debug", "3"+worker.get().toString());
        Log.d("debug", "4"+worker.get().toString());
    }
}

打印输出如下:

2018-11-12 21:26:26.365 30805-30805/? D/debug: 1io.github.yuweiguocn.test.Student@205713d
2018-11-12 21:26:26.365 30805-30805/? D/debug: 2io.github.yuweiguocn.test.Student@6fffd32
2018-11-12 21:26:26.365 30805-30805/? D/debug: 3io.github.yuweiguocn.test.Worker@d03a83
2018-11-12 21:26:26.365 30805-30805/? D/debug: 4io.github.yuweiguocn.test.Worker@d03a83

对比下生成的代码,唯一的区别就是Lazy使用DoubleCheck保证了实例的唯一性:

public final class DaggerPeopleComponent implements PeopleComponent {
  ...

  private PeopleActivity injectPeopleActivity(PeopleActivity instance) {
    PeopleActivity_MembersInjector.injectStudent(instance, (Provider) Student_Factory.create());
    PeopleActivity_MembersInjector.injectWorker(
        instance, DoubleCheck.lazy((Provider) Worker_Factory.create()));
    return instance;
  }
  ...
}

总结

  • 当提供多个相同类型的注入依赖时,可以使用限定符注解@Qualifier和注解@Named,为了避免人为疏忽导致错误,推荐使用注解@Qualifier自定义注解完成相应功能
  • 使用Lazy可以在调用get方法时再初始化,其中使用了双重检查锁定保证了实例的唯一性
  • 使用Provider在调用get方法时会提供新的实例

参考

相关文章

网友评论

    本文标题:Dagger 2 在 Android 上的使用(三)

    本文链接:https://www.haomeiwen.com/subject/fkuzxqtx.html