05.对注解的分类讲解——Module(二)

关于Dagger2

  • Dagger基础用法
  • 对注解的分类讲解——Inject部分
  • 对注解的分类讲解——Module部分(一)
  • 对注解的分类讲解——Component部分
  • 对注解的分类讲解——Module(二)
  • 对注解的分类讲解——Module(三)
  • 对Dagger中几个类的说明
  • Dagger-Android的使用

前言

本文将通过对注解的分类讲解来交代Dagger2的进阶用法。

关于Dagger的注解,简单将它们分成三类:

  • Component部分
  • Module部分
  • Inject部分
注解分类

来对应Dagger完成依赖注入的三部分内容,了解Dagger三部分最基本的那些使用方式,接下来来看看关于Module部分进阶的内容吧。

Module部分注解

@Binds

不同于@Provides注解,当参数和返回值类型相同时,将方法写成抽象方法,用Binds注解,这是一种简写,只能在返回值定义是抽象函数或是接口,具体返回值是具体的实现类时才能使用,举个例子:

// 定义一个抽象类
public abstract class AbsBindAnnotations {
    public abstract void log();

    public void logSelf() {
        Log.d("Dagger测试", "获取到AbsBindAnnotations对象啦");
    }
}
public class BindAnnotations extends AbsBindAnnotations {

    // 需要在需要提供的对象的构造函数上加上@Inject注解
    @Inject
    public BindAnnotations() {}

    @Override
    public void log() {
        Log.d("Dagger测试", "获取到了BindAnnotations对象");
    }
}

接下来来创建Module类:

// 如果类中的方法是抽象方法,这个类也需要进行抽象化处理
@Module
public abstract class BindAnnotationsModule {
    
    // 通过@Binds注解简化原有的提供方式
    @Binds
    abstract AbsBindAnnotations providerBindAnnotations(BindAnnotations bindAnnotations);
}

最后通过Component类来进行连接:

@Component(modules = BindAnnotationsModule.class)
public interface BindAnnotationsComponent {
    void inject(BindAnnotationsActivity activity);
}

在Activity中进行调用:

public class BindAnnotationsActivity extends AppCompatActivity {

    @Inject
    BindAnnotations mBindAnnotations;

    @Inject
    AbsBindAnnotations mAbsBindAnnotations;

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

        DaggerBindAnnotationsComponent.create().inject(this);

        mBindAnnotations.log();
        mAbsBindAnnotations.logSelf();
    }
}

通过这种方式就可以实现抽象方法的对象注入了,但是要注意的是,提供对象的方法必须要是抽象函数作为定义的返回值,而具体返回的值必须是抽象类的实现类:

@Binds
abstract AbsBindAnnotations providerBindAnnotations(BindAnnotations bindAnnotations);

这里要透露一个小秘密哦:抽象类换成接口可以的哦,很符合面向对象的编程方式,自己去尝试一下吧。

此外,当Binds和Provides两种注解同时出现在同一个Module中时需要做下处理,有两种方案可以选择:

  • 最简单的方式就是将@Provides标示的方法设置成static
  • 如果不使用static的话,那么就将所有的@Binds修饰的抽象方法移到一个抽象的Module中

举个例子(采用Binds和Provides两种注解并存解决办法其二):

public abstract class Factory {}
public class FactoryA extends Factory {}
public class FactoryB extends Factory {}
@Module
public abstract class AppModule {
    @Singleton
    @Binds
    public abstract Factory bindsFactory(FactoryB factoryB);

    @Singleton
    @Provides
    public static FactoryA providesFactoryA() {
        return new FactoryA();
    }

    @Singleton
    @Provides
    public static FactoryB providesFactoryB() {
        return new FactoryB();
    }
}

@BindsOptionalOf

@BindsOptionalOf是为了注入Optional类型对象,关于Optional类型,Optional是JDK1.8引入的辅助类,其包裹的对象可能为空,如调用Optional.isPresent()方法判断其内部是否为空。

这里来举一个例子吧,创建一个实体类:

public class OptionalAnnotations {
    public void log() {
        Log.d("Dagger测试", "获取到OptionalAnnotations对象");
    }
}

通过@Binder的方式在Module中提供一个实例:

@Module
public abstract class OptionalAnnotationsModule {
    // 使用了@BindsOptionalOf注解表明这是一个提供的可空类型数据
    // 注意这里我没有传入任何参数,所以提供的对象应该是一个null的对象
    @BindsOptionalOf
    abstract OptionalAnnotations providerOptionalAnnotations();
}

最后通过Component进行连接:

@Component(modules = OptionalAnnotationsModule.class)
public interface OptionalAnnotationsComponent {
    void inject(OptionalAnnotationsActivity activity);
}

在Activity中进行调用:

public class OptionalAnnotationsActivity extends AppCompatActivity {

    // 注入对象,需要声明为Optio形式
    @Inject
    Optional mOptionalAnnotations;

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

        DaggerOptionalAnnotationsComponent.create().inject(this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 对对象的空值进行判断
            if (mOptionalAnnotations.isPresent()) {
                mOptionalAnnotations.get().log();
            } else {
                Log.d("Dagger测试", "获取到的OptionalAnnotations对象为空哦");
            }
        }
    }
}

打印结果:


打印结果

可以看到确实返回了为null,如果在提供对象的时候设置了返回的对象这里就可以获得不为null 的对象啦。

@Reusable

@Reusable注解的功能与@Singleton注解类似,两者不同点在于@Singleton注解是某种意义上的区域性的单例,而@Reusable注解只不过是为了简单复用而使用的一种方式,在多线程情况下@Reusable注解还是会产生多个对象不能保持单例的状态。

两个的区别在于构造过程中分别使用了DoubleCheck和SingleCheck,简单看一下它们的源码就知道它们的差别所在了:

SingleCheck

@Reusable注解所使用的是SingleCheck做检测:

public T get() {
    Object local = instance;
    if (local == UNINITIALIZED) {
      Provider providerReference = provider;
      if (providerReference == null) {
        local = instance;
      } else {
        local = providerReference.get();
        instance = local;
        provider = null;
      }
    }
    return (T) local;
  }

DoubleCheck

@Singleton注解所使用的是DoubleCheck做检测:

public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          instance = reentrantCheck(instance, result);
          provider = null;
        }
      }
    }
    return (T) result;
  }

对单例模式熟悉的话,可以发现这两者区别在于对实例的检测方式,所以要使用@Reusable和@Singleton这两个注解的时候首先要分清楚使用的场景哦。

你可能感兴趣的:(05.对注解的分类讲解——Module(二))