• Dagger2使用攻略


    Dagger2使用攻略

    Dagger 2 是 Square 的 Dagger 分支,是一种依赖注入框架。眼下由 Google 接手进行开发,Dagger2是使用代码自己主动生成和手写代码来实现依赖注入。据说在 Dagger 的基础上效率又提升了13%。而且相同功能强大。

    1.Gradle配置

    (1)须要配置apt 插件:(在project根文件夹build.gradle文件里加入例如以下代码)

    dependencies {
            ...
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }

    (2)加入依赖:(在modulebuild.gradle文件里加入例如以下代码)

    apply plugin: 'com.neenbedankt.android-apt'// 凝视处理
    
        dependencies {
            ...
            compile 'com.google.dagger:dagger:2.0.2'
            apt 'com.google.dagger:dagger-compiler:2.0.2'
            compile 'org.glassfish:javax.annotation:10.0-b28' // Java标注
        }

    ● 当前最新版本号是2.0.2

    ● 加入2、3条依赖的原因參考:Dagger2入坑指南

    ● 假设在项目中同一时候用了Butterknife,在Build时会报凝视冲突。
    这里写图片描写叙述

    解决方法:(在modulebuild.gradle文件里加入例如以下代码)

     packagingOptions {
            exclude 'META-INF/services/javax.annotation.processing.Processor'
     }

    (3)最后点击Build-->Make Project就能够開始使用Dagger2了。

    2.Dagger2 经常使用注解

    写了一个简单的Demo,以下依据Demo进行介绍。

    Dagger2要理解必须看Dagger 2自己主动生成的代码。Build后代码在项目-->app-->build-->generated-->source-->apt-->debug文件夹下。

    1.Inject

    @Inject:在须要依赖的地方使用这个注解。告诉Dagger这个类或者字段须要依赖注入,这样Dagger会构造一个这个类实例来满足依赖。

    1.构造器注入:首先举一个简单的样例。无參构造方法。

    public class Person {
        private String name;
        private int age;
    
        @Inject
        public Person() {
        }
    
        public String getName() {
            return "Jack";
        }
    
        public int getAge() {
            return 15;
        }
    
    }

    这个的局限性是我们不能给这个类中的多个构造器作@Inject注解。

    2.注解成员变量:

    接着上面我们要使用这个实例化类。

    public class MainActivity extends AppCompatActivity {
    
        @Inject
        Person mPerson;
    
        StorageComponent mStorageComponent;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mStorageComponent = ((MyApplication)this.getApplication()).getStorageComponent();
            mStorageComponent.inject(this);//注入MainActivity
    
            Toast.makeText(this,mPerson.getName() + "----" +mPerson.getAge(),Toast.LENGTH_SHORT).show();
         }
     }

    这里我们能够查看生成的代码:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
      private final MembersInjector<AppCompatActivity> supertypeInjector;
      private final Provider<Person> mPersonProvider;
    
      public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Person> mPersonProvider) {  
    
        assert supertypeInjector != null;
        this.supertypeInjector = supertypeInjector;
        assert mPersonProvider != null;
        this.mPersonProvider = mPersonProvider;
      }
    
      @Override
      public void injectMembers(MainActivity instance) {  
        if (instance == null) {
          throw new NullPointerException("Cannot inject members into a null reference");
        }
        supertypeInjector.injectMembers(instance);
        instance.mPerson = mPersonProvider.get();//这里mPersonProvider替我们实例化了Person
      }
    
      public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Person> mPersonProvider) {  
          return new MainActivity_MembersInjector(supertypeInjector, mPersonProvider);
      }
    }
    
    

    同一时候我们也能够了解到@Inject Person mPerson; 为什么不能使用private 。上面代码中的injectMembers 方法调用后面会说到。

    3.方法注入

    public class LoginActivityPresenter {
    
        private LoginActivity loginActivity;
    
        @Inject //构造方法注入
        public LoginActivityPresenter(LoginActivity loginActivity) {
            this.loginActivity = loginActivity;
        }
    
        @Inject //方法注入
        public void enableWatches(Watches watches) {
            watches.register(this);   
        }
    }

    如当我们希望传入类的当前实例(this引用)到被注入的依赖中。方法注入会在构造器调用后立即被调用,所以这表示我们能够传入全然被构造的this。

    2.Module

    @Module:用来修饰类,表示此类的方法是用来提供依赖的,它告诉Dagger在哪里能够找到依赖。

    @Module
    public class StorageModule {
    
        private final MyApplication application;
    
        public StorageModule(MyApplication application) {
            this.application = application;
        }
    
        @Provides
        @Singleton
        SharedPreferences provideSharedPreferences(){
            return PreferenceManager.getDefaultSharedPreferences(application);
        }
    
    }
    

    @Provides以下说到,@Singleton 单例,使用@Singleton注解之后。对象仅仅会被初始化一次。之后的每次都会被直接注入相同的对象。@Singleton就是一个内置的作用域。

    3.Provides

    @Provides:在@Module 中使用。我们定义的方法用这个注解,用于告诉 Dagger 我们须要构造实例并提供依赖。

    为什么要使用@Provides,由于默认情况下,Dagger满足依赖关系是通过调用构造方法得到的实例,比方上面的Person类使用。可是有时由于@Inject 的局限性。我们不能使用@Inject。比方构造方法有多个、三方库中的类我们不知道他是怎么实现的等等。比如以下代码中的SharedPreferences。我们使用@Provides 返回一个创建好的实例,这样做也显得灵活不是吗?

        @Provides
        @Singleton
        SharedPreferences provideSharedPreferences(){
            return PreferenceManager.getDefaultSharedPreferences(application);
        }

    注意:

    ● 依照习惯 @Providers方法都会用provide作为前缀,@Module类都用Module作为后缀。

    ● 假设@Provides方法有參数。这个參数也要保证能够被Dagger得到(比如通过其它的@Provides方法或者@Inject注解的构造方法。)

    4.Component

    @Component: 是@Inject@Module的桥梁,须要列出所有的@Modules以组成该组件。

    @Singleton
    @Component(modules = {
            StorageModule.class ,
            ScheduleModule.class
    })
    public interface StorageComponent {
    
        Storage execute();
        void inject(MainActivity mMainActivity);
    }

    Dagger会依照上面接口生成一个实现类。生成类以Dagger为前缀。提供builder()来生成实例。调用方法:(由于是单例,所以这里放到了MyApplication)

    public class MyApplication extends Application {
    
        private StorageComponent component;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            component = DaggerStorageComponent
                    .builder()//调用构建类
                    .storageModule(new StorageModule(this)) //传入Module
                    .build();//生成实例
    
        }
    
        public StorageComponent getStorageComponent() {
            return component;
        }
    
    }

    MainActivity代码:

    public class MainActivity extends AppCompatActivity {
    
        @Bind(R.id.button1)
        Button mButton1;
    
        @Bind(R.id.button2)
        Button mButton2;
    
        @Inject
        SharedPreferences mPreferences;//全局的SharedPreferences
    
        @Inject
        Person mPerson;
    
        StorageComponent mStorageComponent;
        private final String KEY = "Dagger 2";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
    
            mStorageComponent = ((MyApplication)this.getApplication()).getStorageComponent();
            mStorageComponent.inject(this);//注入MainActivity
            mStorageComponent.execute().storage();//运行储存操作
    
        }
    
        @OnClick({R.id.button1,R.id.button2})
        void onButtonClicked(View v) {
            switch (v.getId()) {
                case R.id.button1:
                    Toast.makeText(this,mPreferences.getString(KEY,"---"),Toast.LENGTH_SHORT).show();
                    //上面是演示样例获取mPreferences,实际中将SharedPreferences操作都能够封装进Storage中。例如以下
                    //Toast.makeText(this,
                    //mStorageComponent.execute().getStorage(),Toast.LENGTH_SHORT).show();
                    break;
                case R.id.button2:
                    Toast.makeText(this,
                    mPerson.getName() + "----" + mPerson.getAge(),Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }

    下来把整个流程走一遍:首先进入MyApplication 运行DaggerStorageComponent.builder().storageModule(new StorageModule(this)).build(); 方法获取实例化StorageComponent。那我我们查看DaggerStorageComponent 类:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class DaggerStorageComponent implements StorageComponent {
      private Provider<SharedPreferences> provideSharedPreferencesProvider;
      private Provider<ScheduleImpl> provideScheduleProvider;
      private Provider<Storage> storageProvider;
      private MembersInjector<MainActivity> mainActivityMembersInjector;
    
      private DaggerStorageComponent(Builder builder) {  
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {  
        return new Builder();
      }
    
      private void initialize(final Builder builder) {  
        this.provideSharedPreferencesProvider = ScopedProvider.create(StorageModule_ProvideSharedPreferencesFactory.create(builder.storageModule));
        this.provideScheduleProvider = ScopedProvider.create(ScheduleModule_ProvideScheduleFactory.create(builder.scheduleModule));
        this.storageProvider = Storage_Factory.create(provideSharedPreferencesProvider, provideScheduleProvider);
        this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideSharedPreferencesProvider, Person_Factory.create());//实例化到这里结束
      }
    
      @Override
      public Storage execute() {  
        return storageProvider.get();
      }
    
      @Override
      public void inject(MainActivity mMainActivity) {  
        mainActivityMembersInjector.injectMembers(mMainActivity);
      }
    
      public static final class Builder {
        private StorageModule storageModule;
        private ScheduleModule scheduleModule;
    
        private Builder() {  
        }
    
        public StorageComponent build() {  
          if (storageModule == null) {
            throw new IllegalStateException("storageModule must be set");
          }
          if (scheduleModule == null) {
            this.scheduleModule = new ScheduleModule();
          }
          return new DaggerStorageComponent(this);
        }
    
        public Builder storageModule(StorageModule storageModule) {  
          if (storageModule == null) {
            throw new NullPointerException("storageModule");
          }
          this.storageModule = storageModule;
          return this;
        }
    
        public Builder scheduleModule(ScheduleModule scheduleModule) {  
          if (scheduleModule == null) {
            throw new NullPointerException("scheduleModule");
          }
          this.scheduleModule = scheduleModule;
          return this;
        }
      }
    }

    上面代码最后运行到MainActivity_MembersInjector.create(…)查看MainActivity_MembersInjector类:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
      private final MembersInjector<AppCompatActivity> supertypeInjector;
      private final Provider<SharedPreferences> mPreferencesProvider;
      private final Provider<Person> mPersonProvider;
    
      public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<SharedPreferences> mPreferencesProvider, Provider<Person> mPersonProvider) {  
        assert supertypeInjector != null;
        this.supertypeInjector = supertypeInjector;
        assert mPreferencesProvider != null;
        this.mPreferencesProvider = mPreferencesProvider;
        assert mPersonProvider != null;
        this.mPersonProvider = mPersonProvider;
      }
    
      @Override
      public void injectMembers(MainActivity instance) {  
        if (instance == null) {
          throw new NullPointerException("Cannot inject members into a null reference");
        }
        supertypeInjector.injectMembers(instance);
        instance.mPreferences = mPreferencesProvider.get();//赋值
        instance.mPerson = mPersonProvider.get();
      }
    
      public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<SharedPreferences> mPreferencesProvider, Provider<Person> mPersonProvider) {  
          return new MainActivity_MembersInjector(supertypeInjector, mPreferencesProvider, mPersonProvider);
      }
    }
    

    下来进入到了MainActivity 。在通过((MyApplication)this.getApplication()).getStorageComponent() 获取到component 后运行mStorageComponent.inject(this); 方法注入MainActivity,终于回调到上面代码中的injectMembers方法中。能够看出MainActivity中的成员变量所有初始完毕。之后就能够直接使用了。

    5.Lazy 类

    Lazy类是实现懒载入,调用的时候才创建实例,通过Lazy对象实现,得到对象的实例使用get()方法。比如:

    public class Storage {
    
        private SharedPreferences mPreferences;
        private Lazy<ScheduleImpl> mScheduleImpl;//Lazy 类
        private final String KEY = "Dagger 2";
    
        @Inject
        public Storage(SharedPreferences mPreferences ,Lazy<ScheduleImpl> mScheduleImpl) {
            this.mPreferences = mPreferences;
            this.mScheduleImpl = mScheduleImpl;
        }
    
        public void storage() {
            mScheduleImpl.get().start();//get()方法
            mPreferences.edit().putString(KEY, "Dagger 2 -- Example").commit();
            mScheduleImpl.get().end();
        }
    
    }
    

    6.Scope

    @Scope:注解作用域,通过自己定义注解限定对象的作用范围。它是JSR-330标准的一部分,事实上@Singleton就是一种@Scope

    在Dagger 2中,@Scope被用于标记自己定义的scope注解。简单说它们能够相似单例地标记依赖。

    被作注解的依赖会变成单例,可是这会与component的生命周期(不是整个应用)关联。

    首先创建一个LoginScope:

    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LoginScope {
    }
    

    Module:

    @Module
    public class LoginModule {
    
        @Provides
        @LoginScope  //<---这里为单例
        Person providePerson() {
            Person mPerson = new Person();
            mPerson.setAge(23);
            mPerson.setName("WeiLu");
            return mPerson;
        }
        @Provides
        Login provideLogin() {
            Login mLogin = new Login();
            mLogin.setPassword("######");
            mLogin.setName("小关");
            return mLogin;
        }
    }

    Component:

    @LoginScope
    @Component(modules = {
            LoginModule.class
    })
    public interface LoginComponent {
        void inject(MyApplication myApplication);
    }
    

    调用:

    mLoginComponent = DaggerLoginComponent.builder()
                    .loginModule(new LoginModule())
                    .build();
    mLoginComponent.inject(this);

    这里我们看一下生成代码:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class DaggerLoginComponent implements LoginComponent {
      private Provider<Person> providePersonProvider;
      private Provider<Login> provideLoginProvider;
      private MembersInjector<MyApplication> myApplicationMembersInjector;
    
      private DaggerLoginComponent(Builder builder) {  
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {  
        return new Builder();
      }
    
      public static LoginComponent create() {  
        return builder().build();
      }
    
      private void initialize(final Builder builder) {  
        this.providePersonProvider = ScopedProvider.create(LoginModule_ProvidePersonFactory.create(builder.loginModule));
        this.provideLoginProvider = LoginModule_ProvideLoginFactory.create(builder.loginModule);
        this.myApplicationMembersInjector = MyApplication_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), providePersonProvider, provideLoginProvider);
      }
    
      @Override
      public void inject(MyApplication myApplication) {  
        myApplicationMembersInjector.injectMembers(myApplication);
      }
    
      public static final class Builder {
        private LoginModule loginModule;
    
        private Builder() {  
        }
    
        public LoginComponent build() {  
          if (loginModule == null) {
            this.loginModule = new LoginModule();
          }
          return new DaggerLoginComponent(this);
        }
    
        public Builder loginModule(LoginModule loginModule) {  
          if (loginModule == null) {
            throw new NullPointerException("loginModule");
          }
          this.loginModule = loginModule;
          return this;
        }
      }
    }
    
    

    initialize方法:没有加@LoginScope 的Login类。那么他的创建是就是利用工厂模式new了一个Login。

    相反加了@LoginScope 的Person类,是利用ScopedProvider 储存了起来。源代码例如以下:

    public final class ScopedProvider<T> implements Provider<T> {
        private static final Object UNINITIALIZED = new Object();
        private final Factory<T> factory;
        private volatile Object instance;
    
        private ScopedProvider(Factory<T> factory) {
            this.instance = UNINITIALIZED;
    
            assert factory != null;
    
            this.factory = factory;
        }
    
        public T get() {
            Object result = this.instance;
            if(result == UNINITIALIZED) {
                synchronized(this) {
                    result = this.instance;
                    if(result == UNINITIALIZED) {
                        this.instance = result = this.factory.get();
                    }
                }
            }
    
            return result;
        }
    
        public static <T> Provider<T> create(Factory<T> factory) {
            if(factory == null) {
                throw new NullPointerException();
            } else {
                return new ScopedProvider(factory);
            }
        }
    }
    

    7.Qualifier

    @Qualifier:限定符,当类的类型不足以鉴别一个依赖的时候会使用到。假设我们没有去区分,会报错:xxx cannot be provided without an @Provides-annotated method。比如上面的Person类,我们如今准备返回两个:小明与小关。返回的都是Person类,怎么区分依赖?

    首先自己定义一个@Qualifier

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

    下来是Module:

    @Module
    public class UserModule {
    
        @Provides
        @User//加上这个自己定义注解用于区分
        Login provideXiaoMingUser() {
            Login xiaomin = new Login();
            xiaomin.setPassword("******");
            xiaomin.setName("小明");
            return xiaomin;
        }
        @Provides
        Login provideXiaoGuanUser() {
            Login xiaoguan = new Login();
            xiaoguan.setPassword("######");
            xiaoguan.setName("小关");
            return xiaoguan;
        }
    }

    Component:

    @Subcomponent(modules = {
            UserModule.class
    })
    public interface UserComponent {
        void inject(SecondActivity mSecondActivity);
    }

    这里用到了@Subcomponent,我们想复用组件时。能够使用它。以下是父组件用法。还有一种是注解属性加入dependencies。

    @Singleton
    @Component(
            modules ={ AppModule.class
        }
    )
    public interface AppComponent {
    
        Context getAppContext();
        UserComponent createUserComponent(UserModule userModule);
    }

    这样的复用组件事实上是在在父组件中创建了子组件的内部类。例如以下:

    @Generated("dagger.internal.codegen.ComponentProcessor")
    public final class DaggerAppComponent implements AppComponent {
      private Provider<Context> provideContextProvider;
    
      private DaggerAppComponent(Builder builder) {  
        assert builder != null;
        initialize(builder);
      }
    
      public static Builder builder() {  
        return new Builder();
      }
    
      private void initialize(final Builder builder) {  
        this.provideContextProvider = ScopedProvider.create(AppModule_ProvideContextFactory.create(builder.appModule));
      }
    
      @Override
      public Context getAppContext() {  
        return provideContextProvider.get();
      }
    
      @Override
      public UserComponent createUserComponent(UserModule userModule) {  
        return new UserComponentImpl(userModule);
      }
    
      public static final class Builder {
        private AppModule appModule;
    
        private Builder() {  
        }
    
        public AppComponent build() {  
          if (appModule == null) {
            throw new IllegalStateException("appModule must be set");
          }
          return new DaggerAppComponent(this);
        }
    
        public Builder appModule(AppModule appModule) {  
          if (appModule == null) {
            throw new NullPointerException("appModule");
          }
          this.appModule = appModule;
          return this;
        }
      }
    
      private final class UserComponentImpl implements UserComponent {//内部类
        private final UserModule userModule;
        private Provider<Login> provideXiaoMingUserProvider;
        private Provider<Login> provideXiaoGuanUserProvider;
        private MembersInjector<SecondActivity> secondActivityMembersInjector;
    
        private UserComponentImpl(UserModule userModule) {  
          if (userModule == null) {
            throw new NullPointerException();
          }
          this.userModule = userModule;
          initialize();
        }
    
        private void initialize() {  
          this.provideXiaoMingUserProvider = UserModule_ProvideXiaoMingUserFactory.create(userModule);
          this.provideXiaoGuanUserProvider = UserModule_ProvideXiaoGuanUserFactory.create(userModule);
          this.secondActivityMembersInjector = SecondActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideXiaoMingUserProvider, provideXiaoGuanUserProvider);
        }
    
        @Override
        public void inject(SecondActivity mSecondActivity) {  
          secondActivityMembersInjector.injectMembers(mSecondActivity);
        }
      }
    }

    初始化:(MyApplication中)

    mAppComponent = DaggerAppComponent.builder()
                    .appModule(new AppModule(this))
                    .build();
            mUserComponent = mAppComponent.createUserComponent(new UserModule());
    

    调用:

    public class SecondActivity extends AppCompatActivity {
    
        UserComponent userComponent;
    
        @Inject
        @User
        Login xiaoming;
    
        @Inject
        Login xiaoguan;
    
        @Bind(R.id.button4)
        Button mButton4;
    
        @Bind(R.id.button5)
        Button mButton5;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            ButterKnife.bind(this);
            userComponent = ((MyApplication)this.getApplication()).getUserComponent();
            userComponent.inject(this);
        }
        @OnClick({
                R.id.button4,
                R.id.button5,
        })
        void onButtonClicked(View v) {
            switch (v.getId()) {
                case R.id.button4:
                    Toast.makeText(this,
                    xiaoming.getName() + "----" + xiaoming.getPassword(),Toast.LENGTH_SHORT).show();
                    break;
                case R.id.button5:
                    Toast.makeText(this,
                    xiaoguan.getName() + "----" + xiaoguan.getPassword(),Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
    

    详细生成的代码。大家下载Demo后自行查看。

    通过自己主动生成的代码能够看出Dagger 2主要用到了Builder模式、Factory模式。代码不难理解。同一时候由于Dagger 2没有使用反射,尽管效率提高了,可是缺乏灵活性。

    这也是为了提高效率的代价。

    3.Dagger2 练习Demo

    Demo下载链接:Dagger2Example

    这里写图片描写叙述

    4.參考

    1. Dagger 2 API文档

    2. Android Dagger2学习

    3. 使用Dagger 2依赖注入 - API

    4. Dependency injection with Dagger 2 - Custom scopes


    PS:最后说一下,关于Dagger2的学习成本还是挺高的。我自己也是从零開始接触。利用业余时间前前后后用了近一周时间去学习,一開始看的也是云里雾里。

    事实上对比着自己主动生成的代码多看看就比較好理解了。有什么错误地方。希望多多交流。就这样。

    。。

  • 相关阅读:
    OC与JS交互之WKWebView
    iOS下JS与OC互相调用(三)--MessageHandler
    html base64 img 图片显示
    Vue中img的src属性绑定与static文件夹
    XML 树结构
    XML 用途
    XML 简介
    JS Window对象
    JS Math对象
    JS 字符串操作
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/7096069.html
Copyright © 2020-2023  润新知