Dagger2使用攻略
来自: 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.参考
4. Dependency injection with Dagger 2 - Custom scopes
PS:最后说一下,关于Dagger2的学习成本还是挺高的。我自己也是从零开始接触,利用业余时间前前后后用了近一周时间去学习,一开始看的也是云里雾里。其实对照着自动生成的代码多看看就比较好理解了。有什么错误地方,希望多多交流。就这样。。。