美文网首页
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