美文网首页
Dagger 2 学习笔记

Dagger 2 学习笔记

作者: 这是一个随便起的昵称 | 来源:发表于2018-07-07 11:26 被阅读22次

学习资料在这

日常开发中使用到了 Dagger2 框架,一直知识按照别人的代码“依葫芦画瓢”,没有自己去好好的了解这个框架。最近花了时间去仔细学习了一下这个框架,然后把所学的东西总结一下。
为什么会有Dagger2这个框架产生?
一般我们在我们写的代码中,会这样实例化

public class A {
    
    public A() {
        
    }
}
 
public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        A mA = new A();
        Log.i(TAG, mA.toString());
    }
}

这样写一般来讲是没有什么大问题的,但是我们考虑下面的这个情况。由于业务需求我们需要在 A 中添加一个成员变量 mUser

public class User {
    public User(){
        
    }
}

public class A {

    private User mUser;
    
    public A(User user) {
        mUser = user;
    }
}
public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        A mA = new A(new User());
        Log.i(TAG, mA.toString());
    }
}

如果 A 被 100个地方实例化,那估计改程序的人要骂娘了。。。

可能会有人说重载构造方法,也可以解决这个问题。但是如果下一次需要你添加一个 Person 成员变量,下下次需要再添加... 显然这个解决方案并不利于代码维护。

顺便说一下另一个问题,这么多的 new 操作也是很浪费体力的一件事。
注入式框架就是为了解决这个问题而生的(Dagger是注入式框架的一种)。Dagger2 与其它注入式框架不同的是 dagger2 是编译时生成相关代码。而大部分其它注入式框架是运行时注入。

下面先来预先体验一下 用Dagger2 改造后的代码

@Component(modules = {AppModule.class})
public interface AppComponent {
    void inject(MainActivity mainActivity);
}

@Module
public class AppModule {
    @Provides
    public User provideUser(){
        return new User();
    }

    @Provides
    public A provideA(User user){
        return new A(user);
    }
}

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    @Inject
    A mA;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerAppComponent.create().inject(this);
        Log.i(TAG, mA.toString());
    }
}

可以看到我们省去了 具体的实例化过程。可能暂时还看不懂,但是我可以告诉你,不管以后需求需要添加什么成员变量,MainActivity中的代码“不需要动”。下面开始正式进入 Dagger2 !!!
引入 Dagger2 (Android Studio)

    compile 'com.google.dagger:dagger:2.11-rc2'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'

Dagger 2的几个重要知识点
0.@Inject
1.@Module
2.@Provides
3.@Component
4.@Qualifier
5.@Scope
6.@SubComponent
7.@Binds
以下内容,全部取自这个系列的Blog ,非常感谢原作者。
作者先是从日常开发中最常用的单例模式开始讲解

1.单例创建

  @Module
public class AppModule {
    private Context mContext;

    public AppModule(@NonNull Context context) {
        mContext = context;
    }

    @Provides
    @Singleton
    Context provideContext(){
        return mContext;
    }
}
@Module
public class ReceiversModule {
    @Provides
    @NonNull
    @Singleton
    public NetworkChannel provideNetworkChannel(){
        return new NetworkChannel();
    }
}

@Module
public class UtilsModule {

    @Provides
    @NonNull
    @Singleton
    public RxUtilsAbs provideRxUtils(Context context){
        return new RxUtils(context);
    }

    @Provides
    @NonNull
    @Singleton
    public NetworkUtils provideNetworkUtils(Context context, NetworkChannel networkChannel){
        return new NetworkUtils(context, networkChannel);
    }

}

正如我们知道的 @Module 注解是用来指明某个类用来提供依赖。我将会称这些类 为 Modules,并且我会这些提供依赖的方法为 provide method。例如 ReceiverModules 中有 provideNetworkChannel 方法。@Provides 标记的方法重要的是它的返回类型,返回类型可以是抽象类型,具体方法名是什么其实不那么重要,但是我们习惯以 provide开头。
看一下 UtilsModule 中的public NetworkUtils provideNetworkUtils(Context context, NetworkChannel networkChannel),他的方法中有两个参数,它会向 Dagger 表明我需要这两个参数创建 NetworkUtils 对象。这也是为什么我们会写了 AppModule 和 ReceiversModule的原因(提供这两个参数的依赖)。

下面创建Component

@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
    void inject(DaggerActivity daggerActivity);
}

@Component 是 @Module 和 @Inject 之间的一座桥梁。@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})这行代码向Dagger 表明AppComponent 由三个 Module组成。 每个 Module提供的依赖都能被其它Module使用,这三个模块被 AppComponent紧紧的连接在了一起!下面的图很好的表明了三者之间的关系


1*gdJbgBd-LqSf1KOtUiriOw.png

这张图很好的表明了 Dagger2 获取 Context 和 NetwrokChannel 去创建 NetworkUtils对象。举个例子,假设我们去掉 AppModule 那么Dagger 2就会报错,因为它不知道如何去获取 Context 对象。当然我们也可以把依赖注入到多个对象。

@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
    void inject(MainActivity mainActivity);
    void inject(SecondActivity secondActivity);
}

同样注入参数的名字不重要,类型才重要。注入的类型不能是通用类型,下面这种写法是不允许的!一定要是具体的类型!

@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
    void inject(Object object);
}

最后一步实现对象注入

public class AApplication extends Application {


    private static AApplication sAApplication;
    private static AppComponent sAppComponent;


    public static AApplication getAApplication(){
        return sAApplication;
    }

    public static AppComponent getAppComponent(){
        return sAppComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Utils.init(this);
        sAApplication = this;

        sAppComponent = buildAppComponent();
    }

    protected AppComponent buildAppComponent(){
        return DaggerAppComponent.builder().appModule(new AppModule(getApplicationContext())).build();
    }


}

public class DaggerActivity extends AppCompatActivity {
    @Inject
    RxUtilsAbs mRxUtilsAbs;

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

        AApplication.getAppComponent().inject(this);

        System.out.println(mRxUtilsAbs.toString());
        System.out.println();mNetworkUtils.toString();

    }
}

out

System.out: com.zsy.androidtraining.dagger2.entity.RxUtils@33bcd43
System.out: com.zsy.androidtraining.dagger2.entity.NetworkUtils@dd2ffc0

从输出的结果可以看出,对象注入成功。

注入的过程图

1*ZbjJyCisnVFAgtfxjkIZQA.png

可以把 AppComponent 理解成一个大的对象池,里面为你提供了所有你想获得的对象,所有实例由它管理。当然前提是你必须提供这些 实例化的方法,这就是 Module类的作用(当然@Inject 也可以提供依赖,后面会讲到)。

下面继续讲单例,这里就不跟随原作者的脚步了。一是因为作者的图上有一个小小的错误。二是因为这部分个人感觉从代码角度讲解更容易理解。
先看一下 @Singleton 的源码

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Scope 是一个特别重要的注解,利用这个注解可以实现我们自己的单例如:

@Scope
@Documented
@Retention(RUNTIME)
public @interface MyValidScope {}

作用完全一样!所以这里可以看出 其实名字不重要,还是那句话。
@Scope 表明的是一个有效作用域。就好比 任何变量都有一个作用域,如方法中 变量的作用域是只在这个方法中有效。方法外部是无法 获取 和 感知的。Dagger 2中同样有这一理念,Dagger 2 中同样需要“变量”有作用域。@Provides 标记的方法上 加上 @ MyValidScope 表明 在这个 我在“ MyValidScope”范围内有效。那这个“ MyValidScope”到底是什么范围呢?先看下面代码

@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@MyValidScope
public interface AppComponent {
    void inject(Object object);
}

@MyValidScope 被标记在了 AppComponent 上,表明我的有效范围是在 AppComponent 中, 在这个范围内有效当然是单例。就好像一个类中的成员变量在这个类范围内当然是“单例”。

@Component(modules = {AppModule.class, UtilsModule.class, ReceiversModule.class})
@Singleton
public interface AppComponent {
    void inject(Object object);
}

如果AppModule, UtilsModule, ReceiversModule 中任何 一个 方法 被 @Singleton 标记,那么上面的代码中的 @Singleton 必须要 写上。否则 实例将会为自己的作用范围而感到“困惑”,就是报错啦!你可以试一下!

下面来看一下编译成功后生成的代码(省略掉一些非必要的代码)。这个代码是加上@Singleton后生成的代码

@Singleton
    public NetworkChannel provideNetworkChannel(){
        return new NetworkChannel();
    }
public final class DaggerAppComponent implements AppComponent {
    
    private Provider<NetworkChannel> provideNetworkChannelProvider;
  
    ...
  
    private DaggerAppComponent(Builder builder) {
      assert builder != null;
      initialize(builder);
    }
  
    public static Builder builder() {
      return new Builder();
    }
  
    @SuppressWarnings("unchecked")
    private void initialize(final Builder builder) {
  
     this.provideNetworkChannelProvider =
        DoubleCheck.provider(
            ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule));
  
      ...
    }
  
   ...
  }

我们需要重点关注一下这个 DoubleCheck.provider方法,继续看源码(去掉不必要的注释)

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();
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result + ". This is likely "
                + "due to a circular dependency.");
          }
          instance = result;
          provider = null;
        }
      }
    }
    return (T) result;
  }

  /** Returns a {@link Provider} that caches the value from the given delegate provider. */
  public static <T> Provider<T> provider(Provider<T> delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
      return delegate;
    }
    return new DoubleCheck<T>(delegate);
  }
  public static <T> Lazy<T> lazy(Provider<T> provider) {
    if (provider instanceof Lazy) {
      @SuppressWarnings("unchecked")
      final Lazy<T> lazy = (Lazy<T>) provider;
      return lazy;
    }
    return new DoubleCheck<T>(checkNotNull(provider));
  }
}

可以看到 其实现实例的方法

 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("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result + ". This is likely "
                + "due to a circular dependency.");
          }
          instance = result;
          provider = null;
        }
      }
    }

第一次获取会去“创建”,并且会保存结果,之后每次获取都是返回之前创建好的值。
再来看一下没加@Singleton后的代码

加@Singleton

this.provideNetworkChannelProvider =
        DoubleCheck.provider(
            ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule));
  

没加@Singleton

 this.provideNetworkChannelProvider =
        ReceiversModule_ProvideNetworkChannelFactory.create(builder.receiversModule);

代码变成了这样,这个可以真正看出 @Singleton的作用所在!!
可能有点迷糊,后面会写一篇注入原理的笔记。暂时先理解这个作用域的概念很重要。

相关文章

网友评论

      本文标题:Dagger 2 学习笔记

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