Dagger2使用攻略

brzq3054 8年前

来自: http://blog.csdn.net/qq_17766199/article/details/50606011


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的学习成本还是挺高的。我自己也是从零开始接触,利用业余时间前前后后用了近一周时间去学习,一开始看的也是云里雾里。其实对照着自动生成的代码多看看就比较好理解了。有什么错误地方,希望多多交流。就这样。。。