這是為Dagger2
的詳細(xì)分析第一篇,以做記錄答恶。
此篇介紹以下內(nèi)容:
- @Inject
- @Component
- @Module
- @Scope
- @SubComponent
- denpendency
- @Lazy
- @Binds
- @IntoSet
- @IntoMap
此篇不會(huì)從頭開(kāi)始介紹dagger2,假設(shè)讀者已經(jīng)對(duì)dagger2有一定的了解哈
基本用法--沒(méi)有Module
- 構(gòu)造函數(shù)上標(biāo)注@Inject
public class A {
@Inject
public A() {}
}
- 定義Component作為連接器
@Component
public interface ActivityComponent {
void inject(MainActivity MainActivity);
}
- 要進(jìn)行注入的Activity
public class MainActivity extends Activity implements LifecycleOwner
{
@Inject
A a;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
DaggerActivityComponent.builder().build().inject(this);
}
}
我們?cè)敿?xì)看生成了什么:
對(duì)于ActivityComponent
,dagger
會(huì)幫你生成DaggerxxxComponent
對(duì)于MainActivity
,dagger
會(huì)幫你生成xxxMemberInjector
public final class DaggerActivityComponent implements ActivityComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static ActivityComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(A_Factory.create());
}
@Override
public void inject(MainActivity MainActivity) {
mainActivityMembersInjector.injectMembers(MainActivity);
}
public static final class Builder {
private Builder() {}
public ActivityComponent build() {
return new DaggerActivityComponent(this);
}
}
}
兩行關(guān)鍵代碼:
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(A_Factory.create());
mainActivityMembersInjector.injectMembers(MainActivity);
Factory
是真正提供數(shù)據(jù)的工廠,Injector
包含Factory
,所以Injector
可以向變量注入實(shí)例。
@Module
- 不在構(gòu)造函數(shù)上標(biāo)注@Inject,而是新生成一個(gè)Module
@Module
public class ActivityModule {
@Provides
public A provideA(){
return new A();
}
}
這是什么意思呢,就是需要實(shí)例時(shí)不會(huì)再?gòu)臉?gòu)造函數(shù)中去獲取,而是通過(guò)providexxx
獲取。
- 定義Component作為連接器,注意注解參數(shù)與前面的區(qū)別
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity MainActivity);
}
在MainActivity
中:
// 構(gòu)造橋梁對(duì)象
DaggerActivityComponent.builder()
.activityModule(new ActivityModule())
.build()
.inject(this);
注意這里.activityModule(new xxx)
不寫(xiě)也沒(méi)事,dagger
會(huì)自動(dòng)幫我們構(gòu)建,當(dāng)然如果ActivityModule
的構(gòu)造函數(shù)需要傳遞參數(shù)就需要我們手動(dòng)寫(xiě)了验毡。
注入過(guò)程
- 查找Module中是否存在創(chuàng)建該類的方法坦喘。
- 若存在創(chuàng)建類方法,查看該方法是否存在參數(shù)
- 若存在參數(shù)采幌,則按從步驟1開(kāi)始依次初始化每個(gè)參數(shù)
- 若不存在參數(shù)贴届,則直接初始化該類實(shí)例,一次依賴注入到此結(jié)束
- 若不存在創(chuàng)建類方法蜡吧,則查找Inject注解的構(gòu)造函數(shù)毫蚓,看構(gòu)造函數(shù)是否存在參數(shù)
- 若存在參數(shù),則從步驟1開(kāi)始依次初始化每個(gè)參數(shù)
- 若不存在參數(shù)昔善,則直接初始化該類實(shí)例元潘,一次依賴注入到此結(jié)束
@Scope
一個(gè)很具有疑惑性的注解。
- 創(chuàng)建注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
- 在Module或構(gòu)造函數(shù)上標(biāo)注
@Module
public class UserModule {
...
@Provides
@UserScope
User providesUser() {
return new A();
}
}
or
@UserScope
public class User {
@Inject
public User() {
}
}
- 在使用該作用于的Component上標(biāo)注
@UserScope
@Component(modules = {UserModule.class})
public interface UserComponent {
...
}
先說(shuō)一下這個(gè)注解到底是什么意思君仆。經(jīng)臭娓牛看到什么@Singleton|@PerActivity|PerFragment
。那些背后都是靠一個(gè)@Scope
完成的返咱。那這個(gè)作用域是由誰(shuí)控制的, 其實(shí)就是靠Component
控制的钥庇。
即:Component活多久,這個(gè)注解生成的實(shí)例就活多久咖摹。
我們以常見(jiàn)的PerActivity
為例,為什么它能做到跟Activity一個(gè)生命周期,就是因?yàn)槟忝啃律梢粋€(gè)Activity
,你的DaggerxxxComponent
都會(huì)由你重新.build
生成评姨。
假設(shè)我們?cè)?code>Application中生成一個(gè)component
,并且在每個(gè)Activity
里通過(guò)getApplication().getComponent()
去獲取Component
,那么不好意思,你的實(shí)例就和Application
同生死了萤晴。
所以這個(gè)是怎么做到的,很簡(jiǎn)單,背后利用了DoubleCheck
DoubleCheck:
在Provider
外面包一層DoubleCheck
,每次get
時(shí)看當(dāng)前Component
下有沒(méi)有已經(jīng)生成的實(shí)例,有的話就直接返回吐句。
@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();
/* Get the current instance and test to see if the call to provider.get() has resulted
* in a recursive call. If it returns the same instance, we'll allow it, but if the
* instances differ, throw. */
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result);
}
instance = result;
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
很簡(jiǎn)單就實(shí)現(xiàn)了作用域的控制。我的理解就是這么簡(jiǎn)單店读。
dependency
@Component(modules = ActivityModule.class)
public interface AComponent {
A provideA();
}
@Component(dependencies = AComponent.class)
public interface BComponent {
void inject(MainActivity MainActivity);
}
DaggerBComponent.builder().activityComponent(DaggerAComponent.builder().activityModule(new ActivityModule()).build()).build().inject(this);
先看上面代碼,有兩個(gè)Component
嗦枢。B
是我們常見(jiàn)的寫(xiě)法,但是它@Component(dependencies = ActivityComponent.class)
依賴于A。
A
只暴露了一個(gè)接口A provideA();
所以最終實(shí)現(xiàn)什么效果呢,B
可以使用A
提供的provideA
去注入A的實(shí)例屯断。
父Component中要顯式的寫(xiě)出需要暴露可提供給子Component的依賴文虏;
子Component在注解中使用dependencies=來(lái)連接父Component侣诺;
@SubComponent
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
AnotherComponent anotherComponent();
}
@Subcomponent
public interface AnotherComponent {
void inject(MainActivity MainActivity);
}
DaggerActivityComponent.builder().build().anotherComponent(DaggerAnotherComponent.builder().build()).inject(this);
我們會(huì)看到,它跟denpendency
不同,后者會(huì)生成兩個(gè)DaggerxxxComponent
,但它只會(huì)生成一個(gè)。
public final class DaggerActivityComponent implements ActivityComponent {
...
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideAProvider = ActivityModule_ProvideAFactory.create(builder.activityModule);
}
@Override
public AnotherComponent anotherComponent() {
return new AnotherComponentImpl();
}
public static final class Builder {
private ActivityModule activityModule;
private Builder() {}
public ActivityComponent build() {
if (activityModule == null) {
this.activityModule = new ActivityModule();
}
return new DaggerActivityComponent(this);
}
public Builder activityModule(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
return this;
}
}
private final class AnotherComponentImpl implements AnotherComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private AnotherComponentImpl() {
initialize();
}
@SuppressWarnings("unchecked")
private void initialize() {
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(DaggerActivityComponent.this.provideAProvider);
}
@Override
public void inject(MainActivity MainActivity) {
mainActivityMembersInjector.injectMembers(MainActivity);
}
}
}
先說(shuō)一個(gè)事實(shí)择葡,一個(gè)Component
要不只能是@Component,要不只能是@SubComponent
然后看上面代碼,最終是由標(biāo)注了@SubComponent的AnotherComponent
去進(jìn)行了真正的注入紧武。這里@SubComponent可以繼承它的父類提供的所有實(shí)例。我試了一下,如果只有AnotherComponent
存在是無(wú)法完成注入的敏储,也就是說(shuō)他是不獨(dú)立的阻星,它必須依附于父類。
所以dependency
與subComponent
的區(qū)別:
Component Dependencies - Use this when:
you want to keep two components independent.
you want to explicitly show what dependencies from one component is used by the other
Subcomponents - Use this when:
you want to keep two component cohesive
you may not care to explicitly show what dependencies from one component is used by the other
前者更獨(dú)立,組件只是提供provide
給另一個(gè)借用一下已添。
同時(shí)注意:
兩個(gè)擁有依賴關(guān)系的 Component 是不能有相同 @Scope 注解的妥箕!使用@SubComponent 則可以使用相同的@Scope注解。 (很重要8琛F璐薄!@虏酢)
@Lazy & @Provider
在Activity
中宇葱,我們可以這么聲明變量。
@Inject
Lazy<A> a;
Provider<A> b;
只有真正去aLazy.get()
才會(huì)進(jìn)行注入刊头。
實(shí)現(xiàn)也很簡(jiǎn)單,繼續(xù)DoubleCheck
包裹Provider
,然后真正get
的時(shí)候才會(huì)從Factory
中獲取黍瞧。不過(guò)他跟前面的scope
不大一樣,是在對(duì)instance
實(shí)例賦值的時(shí)候進(jìn)行包裹的。
其中Lazy(懶加載)的作用好比component初始化了一個(gè)present對(duì)象,然后放到一個(gè)池子里,需要的時(shí)候就get它,所以你每次get的時(shí)候拿到的對(duì)象都是同一個(gè);并且當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例.
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.a = DoubleCheck.lazy(bAndAProvider);
instance.b = bAndAProvider;
}
而對(duì)于b直接賦予provider
的值,后面要get
時(shí)就從provider.get
獲取原杂。
procider(強(qiáng)制加載)的作用:
1:同上當(dāng)你第一次去get時(shí),它才會(huì)去初始化這個(gè)實(shí)例
2:后面當(dāng)你去get這個(gè)實(shí)例時(shí),依舊是從
Provider
中獲取,所以跟基本用法是一樣的印颤。
所以Provider與Lazy的區(qū)別在于,Lazy延遲加載之后每次的調(diào)用都是同一個(gè)對(duì)象,而Provider只是延遲加載,至于調(diào)用的對(duì)象是不是同一個(gè)就要看@scope
有沒(méi)有進(jìn)行約束了。
@Binds
現(xiàn)在我們假設(shè)有個(gè)接口叫IinterfaceA
UserA
和UserB
分別實(shí)現(xiàn)了這個(gè)接口穿肄。
然后我們?cè)?code>Activity里這么寫(xiě):
@Inject
IinterfaceA a;
我們希望實(shí)現(xiàn)IinterfaceA a = new UserA()
我們不能直接在UserA
的構(gòu)造函數(shù)上標(biāo)注@Inject
因?yàn)槲覀円⑷氲氖墙涌诓皇菍?shí)現(xiàn)年局。所以按已有的知識(shí)體系那就用Module
咯:
@Provides
public IinterfaceA providesA(){
return new UserA();
}
而如果用@Binds
這個(gè)新的特性,我們可以這么寫(xiě)。注意abstract
@Module
public abstract class AModule {
@Binds
public abstract IinterfaceA bindA(UserA userA);
}
這個(gè)意思是如果你想要注入到IinterfaceA
,請(qǐng)找UserA
咸产。
其實(shí)這兩貨看起來(lái)差不多,那為什么要出現(xiàn)@Binds
矢否。后來(lái)看有個(gè)解釋挺不錯(cuò):
@Provides methods are instance methods and they need an instance of our module in order to be invoked. If our Module is abstract and contains @Binds methods, dagger will not instantiate our module and instead directly use the Provider of our injected parameter (LoginPresenter in the above case).
對(duì)于Provides
而言,Module
是會(huì)被實(shí)例化的,因?yàn)橐?chuàng)建UserA
。但是對(duì)于@Binds
而言,不需要實(shí)例化Module
就可以做到脑溢。
所以@Binds解決了我們面向接口編程的需求兴喂。
我的理解,@Binds
是完全可以被替代的,就看你自己的需求了。
@IntoSet & @ElementsIntoSet
如果我們?cè)?code>Activity里:
@Inject
Set<String> a
這個(gè)可以通過(guò)@IntoSet & @ElementsIntoSet
實(shí)現(xiàn)實(shí)例一一注入到集合中,看個(gè)例子
@Provides @IntoSet
String providexxx() {
return "ABC";
}
@Provides @IntoSet
String providekkk() {
return "Acc";
}
@Provides @ElementsIntoSet
Set<String> provideSomeStrings() {
return new HashSet<String>(Arrays.asList("DEF", "GHI"));
}
這樣a就包含<"ABC","Acc","DEF","GHI">
其實(shí)到這里基本的套路都差不多,直接上編譯出來(lái)的源碼:
//(int individualProviderSize, int collectionProviderSize)
this.setOfStringProvider =
SetFactory.<String>builder(2, 1)
.addProvider(providexxxProvider)
.addProvider(providekkkProvider)
.addCollectionProvider(provideSomeStringsProvider)
.build();
@Override
public Set<T> get() {
int size = individualProviders.size();
// Profiling revealed that this method was a CPU-consuming hotspot in some applications, so
// these loops were changed to use c-style for. Versus enhanced for-each loops, C-style for is
// faster for ArrayLists, at least through Java 8.
List<Collection<T>> providedCollections =
new ArrayList<Collection<T>>(collectionProviders.size());
for (int i = 0, c = collectionProviders.size(); i < c; i++) {
Collection<T> providedCollection = collectionProviders.get(i).get();
size += providedCollection.size();
providedCollections.add(providedCollection);
}
Set<T> providedValues = newHashSetWithExpectedSize(size);
for (int i = 0, c = individualProviders.size(); i < c; i++) {
providedValues.add(checkNotNull(individualProviders.get(i).get()));
}
for (int i = 0, c = providedCollections.size(); i < c; i++) {
for (T element : providedCollections.get(i)) {
providedValues.add(checkNotNull(element));
}
}
return unmodifiableSet(providedValues);
}
我們看到,individualProviderSize
對(duì)應(yīng)@IntoSet
的個(gè)數(shù),collectionProviderSize
對(duì)應(yīng)@ElementsIntoSet
的個(gè)數(shù)焚志。
當(dāng)get
時(shí)會(huì)通過(guò)individualProvider
和collectionProvider
提供的數(shù)據(jù)注入到instance
中形成集合衣迷。
@IntoMap
跟IntoSet
一個(gè)性質(zhì),只不過(guò)多了Key
的約束。上例子:
@Provides
@IntoMap // 指定該@Provides方法向Map提供元素
@StringKey("A") // 指定該元素在Map中所對(duì)應(yīng)的的Key
String providexxx() {
return "ABC";
}
@Provides
@IntoMap // 指定該@Provides方法向Map提供元素
@ClassKey(MainActivity.class)
String providekkk() {
return "Acc";
}
StringKey
代表以字符串作為key,ClassKey
代表以類作為key酱酬。
目前dagger2
預(yù)定義的有:
- ClassKey
- IntKey
- LongKey
- StringKey
編譯后的代碼不貼了,跟set
原理差不多
自定義key類型
enum MyEnum {
ABC, DEF;
}
@MapKey
@interface MyEnumKey {
MyEnum value();
}
@MapKey
@interface MyNumberClassKey {
Class<? extends Number> value();
}
@Module
class MyModule {
@Provides @IntoMap
@MyEnumKey(MyEnum.ABC)
static String provideABCValue() {
return "value for ABC";
}
@Provides @IntoMap
@MyNumberClassKey(BigDecimal.class)
static String provideBigDecimalValue() {
return "value for BigDecimal";
}
}
這里定義了自定義兩種key:ABC,DEF
Map
的高級(jí)用法請(qǐng)見(jiàn)下節(jié)壶谒。