本文主要從Dagger2生成的源碼角度講解Scope注解的作用
代碼已上傳至GitHub,戳https://github.com/tb-yangshu/android-dagger2-example,歡迎star或者fork
Scope
Identifies scope annotations. A scope annotation applies to a class containing an injectable constructor and governs how the injector reuses instances of the type. By default, if no scope annotation is present, the injector creates an instance (by injecting the type's constructor), uses the instance for one injection, and then forgets it. If a scope annotation is present, the injector may retain the instance for possible reuse in a later injection. If multiple threads can access a scoped instance, its implementation should be thread safe. The implementation of the scope itself is left up to the injector.
以上解釋來(lái)源于http://docs.oracle.com/javaee/7/api/javax/inject/Scope.html
意思是,Scope注解被標(biāo)注在可被注入類(lèi)的構(gòu)造器上,注入器根據(jù)Scope類(lèi)型來(lái)決定如何對(duì)實(shí)例進(jìn)行復(fù)用。默認(rèn)情況下,被注入類(lèi)上沒(méi)有Scope注解,此時(shí)注入器每次注入都會(huì)生成新的實(shí)例并注入。如果標(biāo)注了Scope注解,注入器創(chuàng)建成功實(shí)例后會(huì)將其緩存,在下次注入時(shí)直接返回緩存結(jié)果。多線程模式下可以訪問(wèn)同一個(gè)被Scope標(biāo)注的實(shí)例,因?yàn)樵搶?shí)例是線程安全的。注入器將會(huì)生成Scope的具體實(shí)現(xiàn)類(lèi)。
Singleton
Singleton是由javax.inject包提供的,用來(lái)控制被注入實(shí)例被創(chuàng)建后進(jìn)行緩存復(fù)用。
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
Custom Scope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}
PerActivity用來(lái)標(biāo)注被注入類(lèi)和與之對(duì)應(yīng)的Component,來(lái)控制被注入實(shí)例在Activity生命周期中可以被復(fù)用。
源碼分析
接下來(lái),從Dagger2生成的代碼來(lái)分析Scope注解的作用。源碼地址
以下是使用Dagger2為MainActivity注入ActivityData和ActivityDataWithScope實(shí)例的代碼。
ActivityComponent.java
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
ActivityData.java
public class ActivityData {
@Inject
public ActivityData() {
}
}
ActivityDataWithScope.java
@PerActivity
public class ActivityDataWithScope {
@Inject
public ActivityDataWithScope() {
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
...
@Inject
ActivityData mActivityData;
@Inject
ActivityDataWithScope mActivityDataWithScope;
private ActivityComponent activityComponent;
...
public ActivityComponent getActivityComponent() {
if (activityComponent == null) {
activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(DemoApplication.get(this).getComponent())
.build();
}
return activityComponent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 注入實(shí)例
getActivityComponent().inject(this);
...
}
...
}
從上面的代碼可以看出,使用Dagger2寥寥數(shù)行代碼就實(shí)現(xiàn)了實(shí)例的注入,極大的節(jié)省了開(kāi)發(fā)時(shí)間,省去了手動(dòng)new的操作,代碼也看起來(lái)更加優(yōu)雅,美觀。
下面從Dagger2生成的ActivityComponent實(shí)現(xiàn)類(lèi)DaggerActivityComponent來(lái)分析依賴注入的實(shí)現(xiàn)流程。
DaggerActivityComponent.java
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class DaggerActivityComponent implements ActivityComponent {
...
private Provider<ActivityData> activityDataProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
...
this.activityDataWithScopeProvider =
DoubleCheck.provider(ActivityDataWithScope_Factory.create());
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(
getDataManagerProvider, ActivityData_Factory.create(), activityDataWithScopeProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private ApplicationComponent applicationComponent;
private Builder() {}
public ActivityComponent build() {
if (applicationComponent == null) {
throw new IllegalStateException(
ApplicationComponent.class.getCanonicalName() + " must be set");
}
return new DaggerActivityComponent(this);
}
...
}
}
該類(lèi)使用了Builder模式來(lái)創(chuàng)建ActivityComponent實(shí)例。這里需要留意下initialize方法,該方法創(chuàng)建了生產(chǎn)被注入實(shí)例的Provider和注入器MembersInjector的實(shí)例。
不難發(fā)現(xiàn)activityDataWithScopeProvider的創(chuàng)建過(guò)程與ActivityData的Provider稍有不同。這是由于ActvityDataWithScope被標(biāo)注了PerActivity注解。
接下來(lái)比較下Dagger2生成ActivityData實(shí)例與ActivityDataWithScope實(shí)例的不同之處,這也是Scope注解的作用。
先來(lái)看一下ActivityData實(shí)例的創(chuàng)建方式,以下是ActivityData_Factory的代碼
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class ActivityData_Factory implements Factory<ActivityData> {
private static final ActivityData_Factory INSTANCE = new ActivityData_Factory();
@Override
public ActivityData get() {
return new ActivityData();
}
public static Factory<ActivityData> create() {
return INSTANCE;
}
}
可以看出每次調(diào)用get方法都會(huì)重新返回一個(gè)新的ActivityData實(shí)例。
接下來(lái)分析ActivityDataWithScope實(shí)例的創(chuàng)建方式。
DoubleCheck.provider(ActivityDataWithScope_Factory.create())顯然對(duì)Provider進(jìn)行了再次封裝。下面是DoubleCheck的代碼。
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance;
private DoubleCheck(Provider<T> provider) {
this.instance = UNINITIALIZED;
assert provider != null;
this.provider = provider;
}
public T get() {
Object result = this.instance;
if(result == UNINITIALIZED) {
synchronized(this) {
result = this.instance;
if(result == UNINITIALIZED) {
result = this.provider.get();
Object currentInstance = this.instance;
if(currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning different results: " + currentInstance + " & " + result);
}
this.instance = result;
this.provider = null;
}
}
}
return result;
}
public static <T> Provider<T> provider(Provider<T> delegate) {
Preconditions.checkNotNull(delegate);
return (Provider)(delegate instanceof DoubleCheck?delegate:new DoubleCheck(delegate));
}
public static <T> Lazy<T> lazy(Provider<T> provider) {
if(provider instanceof Lazy) {
Lazy lazy = (Lazy)provider;
return lazy;
} else {
return new DoubleCheck((Provider)Preconditions.checkNotNull(provider));
}
}
}
分析上面的get方法,不難看出是對(duì)Provider.get()返回的實(shí)例進(jìn)行了緩存來(lái)實(shí)現(xiàn)單例。
總結(jié)
從上面的分析可以看出,Scope注解的作用就是在特定作用域內(nèi)控制被注入實(shí)例的復(fù)用。
比如,從登錄到退出,用戶基本信息(如uid)是不會(huì)發(fā)生變化的,用來(lái)存儲(chǔ)用戶數(shù)據(jù)的實(shí)例就可以通過(guò)Scope標(biāo)識(shí)來(lái)復(fù)用。
參考資料