之前寫了幾篇關于dagger2的基礎介紹,使用方式以及源碼分析,最后準備寫一篇關于dagger2的使用擴展.
dagger2提供了很多修飾符
@scope(作用域)
在這里記錄我的源碼分析結果驗證結果,按照上一篇案例我在
@Singleton
public class DemoPresenter
Presenter類上加上Singleton修飾,然后我們跑一下代碼可以看到Inject注入的二個對象的地址一模一樣!
現在去掉這個修飾符再跑下看看結果
可以看到Singleton是完全有創建單例能力的啊,并且我的Component是在MainActicity中初始化的,現在再創建一個SecondActivity生成對象試試,
public class SecondActivity extends AppCompatActivity implements BaseView {
@Inject
DemoPresenter mDemoPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
DaggerCompnoent.builder().module(new Module(this)).build().inject(this);
Log.e("wwwSecondDemoPresenter2",mDemoPresenter.toString());
}
@Override
public void setXxx(Person p) {
}
}
簡單寫下代碼
現在MainActivity中二個注入的對象地址一模一樣的我們跳轉到SecondActivity看下生成對象地址
咦?我特么以為地址會一模一樣怎么跳一下界面地址就變了,帶著大大的疑問我決定去看看源碼?所謂知己知彼百戰不殆嘛
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.getBaseViewProvider = Module_GetBaseViewFactory.create(builder.module);
this.demoPresenterProvider =
DoubleCheck.provider(DemoPresenter_Factory.create(getBaseViewProvider));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(demoPresenterProvider);
this.secondActivityMembersInjector =
SecondActivity_MembersInjector.create(demoPresenterProvider);
}
這里之前看過源碼分析的已經很清楚了就不多說了,
MainActivity_MembersInjector
SecondActivity_MembersInjector
都是一個工廠類里面創建對象,然后inject會調用對象中的injectMembers然后把demoPresenterProvider.get生成的presenter對象注入進去
DoubleCheck.provider(DemoPresenter_Factory.create(getBaseViewProvider));
之前分析的時候好像沒有DoubleCheck這個玩意,貌似加了個Singleton就多出個這DoubleCheck.provider( );
點進去看看是什么
/** 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) {
/* This should be a rare case, but if we have a scoped @Bind that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
return new DoubleCheck<T>(delegate);
}
里面接收DemoPresenter_Factory對象,把DemoPresenter_Factory傳到DoubleCheck構造參數中,然后返回這個對象,我們看看DoubleCheck的get方法做了什么
@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) {
instance = result = provider.get();
/* 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;
}
其實DoubleCheck就是個代理類,它進行一系列判斷后調用DemoPresenter_Factory的get方法生成p對象,
我們前面MainActivity生成的對象一模一樣就是因為在MainActivity中
Compnoent只生成一次,而注入對象時調用的就是我們上面分析的代理類DoubleCheck的get()方法,通過分析代碼可以知道只要Compnoent對象不變,那么它永遠只調用一次get()方法生成一次對象.這里可以解釋之前MainActivity生成單例的錯覺了,因為我們Compnoent只生成過一次!!!
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mDemoPresenter1 = mDemoPresenter1AndMDemoPresenter2Provider.get();
instance.mDemoPresenter2 = mDemoPresenter1AndMDemoPresenter2Provider.get();
}
至于為什么SecondActivity生成的又不一樣了,SecondActivity跟MainActivity完全是二個不同對象,生成的Compnoent成員變量自然在二個對象的堆地址中,所以生成的p對象當然不相同了~
這也是Singleton最坑的地方,讓我們誤以為加上這個注釋就具有單例的能力
我在MainActivity中再生成一次compnoent,前面分析如果
compnoent不變那么compnoent生成的對象就會一直不變,如果它的地址改變了,那么生成的對象地址也會改變。我們看看是不是這樣
可以看到第一次注入的二個對象都是第一個compnoent生成的對象是相同的跟我們前面分析的也一致,下一步我們看看第二個compnoent生成的對象
由結果可以看到確實跟我們分析的一樣compnoent變了生成的對象地址同樣變了!
所以我們加上Singleton注解生成的那段代碼我的理解是在compnoent不變的情況下保證compnoent生成的對象永遠一樣!所以compnoent單例還是需要我們自己做的, Singleton同樣需要添加的!
@Qualifier (限定符)
這個注解作用與當我們@provide提供了多個返回相同對象的方法時,dagger懵比了,它并不知道選擇哪一個進行返回,這個時候我們用@inject注解就會編譯錯誤
那怎么辦呢???這個時候Qualifier就派上用場了
@Provides
@Named("name1")
public DemoPresenter provideDemoPresenter1(){
return new DemoPresenter(mBaseView);
}
@Provides
@Named("name2")
public DemoPresenter provideDemoPresenter2(){
return new DemoPresenter(mBaseView);
}
比如這段代碼中,我提供了二個都返回DemoPresenter對象的方法,我們這個時候需要使用限定符進行標識,@Named是Qualifier 默認實現的一個注解,這里把方法一表示為name1,方法二標識為name2,我們在需要依賴的地方使用
@Inject
@Named("name1")
DemoPresenter mDemoPresenter2;
這樣就依賴上方法一返回的對象了~當然你也可以自己自定義一個Qualifier
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
我們看Named怎么定義的,自定義只需要改變Named命名就可以了
接下來看看Lazy用法,
@Inject
Lazy<DemoPresenter> mDemoPresenterLazy;
使用Lazy這個接口注入我們的對象,泛型中存放我們的presenter對象類型,然后運行代碼,dagger會幫我們module中生成的p對象存放到我們上面提到過的DoubleCheck類中我們翻下源碼
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mDemoPresenterLazy = DoubleCheck.lazy(mDemoPresenterLazyProvider);
}
繼續看看DoubleCheck中lazy方法
/** Returns a {@link Lazy} that caches the value from the given provider. */
public static <T> Lazy<T> lazy(Provider<T> provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck<T> exclusively, which is implemented such that P and L are always
// the same, so it will be fine for that case.
return lazy;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
可以看到返回了一個DoubleCheck對象,
public final class DoubleCheck<T> implements Provider<T>, Lazy<T>
它實現了Lazy接口,這個時候我們如果調用get方法就會返回p對象,這個過程在上面已經分析了源碼過程。
最后再看一個Provider用法
@Inject
Provider<DemoPresenter> mDemoPresenterProvider;
這玩意也非常簡單,就是把生成p的工廠類對象直接給我們,我們調用get就會調用工廠類的get方法生成p對象
@Override
public DemoPresenter get() {
return Preconditions.checkNotNull(
module.provideDemoPresenter1(), "Cannot return null from a non-@Nullable @Provides method");
}
好了dagger的擴展使用全部介紹完畢,整個系列算是告一段落~~~