前言
本系列所有文章:
Android 神兵利器Dagger2使用詳解(一)基礎(chǔ)使用
Android 神兵利器Dagger2使用詳解(二)Module&Component源碼分析
Android 神兵利器Dagger2使用詳解(三)MVP架構(gòu)下的使用
Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析
告別Dagger2模板代碼:DaggerAndroid使用詳解
告別Dagger2模板代碼:DaggerAndroid原理解析
該系列首發(fā)于我的CSDN專欄 :
Android開發(fā):Dagger2詳解
在我的上一篇文章中痕貌,我們以簡單的案例對Dagger2依賴注入庫在實際開發(fā)中的使用方法進行了學(xué)習(xí)耘拇。
本文內(nèi)容:
1.@Singleton 全局單例 注解的使用
2.自定義@Scope 局部單例 注解的使用
3.通過 **源碼分析 **@Singleton和@Scope注解是 如何實現(xiàn)單例 的借跪。
一.@Singleton:全局單例注解
承接上文,我們在AppModule中提供了以下對象的初始化:
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
//提供sp對象
@Provides
SharedPreferences provideSharedPreferences(){
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}
//提供Application對象
@Provides
MyApplication provideApplication(){
return application;
}
}
我們肯定會有這樣一種需求吐绵,類似于SharedPreferences對象眠副,我們可能在整個App的生命周期中只需要一個單例腾啥,而不需要每次都通過application.getSharedPreferences("spfile", Context.MODE_PRIVATE)獲得新的對象芋类,那么怎么辦呢,我們只需要在你需要單例的對象提供方法上加一個注解@Singleton ,類似這樣:
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
//全局單例SharedPreferences
@Provides
@Singleton
SharedPreferences provideSharedPreferences() {
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}
@Provides
MyApplication provideApplication() {
return application;
}
}
只需要一個@Singleton,無論我們在多少個Activity容器中通過@Inject獲取這個SharedPreferences對象的實例错忱,該對象都是同一個對象儡率,從而實現(xiàn)了全局單例的效果。
非常簡單的使用方式以清,你需要什么對象全局單例儿普,就在提供該對象方法的@Provides注解旁加上一個@Singleton,并且在該module關(guān)聯(lián)的Component中加上同樣的注解:
@Singleton //不要忘了還要在關(guān)聯(lián)的Component接口中聲明,否則會編譯報錯
@Component(modules = AppModule.class)
public interface AppComponent {
SharedPreferences sharedPreferences();
MyApplication myApplication();
}
ok掷倔,使用方式很簡單眉孩。
二.自定義@Scope 局部單例注解
事實上,除了全局單例今魔,我們可能在開發(fā)中更多的是需要局部單例,比如障贸,我在ActivityA中需要實例化兩個Student對象错森,在ActivityB中也需要一個Student對象,但我希望ActivityA中的兩個Student都叫小明篮洁,但ActivityB中的Student對象叫小剛涩维。
這樣的需求,全局單例注解@Singleton是不行的袁波,因為如果在AppModule中通過@Singleton注解提供Student瓦阐,兩個Activity中的Student都是小明(全局單例)了,而如果不用@Singleton注解篷牌,那么兩個Activity中的Student是三個不同的對象睡蟋,這樣又不滿足“ActivityA中的兩個Student都叫小明”的需求。
這時我們就需要自定義@Scope注解實現(xiàn)局部單例了枷颊。
1.自定義@Scope注解
實現(xiàn)方式很簡單戳杀,首先這樣自定義一個接口ActivityScope该面,聲明這個注解可以使對象在同一個Activity中實現(xiàn)單例:
@Scope //聲明這是一個自定義@Scope注解
@Retention(RUNTIME)
public @interface ActivityScope {
}
2.未使用@Scope注解效果展示:
我們先聲明2個Activity及相關(guān)代碼:
兩個小明的Activity:
public class A03Activity extends AppCompatActivity {
@BindView(R.id.btn_login)
Button btnLogin;
@BindView(R.id.tv_student1)
TextView tvStudent1;
@BindView(R.id.tv_student2)
TextView tvStudent2;
@Inject
Student student1;
@Inject
Student student2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a03);
ButterKnife.bind(this);
DaggerA03Component.builder()
.a03Module(new A03Module(this))
.build()
.inject(this);
//打印兩個Student類
tvStudent1.setText(student1.toString());
tvStudent2.setText(student2.toString());
}
@OnClick(R.id.btn_login)
public void onViewClicked() {
startActivity(new Intent(this, A04Activity.class));
}
}
小剛的Activity:
public class A04Activity extends AppCompatActivity {
@BindView(R.id.tv_student)
TextView tvStudent;
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a04);
ButterKnife.bind(this);
DaggerA04Component.builder()
.a04Module(new A04Module(this))
.build()
.inject(this);
//打印Student
tvStudent.setText(student.toString());
}
}
相關(guān)的兩個Module:
@Module
public class A03Module {
private A03Activity activity;
public A03Module(A03Activity activity) {
this.activity = activity;
}
@Provides
Student provideStudent() {
return new Student();
}
}
@Module
public class A04Module {
private A04Activity activity;
public A04Module(A04Activity activity) {
this.activity = activity;
}
@Provides
Student provideStudent() {
return new Student();
}
}
相關(guān)的Component:
@Component(modules = A03Module.class)
public interface A03Component {
void inject(A03Activity activity);
}
@Component(modules = A04Module.class)
public interface A04Component {
void inject(A04Activity activity);
}
編譯成功后運行,結(jié)果如下:
可以看到信卡,未使用自定義@Scope注解隔缀,每次@Inject注解獲取的Student對象都是一個新的對象。
這樣展示在界面的效果是3個不同的Student:
ActivityA中的兩個Student是小明和小紅(并未實現(xiàn)Activity局部單例)
ActivityB中的Student是小剛
3.使用自定義@Scope注解:
下面我們稍作幾行代碼的改變傍菇,實現(xiàn)Activity局部單例:
(1).在Module中添加@ActivityScope注解:
@Module
public class A03Module {
private A03Activity activity;
public A03Module(A03Activity activity) {
this.activity = activity;
}
@Provides
@ActivityScope//添加注解實現(xiàn)局部單例
Student provideStudent() {
return new Student();
}
}
(2).在Component中同樣添加@ActivityScope注解猾瘸,否則會編譯報錯:
@ActivityScope//添加注解實現(xiàn)局部單例
@Component(modules = A03Module.class, dependencies = AppComponent.class)
public interface A03Component {
void inject(A03Activity activity);
}
好的,僅僅添加兩行注解代碼丢习,我們接下來運行看結(jié)果:
成功牵触!我們可以看到我們成功實現(xiàn)了這樣的效果:
ActivityA中的兩個Student都是小明(Activity局部單例)
ActivityB中的Student是小剛(換了Activity,生成另外一個對象)
也就是說,在添加@ActivityScope后泛领,該Activity中通過@Inject依賴注入生成的Student對象全部唯一荒吏,但單例范圍僅僅是該Activity中,出了該Activity渊鞋,生成的Student仍然是非單例的绰更。
三.@Singleton@Scope注解原理分析
這樣實在太神奇了,我們不禁想到锡宋,僅僅通過這樣一個聲明儡湾,就能實現(xiàn)對象的生成和Activity的生命周期綁定嗎?只要該Activity存在执俩,里面的Student對象就是單例徐钠?
僅僅通過這樣的聲明,就能實現(xiàn)”同生共死“嗎:
@Scope
@Retention(RUNTIME) //Activity局部單例
public @interface ActivityScope {
}
@Scope
@Retention(RUNTIME) //Fragment局部單例
public @interface FragmentScope {
}
分析源碼前先公布答案:
自定義的@Singleton、@ActivityScope注解根本就沒有這些功能,它的作用僅僅是”標(biāo)記“役首。**
是不是難以置信尝丐,事實上的確如此,以小明的Activity為例衡奥,我們查看編譯器為我們生成的源碼目錄:
我們發(fā)現(xiàn)爹袁,無論是否添加@ActivityScope注解,自動生成的文件目錄中文件的數(shù)量都沒有改變矮固,如上圖失息,也就是說:
自定義@Scope注解并沒有生成任何文件。
那么自定義@Scope注解是如何實現(xiàn)單例的呢
(1)A03Activity_MembersInjector.java
我們輕車熟路(如果您仔細(xì)閱讀了前幾篇文章的話)打開這個文件档址,發(fā)現(xiàn)無論是否添加了@Scope注解盹兢,代碼皆如下:
public final class A03Activity_MembersInjector implements MembersInjector<A03Activity> {
private final Provider<Student> student1AndStudent2Provider;
public A03Activity_MembersInjector(Provider<Student> student1AndStudent2Provider) {
assert student1AndStudent2Provider != null;
this.student1AndStudent2Provider = student1AndStudent2Provider;
}
public static MembersInjector<A03Activity> create(Provider<Student> student1AndStudent2Provider) {
return new A03Activity_MembersInjector(student1AndStudent2Provider);
}
@Override
public void injectMembers(A03Activity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
//請注意該行
instance.student1 = student1AndStudent2Provider.get();
instance.student2 = student1AndStudent2Provider.get();
}
public static void injectStudent1(A03Activity instance, Provider<Student> student1Provider) {
instance.student1 = student1Provider.get();
}
public static void injectStudent2(A03Activity instance, Provider<Student> student2Provider) {
instance.student2 = student2Provider.get();
}
}
需要注意的是,當(dāng)我們依賴注入Activity時守伸,最重要的其實是這兩行代碼:
//請注意該行
instance.student1 = student1AndStudent2Provider.get();
instance.student2 = student1AndStudent2Provider.get();
無論是否添加了@ActivityScope注解绎秒,Activity中的倆個Student對象,都是通過student1AndStudent2Provider.get()進行實例化的尼摹,也就是說替裆,當(dāng)添加了@ActivityScope之后校辩,get()方法會生成單例,否則就沒有單例辆童,我們來看一下student1AndStudent2Provider對象是如何實例化的:
private final Provider<Student> student1AndStudent2Provider;
public A03Activity_MembersInjector(Provider<Student> student1AndStudent2Provider) {
//2.構(gòu)造方法中實例化student1AndStudent2Provider
assert student1AndStudent2Provider != null;
this.student1AndStudent2Provider = student1AndStudent2Provider;
}
public static MembersInjector<A03Activity> create(Provider<Student> student1AndStudent2Provider) {
//1.有對象執(zhí)行了該方法宜咒,然后執(zhí)行構(gòu)造方法
return new
A03Activity_MembersInjector(student1AndStudent2Provider);
}
可以看到student1AndStudent2Provider是通過有對象執(zhí)行create()方法,將student1AndStudent2Provider作為參數(shù)傳進來進行的初始化把鉴,我們找到執(zhí)行create()方法的地方:DaggerA03Component.java
(2)DaggerA03Component.java
通過對比DaggerA03Component.java故黑,在Provider初始化的地方,我們發(fā)現(xiàn)了端倪:
a.未添加@ActivityScope:
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
//注意這行代碼
this.provideStudentProvider = A03Module_ProvideStudentFactory.create(builder.a03Module);
this.a03ActivityMembersInjector = A03Activity_MembersInjector.create(provideStudentProvider);
}
b.添加了@ActivityScope:
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
//注意這行代碼庭砍,多了一個DoubleCheck.provider()方法
this.provideStudentProvider =
DoubleCheck.provider(A03Module_ProvideStudentFactory.create(builder.a03Module));
this.a03ActivityMembersInjector = A03Activity_MembersInjector.create(provideStudentProvider);
}
我們恍然大悟: @ActivityScope注解改變的只是做一個標(biāo)記场晶,然后Component將有標(biāo)記的對象工廠類(本文為Student工廠)進行了一次”DoubleCheck“單例加工!
我們有理由進行這樣的猜測:
1.未添加自定義@Scope:每次Activity初始化對象怠缸,直接讓工廠類初始化一個對象給Activty(activity.student = new Student());
2.添加了自定義@Scope:每次Activity初始化對象诗轻,直接讓工廠類將單例對象給Activty(
activity.student1 = factory.student(單例);
activity.student2 = factory.student(單例)揭北;);
是不是有一點恍然大悟的感覺扳炬?我們?yōu)榱蓑炞C我們猜測的正確性,我們打開DoubleCheck類進行分析:
(3*)DoubleCheck.java
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 = UNINITIALIZED;
private DoubleCheck(Provider<T> provider) {
assert provider != null;
this.provider = provider;
}
// 單例獲取對象
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result);
}
instance = result;
provider = null;
}
}
}
return (T) result;
}
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
return delegate;
}
return new DoubleCheck<T>(delegate);
}
public static <T> Lazy<T> lazy(Provider<T> provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
}
其中:
public final class DoubleCheck<T> implements Provider<T>, Lazy<T>
看到這行搔体,我們明白了恨樟,其實和普通的Provider工廠類一樣,DoubleCheck也是實現(xiàn)了Provider<T>接口疚俱,這樣通過調(diào)用get()劝术,就能獲得對象的實例了,只不過稍微有所區(qū)別的是:
通過DoubleCheck.get()獲得的對象呆奕,是單例的养晋。
至于DoubleCheck.get()是如何實現(xiàn)單例的(似乎和我們普通實現(xiàn)單例的方式所有不同),篇幅所限梁钾,就不過多介紹绳泉,有興趣的同學(xué)看看這篇博客 多線程問題與double-check小結(jié) @usher,相信會有所收獲陈轿。
總結(jié)
看到最后圈纺,相信大家已經(jīng)對于@Scope和@Singleton的原理了解的差不多了秦忿,其實并沒有什么魔力麦射,能夠真正通過一個注解,實現(xiàn)對Application生命周期或者Activity/Fragment生命周期進行綁定灯谣,無非做出一個標(biāo)記的作用潜秋,檢查到標(biāo)記后,Component將對應(yīng)的工廠實現(xiàn)單例模式胎许,這樣我們的容器去獲取對象時就能實現(xiàn)對應(yīng)的單例效果峻呛。
而對于其他的Activity罗售,因為ActivityA和ActivityB是同一層級互不干擾的,ActivityA的Component和ActivityB的Component互不聯(lián)系钩述,當(dāng)然不可能相互共享單例:
借用@MarkZhai 大神的圖和一句話:位于上層的component是看不到下層的寨躁,而下層則可以使用上層的,但不能引用同一層相鄰component內(nèi)的實例牙勘。
同時职恳,當(dāng)我們的容器(Application/Activity/Fragment等)銷毀時,對應(yīng)的Component當(dāng)然也不復(fù)存在方面,這就是為什么我們看起來@Scope能夠?qū)崿F(xiàn)”同生共死“效果的原因放钦。
至此,Dagger2相關(guān)的知識點我們已經(jīng)了解的差不多了恭金,我們需要的就是更多的去使用它操禀,通過它實現(xiàn)快速開發(fā)和代碼解耦。
2017/8/24日更新
事實上,Android開發(fā)中使用Dagger,開發(fā)人員仍然需要面對一些問題蔑水。
google工程師們嘗試彌補Dagger的問題邢锯,于是Dagger2-Android,基于Dagger2,應(yīng)用于Android開發(fā)搀别,由google開發(fā)的的拓展庫應(yīng)運而生: